Skip to content

feat(workbench): CHAOS-5109 render completeness state and requirement outcomes - #74

Merged
chrisgeo merged 2 commits into
mainfrom
feat/5109-completeness-panel-state
Sep 13, 2026
Merged

chrisgeo merged 2 commits into
mainfrom
feat/5109-completeness-panel-state

Conversation

@chrisgeo

@chrisgeo chrisgeo commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Ticket: CHAOS-5109

TEST-EVIDENCE

What this change accomplishes, and for whom: CompletenessPanel renders the two answer-completeness signals the contract already carries but the panel did not show: completeness.state (the server-derived not_derived/complete/partial/degraded verdict) and completeness.outcomes[] (one row per requirement, what it was supposed to contain and what became of it — identity, obligation, outcome, impact, served/declared counts, declared cause, and any refinement history). A reader auditing an answer's trustworthiness can currently see only terminal_status (why the turn stopped); they could not see whether the underlying requirements were actually satisfied, which one was not, or what dimension was affected. The two top-level signals can legitimately disagree on the same result, so the panel shows both, unreconciled; every field on a row is either a visible column or a disclosed one, nothing silently absent.

Executed claim (end to end, from scratch):

  • Real install: pnpm install --frozen-lockfile in the lane worktree (node_modules a real directory, not symlinked).
  • npx prettier --check . → 0 issues.
  • npx eslint . → 0 issues.
  • npx tsc --noEmit → 0 errors.
  • npx vitest run → 60 files / 977 tests passed, including the full CompletenessPanel.test.tsx and DeterministicAnswerView.test.tsx suites (the pre-existing tests unchanged and green).
  • npx next build (Turbopack) → compiled, typechecked, and statically generated all 4 routes with 0 errors.
  • CI at tip 061e99784237352a8a997e73a4a1ab0286d28418: every workflow run completed, every check-run success (test, Quality, Build, Unit tests, E2E smoke, Dependency Audit, Gitleaks Secret Scan, run_checks.sh self-test, CodeQL) via paginated API conclusions.
  • CodeQL on refs/pull/74/head at the same tip: javascript-typescript=0, python=0, actions=0 results_count via code-scanning/analyses.

Enumerated surface touched:

  • src/components/CompletenessPanel.tsx — the panel itself: state badge, requirement-outcomes table, per-row refinement disclosure, empty state.
  • src/lib/presentation.ts — two new exhaustive tone switches: completenessStateTone (over not_derived|complete|partial|degraded) and planRequirementOutcomeTone (over satisfied|narrowed|unavailable|not_applicable|not_attempted).
  • src/lib/contracts.ts — re-exports PlanRequirementOutcomeRow/RequirementRefinement from the generated contract module (already on the wire at the pinned mirror; not previously named by this repo's own stable-name file).
  • No contract/schema/example-fixture change: the mirror already carries state/outcomes/refusal_basis at the pinned SHA; none of the 4 example fixtures under src/contracts/examples/ needed editing.

state domain (all 4 members, executed): every member of not_derived|complete|partial|degraded is rendered in its own test with its own title (state: <value>) and its own tone class.

state tone class rendered text
not_derived badge--neutral "not derived"
complete badge--ok "complete"
partial badge--warn "partial"
degraded badge--bad "degraded"

outcomes[].outcome domain (all 5 members, executed): every member of satisfied|narrowed|unavailable|not_applicable|not_attempted is rendered in its own test with its own title (outcome: <value>) and its own tone class.

outcome tone class
satisfied badge--ok
not_applicable badge--neutral
narrowed badge--warn
not_attempted badge--warn
unavailable badge--bad

outcomes row-shape domain cells (executed):

  • outcomes absent → empty-state line, agrees with state: not_derived.
  • outcomes: [] → same empty-state line.
  • 3 rows, distinct outcomes → 3 table rows, order-checked against the input array index by index (never re-sorted).
  • requirement present → identity renders; obligation present → renders on its own line beside the identity (present exactly when requirement is, per the schema); obligation absent → no such line.
  • impact → its own column, distinct text from outcome.
  • served/declared → their own column, formatted served / declared.
  • a row carrying a declared cause (cause_narrowing/cause_coverage/cause_overrun) → the cause renders in the Reason column; cause_observed: false → renders with a "(defaulted)" suffix, distinct from a reported cause.
  • refinements present (2 steps, mixed basis/overrun) → a per-row disclosure names the step count and, once opened, every step's own stage/before/after/basis/overrun/coverage; refinements absent → no disclosure rendered.
  • requirement absent → row renders as an honest "—", not dropped from the row count.
  • 200 rows (the contract's own @maxItems 200 bound) → all 200 render, 0 truncated.
  • terminal_status=complete + state=partial (disagreement fixture) → both top-level badges present and unedited.
  • every one of the 4 example fixtures under src/contracts/examples/ carrying a completeness block (enumerated via readdirSync, not hand-listed) renders through the panel with no thrown error.

Class sweep: every field PlanRequirementOutcomeRow/RequirementRefinement carries is either a visible column, a disclosed field, or (for refusal_basis) a deliberately unrendered machine field with a named boundary — none is silently absent from the row. refusal_basis (both the top-level InvestigationResult field and its mirrored completeness.refusal_basis) stays a machine field DeterministicAnswerView deliberately never renders (only the accompanying limitation sentence reaches the reader); this PR does not change that boundary, per the ticket's own instruction to leave it exactly where it is rendered today.

SELF-REVIEW (rule 26, executed against this PR's own tip before TELL): re-read the diff against the four boundary requirements — (a) state badge beside terminal_status, own title, exhaustive tone including a distinct not_derived: present, confirmed via rendered title/class assertions covering all 4 members. (b) outcome rows: identity + obligation + outcome + impact + served/declared + reason, wire order, no dedup, explicit empty state agreeing with not_derived, nothing on the row silently dropped: confirmed — outcomes.map with no sort/filter, index-keyed, every row field either a column or a disclosed field, empty branch keyed off outcomes.length === 0 alone. (c) disagreement between state and terminal_status never reconciled: confirmed — the two badges render from independent switches with no cross-reference between them. (d) refusal_basis untouched: confirmed by grep — zero new references to refusal_basis in this diff. Both new tone switches were re-checked for exhaustiveness by removing a case in a scratch copy and confirming tsc reports a compile error on the resulting non-exhaustive switch, then restored.

RISK-NOTES

  • Purely additive to CompletenessPanel; no existing DOM elements were removed, renamed, or restyled — completeness-chip-row, completeness-terminal-reason and all prior test ids are unchanged, and the full pre-existing CompletenessPanel.test.tsx/DeterministicAnswerView.test.tsx suites stay green with zero edits to their existing cases.
  • No contract, schema, or example-fixture change — this is a pure consumer/rendering change against fields already on the pinned mirror.
  • state/outcomes on every one of today's 4 example fixtures happen to be not_derived/absent (the acr example generator has not yet produced a populated-outcomes example), so the populated-table and refinement-disclosure paths are covered by this PR's own synthetic fixtures rather than a pinned example; the empty-state path is covered by real examples.
  • New table markup (<table>/<thead>/<tbody>, scope="col" headers, per-row <details>) follows the same accessible pattern already used by CohortRankingPanel/CoveragePanel elsewhere in this codebase.

@linear-code

linear-code Bot commented Sep 13, 2026

Copy link
Copy Markdown

CHAOS-5109

@chrisgeo
chrisgeo merged commit a9a72b1 into main Sep 13, 2026
13 checks passed
@chrisgeo
chrisgeo deleted the feat/5109-completeness-panel-state branch September 13, 2026 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant