Repository navigation
Show ConfigMap and Secret consumers with the shared evidence renderer (#410 P1, view) - #509
Merged
Merged
Conversation
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>
There was a problem hiding this comment.
🟡 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-consumersand 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
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>
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>
There was a problem hiding this comment.
🟡 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
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>
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>
Part of #502. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
Part of #502. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
5 tasks done
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>
15 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



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:subPath/subPathExprEnter on a row opens that Pod.
ui/evidence_text.py(S6): one line per source:<resource> <group/version> <scope>: <state> — <basis sentence> · observed <age>.Basishas its own sentence, so widgets never interpret bases.FreshnessPolicy, never when nothing was observed.Text, so markup in a cluster name is shown literally.ui/widgets/config_propagation_screen.py: the evidence lines, rows by mode, and notes.ui/config_propagation_controller.py: availability and the run.k8s.evidence_requests.EvidenceClient: evidence reads, discovery and the session generation as one boundary.KubeClientimplements it.Departures from the design
Recorded in the plan:
:consumers.:propagationwould makepropambiguous with:proposals, which a completion test pins. The palette title and search terms still say "propagation".hint-events, so no new constructor argument.EvidenceClientlives ink8sso the UI depends on the boundary, notKubeClient.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.pyis 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.Verification
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.EvidenceClientand wiring tests.uv run pytest -p no:tach -n 8 tests/ui/ -q: 3659 passed.mkdocs build --strictandcheck_docs_site.py: passed. Docs tests: passed.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.Checklist
make checkpasses locally. (Except the litellm version test above, which depends on this machine's venv.)and a fail-closed audit entry, and sensitive reads are still masked.