Skip to content

Show a workload's creation failures next to its namespace's quotas and LimitRanges (#411 Q1, view) - #526

Merged
hellices merged 10 commits into
mainfrom
feat/525-creation-view
Oct 30, 2026
Merged

hellices merged 10 commits into
mainfrom
feat/525-creation-view

Conversation

@hellices

Copy link
Copy Markdown
Owner

Closes #525

Summary

The view half of slice Q1 of #411, built on the core run from #524. The plan is docs/superpowers/plans/2026-10-30-creation-constraints-view.md.

:blocked, or Explain workload creation failures in the Action Palette, opens a read-only view on the selected Deployment, ReplicaSet, StatefulSet, DaemonSet, Job or CronJob. No default key is bound.

  • Screen (ui/widgets/creation_constraints_screen.py):
    • The header shows the workload and its controller's counters, and each source gets one line from the shared renderer.
    • Recorded creation failures: how often and when each was last recorded, and what it says. A quota is attributed only by the name the message gives, and flagged "no longer present" or "not in the quotas read". A LimitRange message is attributed to "a LimitRange in this namespace". Anything else is "other rejection".
    • ResourceQuotas: grouped values shown verbatim (used X / hard Y), marked "at its limit" or "status not yet updated", with scopes.
    • LimitRanges: each constraint.
    • Notes: rejections are dated individually while policies are as read now; recreated and absent workloads, and earlier incarnations, are said. "No recorded failure" is never a conclusion, because Events expire. "No ResourceQuota" and "No LimitRange" appear only from a conclusive list, and the notes switch to "the list is stale" as the view ages.
    • Enter opens a failure's subject, a quota or a LimitRange in its table, in the run's epoch.
  • Controller (ui/creation_constraints_controller.py): follows the :consumers controller. One exclusive worker runs under a run token; a stale result is dropped; availability reasons are given; the context switch cancels the group; a failed worker is reported.
  • Wiring: the command and router, palette availability, the context-switch cancel, worker-failure reporting, and __main__.
  • Version matrix: a korvid-matrix-quota namespace with a pods: "0" quota, a LimitRange and a Deployment. Its scenario waits for the ReplicaSet's recorded rejection and checks it is read next to the policies, without the template's literal reaching the report. The creation_* modules and k8s/events.py join the workflow's path filters.
  • Docs: docs/tui.md ("Workload creation failures"), docs/keybindings.md, docs/kubernetes-support.md and the unreleased notes.

Verification

  • Tests were written first for the screen and for each wiring step:
    • tests/ui/test_creation_constraints_screen.py: 25 tests covering rows, attribution, markers, notes, keys, literal text and ageing.
    • tests/ui/test_creation_constraints_controller.py: availability, the read set, late results, navigation and cancellation.
    • tests/ui/test_creation_constraints_workflow.py: from :deployments, :blocked and Enter lead to the ReplicaSet; the wrong view says what to open; a failed run is reported.
    • Router, action-policy, coordinator and worker-failure tests.
  • The controller was written before its tests. To check its branches anyway, I mutation-checked it; the screen was mutation-checked too. The helper now runs the unmutated tests first and refuses to report on a failing baseline. Every mutation was caught, except one equivalent guard (child is not None: a target without a child kind never has a children observation).
  • Local gate, using the venv's tools behind the corporate mirror:
    • scripts/check_source_size.py, ruff check src tests, ruff format --check src tests, mypy (285 files) and tach check all passed.
    • pytest -n 8: 15218 passed, 41 skipped, 1 failed. The failure is the known local-only test_device_login_prefixes::test_the_measured_release_is_the_installed_one: the venv has litellm 1.100.3 and can't re-sync behind the mirror.
  • The version-matrix scenario skips locally because there is no kind cluster; the matrix workflow runs it on this PR.

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.
  • 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 7 commits October 30, 2026 21:12
ui/widgets/creation_constraints_screen.py renders a CreationReport: the
workload and its counters, one S6 line per source, recorded failures with
their classification, quotas with grouped values, limit and stale markers
and scopes, LimitRanges, and notes on what is not established. Enter
returns where a row leads. evidence_text gains elapsed_text for the
failures' ages.

Part of #525

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ui/creation_constraints_controller.py runs explain_creation in one
exclusive worker under a run token, opens the view only while the token,
the view, scope and selection are current and no dialog is open, and
opens a row's object in its table in the run's epoch. The shared fake UI
surface now records whether a worker is exclusive.

Part of #525

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The command and its palette entry Explain workload creation failures reach
the controller through the router; the palette greys it out with the
controller's own reason; a context switch cancels its worker group; and a
failed run is reported instead of crashing the app.

Part of #525

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

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A namespace whose quota admits no Pod, a LimitRange, and a Deployment: the
scenario waits for the ReplicaSet's recorded rejection and checks it is
read next to the quota and LimitRange, without the template's literal
environment value reaching the report.

Part of #525

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

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

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

The screen presents unreadable constraint and quota data as confirmed absences, misattributes child-workload events, and leaves time-dependent table cells stale during refresh.

4 open issues
What changed in this PR

Adds the read-only :blocked view for workload creation failures, building on the core diagnostics from #524. This delivers the view portion of #411 through #525, placing recorded rejections alongside namespace quotas and LimitRanges.

Changes:

  • Adds the diagnostic screen, controller, command and palette entry, with context-safe navigation and worker handling.
  • Adds UI coverage and a Kubernetes version-matrix quota-rejection scenario.
  • Documents the command, evidence boundaries and supported scenario.
File Description
tests/​version_matrix/​test_scenarios.py Checks recorded quota rejection alongside namespace policies.
tests/​version_matrix/​fixtures.yaml Adds a quota-blocked Deployment and LimitRange.
tests/​ui/​test_worker_failures.py Checks diagnostic worker failure reporting.
tests/​ui/​test_session_timeline_controller.py Records worker exclusivity in the shared fake.
tests/​ui/​test_evidence_text.py Tests elapsed-time formatting.
tests/​ui/​test_creation_constraints_workflow.py Covers command-to-screen navigation and failures.
tests/​ui/​test_creation_constraints_screen.py Covers rendering, attribution, navigation and ageing.
tests/​ui/​test_creation_constraints_controller.py Covers availability, reads and stale-result guards.
tests/​ui/​test_context_switch_coordinator.py Checks diagnostic worker cancellation order.
tests/​ui/​test_command_router.py Tests routing and unavailable-session warnings.
tests/​ui/​test_action_policy.py Tests palette availability delegation.
src/​korvid/​ui/​worker_failures.py Registers diagnostic failure notifications.
src/​korvid/​ui/​widgets/​creation_constraints_screen.py Displays failures, policies and evidence notes.
src/​korvid/​ui/​messages.py Adds the creation-failures operation.
src/​korvid/​ui/​evidence_text.py Extracts reusable elapsed-time formatting.
src/​korvid/​ui/​creation_constraints_controller.py Runs diagnostics and guards result presentation.
src/​korvid/​ui/​context_switch_coordinator.py Cancels diagnostics during context switches.
src/​korvid/​ui/​command.py Registers :blocked and its palette entry.
src/​korvid/​ui/​command_router.py Routes the command to its controller.
src/​korvid/​ui/​action_policy.py Connects command availability to the controller.
src/​korvid/​__main__.py Wires the controller and object navigation.
docs/​tui.md Explains the view and its evidence boundaries.
docs/​superpowers/​plans/​2026-10-30-creation-constraints-view.md Records the implementation plan.
docs/​release-notes/​unreleased.md Announces the new diagnostic view.
docs/​kubernetes-support.md Documents the version-matrix scenario.
docs/​keybindings.md Lists the :blocked command.
.github/​workflows/​k8s-version-matrix.yml Adds diagnostic modules to workflow path filters.

Review effort: Balanced

…rent

- A quota or LimitRange the projection could not read in full shows
  'constraints could not be read' instead of 'no hard limits' or 'no
  constraints'.
- The earlier-incarnations note covers the workload and its ReplicaSets or
  Jobs, whose own UIDs can change.
- The refresh timer re-renders the table's time-dependent cells too:
  failure ages, and whether a quota missing from the list is gone or just
  not read.

Part of #525

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

Navigation can open the wrong resource table when custom API aliases collide, and multiline admission-denial messages are truncated by one-line table rows.

No open issues 🎉

2 previously missed issues

In code that hasn't changed since last review

Medium severity Preserve API group when resolving failure destinations

src/​korvid/​ui/​widgets/​creation_constraints_screen.py:152

The destination drops the workload's API group and uses its plural as a view alias. If a custom API wins the replicasets or jobs alias, Enter opens that foreign table rather than the native failure subject; jump_to_object looks up the supplied alias directly, and the UID guard cannot correct the wrong resource type. Preserve the group/plural identity and resolve it through resolve_resource and canonical_resource_alias before navigating, as WorkspaceController.drill_into already does.

Medium severity Allow multiline rejection messages to display fully

src/​korvid/​ui/​widgets/​creation_constraints_screen.py:413

_failure_row preserves rejection messages verbatim, including newlines, but DataTable.add_row defaults to a one-line row. A multiline admission denial therefore displays only its first line; horizontal scrolling cannot reveal the remaining lines, and Enter opens the workload rather than the full message. Use automatic row height so the complete rejection remains readable, and cover a multiline message in the screen tests.

4 resolved since last review

Review effort: Balanced

- A Destination carries the API group; the controller resolves it with
  resolve_resource and canonical_resource_alias, so a custom API holding
  the replicasets or jobs alias cannot receive the navigation, and an
  undiscovered resource is reported instead of opened.
- Data rows grow to fit a multiline rejection message.

Part of #525

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

Copy link
Copy Markdown
Owner Author

Copilot's two "previously missed" issues from the 9a18c16 review are addressed in 1e43e43, each with a failing test first:

  • API group on navigation: Destination now carries group and resource instead of a view alias. The controller resolves it with resolve_resource and canonical_resource_alias, as WorkspaceController.drill_into does. A custom API that won the replicasets alias can no longer receive the navigation; it goes to replicasets.apps. A resource the session has not discovered is reported in a warning instead of opened. Tests: test_a_destination_is_opened_under_its_own_api_group_not_a_colliding_alias and test_a_destination_this_session_does_not_serve_is_said_not_opened.
  • Multiline messages: data rows are added with height=None, so a multiline rejection message is shown in full. Test: test_a_multiline_message_is_shown_in_full (a three-line message gives a three-line row).

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

An unparsable status.used value renders the same blank note as below-limit usage, hiding that the quota comparison could not be established.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Distinguish unparsable quota usage from usage below limit

src/​korvid/​ui/​widgets/​creation_constraints_screen.py:176

A quota with matching spec and status hard limits but an unparsable status.used value has stale=False and Usage.NOT_ESTABLISHED. This branch renders the same blank note as BELOW, hiding that the comparison could not be made. Add an explicit comparison-not-established marker after the stale-status check so unknown usage stays distinct from usage below the limit.

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건을 인라인 코멘트로 남겼습니다.

- No recorded failure names Events and conditions only when the workload
  and its children were read, and conditions only for Deployments and
  ReplicaSets; otherwise it names Events and says the rest is not
  established.
- A quota resource whose comparison is not established is marked so,
  never left blank like usage below the limit.
- The multiline test checks the message text, the workflow fake checks the
  name and namespace read, and a test type error is fixed.

Part of #525

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

Copy link
Copy Markdown
Owner Author

Copilot's "previously missed" issue from the 1e43e43 review is addressed in 60c34bb. A quota resource whose comparison is Usage.NOT_ESTABLISHED but whose status is current, such as an unparsable used value, is now marked "not established" instead of being left blank like usage below the limit. Test: test_an_unestablished_comparison_is_marked_not_left_blank.

The same commit fixes the pre-commit and test (3.11) failures on 1e43e43: a mypy error in my new test, which passed a tuple where Coordinate is required.

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 assessment is needed because a workload-replacement race can display stale counters and failures for a replacement workload without warning.

No open issues 🎉

Review effort: Balanced

@hellices
hellices merged commit ad91698 into main Oct 30, 2026
19 checks passed
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 a workload's creation failures next to its namespace's quotas and LimitRanges (#411 Q1, view)

2 participants