Skip to content

Show ConfigMap and Secret consumers with the shared evidence renderer (#410 P1, view) - #509

Merged
hellices merged 21 commits into
mainfrom
feat/502-config-propagation-view
Oct 29, 2026
Merged

hellices merged 21 commits into
mainfrom
feat/502-config-propagation-view

Conversation

@hellices

Copy link
Copy Markdown
Owner

Closes #502
Closes #486

Summary

The view half of slice P1 of #410, on top of the core in #503. It ships the shared evidence renderer (#486, S6 of #409) with its first consumer.

Select a ConfigMap or Secret and run :consumers (also :config-consumers), or Explain configuration propagation in the Action Palette. The view shows each Pod container in the namespace that uses the object, grouped by mode, with what a change means for each:

Mode What a change means
Environment variables Read at start; needs a rollout restart, which asks for approval
Volume, plain or projected Updated eventually unless the object is immutable; whether the app rereads it is not verified
subPath / subPathExpr Never updated; needs a restart
Unmounted volume Nothing reads it
Image pull secret Used only when an image is pulled

Enter on a row opens that Pod.

  • ui/evidence_text.py (S6): one line per source: <resource> <group/version> <scope>: <state> — <basis sentence> · observed <age>.
    • Every Basis has its own sentence, so widgets never interpret bases.
    • A line is marked stale by the source's own FreshnessPolicy, never when nothing was observed.
    • Names are plain Text, so markup in a cluster name is shown literally.
  • ui/widgets/config_propagation_screen.py: the evidence lines, rows by mode, and notes.
    • "No Pod references it" appears only when the Pod list is conclusive; otherwise it says "Not established" and why.
    • A gone or recreated object is said; immutability is never claimed.
  • ui/config_propagation_controller.py: availability and the run.
    • It is unavailable only without a connection, on the wrong view, or with no selection.
    • One exclusive worker runs under a run token: a context switch, new credentials, a newer run or a dialog opened meanwhile drops the result.
    • Choosing a consumer navigates in the run's epoch with the Pod's UID.
    • The context switch cancels the worker group before the client swaps.
  • k8s.evidence_requests.EvidenceClient: evidence reads, discovery and the session generation as one boundary. KubeClient implements it.
  • Version matrix: a consumer Pod in the fixtures, and a scenario that finds env, volume and subPath on every supported version without the ConfigMap's data or the Pod's literal env value.
  • Docs: a Configuration propagation section in the TUI guide, the keybindings table, the support page's scenario list, and the unreleased notes.

Departures from the design

Recorded in the plan:

  • The command is :consumers. :propagation would make prop ambiguous with :proposals, which a completion test pins. The palette title and search terms still say "propagation".
  • The context switch cancels the group by name, as it already does for hint-events, so no new constructor argument.
  • EvidenceClient lives in k8s so the UI depends on the boundary, not KubeClient.
  • The observability builders move to composition_support.py. This frees about 130 lines in __main__.py, which was 3 under its limit, without editing the size gate.

Worth knowing

  • ui/app.py is now exactly at its 1,500-line limit. The next feature that adds an app input will have to move code out of it first.
  • No default key is bound, per design decision 5. You can assign one at review.

Verification

  • New tests, each written first and failing:
    • tests/ui/test_evidence_text.py: 22.
    • tests/ui/test_config_propagation_screen.py: 10.
    • tests/ui/test_config_propagation_controller.py: 11.
    • tests/ui/test_config_propagation_workflow.py: 2, end to end in the real app: :configmaps, :consumers, Enter, and the Pod is selected.
    • Router, policy, coordinator-order, EvidenceClient and wiring tests.
  • Mutation checks: each of 18 changes across the renderer, screen and controller was caught by a test.
  • uv run pytest -p no:tach -n 8 tests/ui/ -q: 3659 passed.
  • mkdocs build --strict and check_docs_site.py: passed. Docs tests: passed.
  • Full gate (check_source_size, ruff check, ruff format --check, mypy, tach check, pytest -n 8): 14882 passed, 40 skipped. One local failure, test_the_measured_release_is_the_installed_one: this machine's venv still has litellm 1.100.3 and cannot install the lock from behind the mirror. CI installs from the lock.
  • The version-matrix scenario runs in this PR's Kubernetes version matrix checks.

Checklist

  • This closes an accepted issue in this repository that is assigned to me.
  • Tests cover the change, written to fail first where behavior changed.
  • make check passes locally. (Except the litellm version test above, which depends on this machine's venv.)
  • User-facing docs and the README match the change, or nothing user-facing changed.
  • No security invariant is weakened: writes still need a keystroke approval
    and a fail-closed audit entry, and sensitive reads are still masked.

hellices and others added 9 commits October 30, 2026 02:08
The observability builders turn config into connectors and hold no
wiring of their own, so they move next to ObservabilityWiring in
composition_support, as earlier helpers did. __main__ keeps the
_build_observability name it imports back, and the two tests that
patched _missing_extra_packages on __main__ patch it where the builder
now looks it up. No behaviour changes.

This frees the composition root for the configuration propagation
view's wiring.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
evidence_text turns an observation into one line - what the source
reads and where, its state, a sentence for its basis, and its age - so
widgets never interpret a basis themselves. Every Basis has its own
sentence, and denied, unsupported, unavailable and partial reads say
different things. The line is marked stale by the source's own
freshness policy, the one its declaration and conclusive() use, and
never when nothing was observed. Cluster-provided names go into a plain
Text, so a name containing markup is shown literally.

Part of #502 (#486, S6 of #409).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ConfigPropagationScreen shows one PropagationReport: the object's and
the Pod list's lines from the shared renderer, then the consumers under
a heading per mode saying what a change means - environment variables
and subPath mounts need a rollout restart, volumes are updated
eventually unless the object is immutable, unmounted volumes and image
pull secrets read nothing at run time - then what the evidence does not
establish. "No Pod references it" appears only when the Pod list is
conclusive; otherwise the view says why the consumer set is not
established. A gone or recreated object is said, and immutability is
never claimed either way.

Enter on a consumer row returns it so the caller can open its Pod; a
heading does nothing. Every cell is plain Text.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
EvidenceClient extends EvidenceReader with discovery_snapshot() and the
session generation, the three things a diagnostic run needs: it
resolves its sources from discovery, reads them, and records the
session in its run token. KubeClient implements it, so the UI can take
a diagnostic client without depending on KubeClient itself.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ConfigPropagationController is available on a selected ConfigMap or
Secret in the core group, and says why it is not: no connection, the
wrong view, or no selection. A refused source never disables it; the
view says so instead.

Opening it runs explain_propagation in one exclusive worker under a run
token, and shows the view only while that token is current, so a
context switch, new credentials or a newer run drops the result, as
does a dialog that opened meanwhile. Choosing a consumer opens its Pod
in the epoch the run started in, with the Pod's UID. cancel_workers()
lets the context switch stop a run that holds the old client.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The :consumers command (also :config-consumers), and its palette entry
Explain configuration propagation, open the view on the selected
ConfigMap or Secret. It has no default key; the palette greys it out
with the controller's own reason. :propagation would have made 'prop'
ambiguous with :proposals in the command bar, so it is not an alias.

The composition root hands the session's KubeClient to the controller
as its EvidenceClient, and the context switch cancels the controller's
worker group right after the workspace quiesce, before the client
swaps. An unavailable reason is shown without markup, like every other
owner's.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The version-matrix fixtures gain a Pod that consumes settings through
envFrom, a mounted volume and a subPath mount, on the pause image every
kind node already has. The scenario runs explain_propagation as the
admin principal and finds the three modes, with a conclusive Pod list,
and neither the ConfigMap's data nor the Pod's literal env value in the
report. The workflow's path filters and the support page cover the new
modules and scenario.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The TUI guide gains a Configuration propagation section: what each
consumption mode means for a change, what the view reads and never
reads, when it says no Pod references the object, and that immutability
is never claimed. The keybindings table lists :consumers, and the
unreleased notes announce it.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The plan for #502 records four departures from the design: the command
is :consumers, so prop stays unambiguous with :proposals; the context
switch cancels the worker group by name; EvidenceClient lives in k8s;
and the observability builders move out of __main__ to make room.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 29, 2026 17:42
@hellices hellices added this to the v0.7.0 milestone Oct 29, 2026
@hellices hellices added enhancement New feature or request area: ui Textual application, screens, widgets, and interaction design labels Oct 29, 2026
@hellices hellices self-assigned this Oct 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Incomplete reference coverage can produce false “no volume references” claims, and pending reads can open an outdated report over a changed workspace without revalidating the originating pane, view, and selection.

4 open issues
What changed in this PR

Adds the ConfigMap and Secret consumer view for #410, building on the core projection from #503 and introducing the shared evidence renderer for #486.

Changes:

  • Adds :consumers / :config-consumers and an Action Palette entry, with consumers grouped by usage mode and navigation to their Pods.
  • Wires evidence reads through EvidenceClient, with run-token checks and context-switch cancellation.
  • Adds UI and Kubernetes version-matrix coverage, updates documentation, and relocates observability builders to make room in the composition root.
File Description
tests/​version_matrix/​test_scenarios.py Tests consumer modes and report payload exclusion.
tests/​version_matrix/​fixtures.yaml Adds a Pod consuming configuration three ways.
tests/​ui/​test_evidence_text.py Tests evidence wording, freshness and literal names.
tests/​ui/​test_context_switch_coordinator.py Checks diagnostic cancellation order.
tests/​ui/​test_config_propagation_workflow.py Exercises command-to-consumer-to-Pod navigation.
tests/​ui/​test_config_propagation_screen.py Tests grouping, notes and screen interaction.
tests/​ui/​test_config_propagation_controller.py Tests availability, run guards and navigation.
tests/​ui/​test_command_router.py Tests propagation command dispatch.
tests/​ui/​test_action_policy.py Tests command availability delegation.
tests/​test_observability_wiring.py Updates patches for relocated builders.
tests/​test_main_wiring.py Checks diagnostic client injection.
tests/​k8s/​test_evidence_requests.py Checks the EvidenceClient contract.
src/​korvid/​ui/​widgets/​config_propagation_screen.py Displays grouped consumers and evidence notes.
src/​korvid/​ui/​messages.py Adds the propagation command operation.
src/​korvid/​ui/​evidence_text.py Centralizes evidence text rendering.
src/​korvid/​ui/​context_switch_coordinator.py Cancels propagation workers during switching.
src/​korvid/​ui/​config_propagation_controller.py Coordinates diagnostic reads and Pod navigation.
src/​korvid/​ui/​command.py Registers command aliases and palette metadata.
src/​korvid/​ui/​command_router.py Routes commands to the propagation controller.
src/​korvid/​ui/​app.py Accepts the diagnostic client dependency.
src/​korvid/​ui/​app_runtime.py Carries the diagnostic client through runtime inputs.
src/​korvid/​ui/​action_policy.py Connects propagation availability checks.
src/​korvid/​k8s/​evidence_requests.py Defines the combined evidence-client boundary.
src/​korvid/​k8s/​client.py Implements the EvidenceClient boundary.
src/​korvid/​composition_support.py Houses relocated observability builders.
src/​korvid/​__main__.py Wires the controller, client and command.
docs/​tui.md Explains consumer modes and view behavior.
docs/​superpowers/​plans/​2026-10-30-config-propagation-view.md Records implementation steps and design departures.
docs/​release-notes/​unreleased.md Announces the consumer view.
docs/​kubernetes-support.md Documents the new matrix scenario.
docs/​keybindings.md Lists consumer command aliases.
.github/​workflows/​k8s-version-matrix.yml Extends triggers to consumer-related modules.

Review effort: Balanced

Comment thread src/korvid/ui/config_propagation_controller.py
Comment thread src/korvid/ui/widgets/config_propagation_screen.py Outdated
Comment thread src/korvid/ui/config_propagation_controller.py
Comment thread src/korvid/ui/widgets/config_propagation_screen.py Outdated

@my-reviewer-agent my-reviewer-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이슈 5건을 인라인 코멘트로 남겼습니다.

Comment thread docs/superpowers/plans/2026-10-30-config-propagation-view.md
Comment thread src/korvid/composition_support.py
Comment thread tests/ui/test_config_propagation_controller.py
Comment thread tests/ui/test_config_propagation_workflow.py Outdated
Comment thread docs/superpowers/plans/2026-10-30-config-propagation-view.md
hellices and others added 4 commits October 30, 2026 03:17
The view took a fixed now, so left open past its freshness limit it
kept saying observed 2s ago and that no Pod references the object. It
now takes a clock and re-renders its evidence lines and notes every few
seconds without reading the cluster again: aged evidence is marked
stale, and a stale Pod list no longer proves absence.

"No Pod references it" now names the forms checked - envFrom, env
valueFrom, configMap, secret and projected volumes, imagePullSecrets -
and says CSI and other volume plugins are not, so a Secret used only
through a CSI driver is not called unused.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A run's token stays current when the user changes view, scope or
selection, so a slow run could open its report over a different
workspace. The controller now records the view, scope and selected row
a run starts from and opens the view only while they are unchanged, as
the relationship view guards its pane and view.

The test clients record every request, and the tests assert the exact
targets: the object's metadata GET and the Pod LIST in the selected
namespace, with the first continuation and the run's budgets.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The propagation worker runs with exit_on_error=False, but the app
reported failures only for the relationship and timeline groups, so a
failed run showed nothing. worker_failures.py now names every reported
group with its label, config-propagation included, and
on_worker_state_changed looks the label up there - which also frees ten
lines in ui/app.py.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The view can retain a stale ConfigMap-deletion message and lacks the required unbound, configurable action in the keybinding editor.

2 open issues
4 resolved since last review

Review effort: Balanced

Comment thread src/korvid/ui/command.py
Comment thread src/korvid/ui/widgets/config_propagation_screen.py Outdated

@my-reviewer-agent my-reviewer-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이슈 2건을 인라인 코멘트로 남겼습니다.

Comment thread docs/superpowers/plans/2026-10-30-config-propagation-view.md Outdated
Comment thread tests/ui/test_config_propagation_controller.py Outdated
hellices and others added 2 commits October 30, 2026 03:50
A 404 that named the object proves it was gone when it was read, not
that it still is. Once the metadata read ages past its policy, the open
view says the object did not exist when it was read and may have been
created since, instead of that it no longer exists.

The controller test now records the metadata GET's byte budget and
asserts the default, as it already did for the Pod list's maximum.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Human review is needed to assess whether the privacy claims accurately distinguish sensitive data fetched from data retained or displayed.

No open issues 🎉

2 resolved since last review

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The privacy documentation overstates the guarantee by claiming environment values are never read, while Pod LIST retrieves literal values and the new scenario only verifies their exclusion from the report.

1 open issue

Review effort: Balanced

Comment thread docs/kubernetes-support.md Outdated

@my-reviewer-agent my-reviewer-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이슈 2건을 인라인 코멘트로 남겼습니다.

Comment thread src/korvid/ui/widgets/config_propagation_screen.py
Comment thread docs/superpowers/plans/2026-10-30-config-propagation-view.md Outdated
The environment and subPath headings suggest a rollout restart, but an
ephemeral container cannot be restarted, and a rollout creates Pods
without it. When a consumer is an ephemeral container, the view now
says so and that a new ephemeral container is the way to read a change,
as it already explains init containers. The TUI guide says so too.

The support page no longer says the version-matrix scenario never reads
the Pod's literal values: the Pod list reads them, and the scenario
checks they do not reach the report. The plan's Task 5 paths are full.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI previously approved these changes Oct 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approved

No active review issues are reported for the read-only ConfigMap and Secret consumer view.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@my-reviewer-agent my-reviewer-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이슈 2건을 인라인 코멘트로 남겼습니다.

Comment thread src/korvid/ui/widgets/config_propagation_screen.py
Comment thread tests/ui/test_config_propagation_controller.py
A running ephemeral container that mounts the object as a plain volume
does see the kubelet's update, so the note now appears only for an
ephemeral consumer through environment variables or a subPath mount.
The controller test asserts the consumer's whole Pod identity and
navigates with the consumer the report produced.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI previously approved these changes Oct 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approved

No active review issues are reported for the ConfigMap and Secret consumer view and shared evidence renderer.

No open issues 🎉

Review effort: Balanced

@my-reviewer-agent my-reviewer-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이슈 3건을 인라인 코멘트로 남겼습니다.

Comment thread docs/superpowers/plans/2026-10-30-config-propagation-view.md
Comment thread src/korvid/ui/config_propagation_controller.py
Comment thread docs/superpowers/plans/2026-10-30-config-propagation-view.md Outdated
Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI previously approved these changes Oct 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approved

No active review issues block the read-only ConfigMap and Secret consumer view.

No open issues 🎉

Review effort: Balanced

@my-reviewer-agent my-reviewer-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이슈 3건을 인라인 코멘트로 남겼습니다.

Comment thread src/korvid/composition_support.py
Comment thread src/korvid/ui/config_propagation_controller.py
Comment thread docs/superpowers/plans/2026-10-30-config-propagation-view.md Outdated
The step listed the workflow test inside a bullet, which the plan sync
does not match, so its code block stayed empty. It now holds the test
file, including the failed-run case Step 6 refers to.

Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@my-reviewer-agent my-reviewer-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이슈 2건을 인라인 코멘트로 남겼습니다.

Comment thread docs/superpowers/plans/2026-10-30-config-propagation-view.md
Comment thread tests/k8s/test_evidence_requests.py
Part of #502.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@my-reviewer-agent my-reviewer-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이슈 3건을 인라인 코멘트로 남겼습니다.

Comment thread src/korvid/ui/config_propagation_controller.py
Comment thread tests/ui/test_config_propagation_controller.py
Comment thread docs/superpowers/plans/2026-10-30-config-propagation-view.md
@hellices
hellices merged commit 61f4697 into main Oct 29, 2026
18 checks passed
hellices added a commit that referenced this pull request Oct 29, 2026
test_absolute_deadline_reaps_reader_that_suppresses_cancellation built
the controller with a 10 ms deadline and the real monotonic clock.
_observe subtracts the time since registration from the deadline before
its first read, so a runner that started the worker more than 10 ms
late marked the deadline elapsed without ever calling the reader, and
the test waited 30 s for a start that never came. It failed twice in a
row on Windows in #509's CI.

The test now freezes the controller's clock, so the run gets its whole
deadline however late it starts, and uses a 0.5 s deadline, which the
run's real asyncio timeout still enforces with room for the read task to
start on a coarse Windows timer. It still proves the absolute deadline
reaps a reader that suppresses cancellation.

Closes #511.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ui Textual application, screens, widgets, and interaction design enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show ConfigMap and Secret consumers with the shared evidence renderer (#410 P1, view) Render evidence observations with one shared renderer (#409 S6)

2 participants