feat(workbench): CHAOS-5109 render completeness state and requirement outcomes - #74
Merged
Merged
Conversation
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.
Ticket: CHAOS-5109
TEST-EVIDENCE
What this change accomplishes, and for whom:
CompletenessPanelrenders 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) andcompleteness.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 onlyterminal_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):
pnpm install --frozen-lockfilein the lane worktree (node_modulesa 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 fullCompletenessPanel.test.tsxandDeterministicAnswerView.test.tsxsuites (the pre-existing tests unchanged and green).npx next build(Turbopack) → compiled, typechecked, and statically generated all 4 routes with 0 errors.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.refs/pull/74/headat the same tip:javascript-typescript=0,python=0,actions=0results_countviacode-scanning/analyses.Enumerated surface touched:
src/components/CompletenessPanel.tsx— the panel itself:statebadge, requirement-outcomes table, per-row refinement disclosure, empty state.src/lib/presentation.ts— two new exhaustive tone switches:completenessStateTone(overnot_derived|complete|partial|degraded) andplanRequirementOutcomeTone(oversatisfied|narrowed|unavailable|not_applicable|not_attempted).src/lib/contracts.ts— re-exportsPlanRequirementOutcomeRow/RequirementRefinementfrom the generated contract module (already on the wire at the pinned mirror; not previously named by this repo's own stable-name file).state/outcomes/refusal_basisat the pinned SHA; none of the 4 example fixtures undersrc/contracts/examples/needed editing.statedomain (all 4 members, executed): every member ofnot_derived|complete|partial|degradedis rendered in its own test with its own title (state: <value>) and its own tone class.outcomes[].outcomedomain (all 5 members, executed): every member ofsatisfied|narrowed|unavailable|not_applicable|not_attemptedis rendered in its own test with its own title (outcome: <value>) and its own tone class.outcomesrow-shape domain cells (executed):outcomesabsent → empty-state line, agrees withstate: not_derived.outcomes: []→ same empty-state line.requirementpresent → identity renders;obligationpresent → renders on its own line beside the identity (present exactly whenrequirementis, per the schema);obligationabsent → no such line.impact→ its own column, distinct text fromoutcome.served/declared→ their own column, formattedserved / declared.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.refinementspresent (2 steps, mixedbasis/overrun) → a per-row disclosure names the step count and, once opened, every step's own stage/before/after/basis/overrun/coverage;refinementsabsent → no disclosure rendered.requirementabsent → row renders as an honest "—", not dropped from the row count.@maxItems 200bound) → all 200 render, 0 truncated.terminal_status=complete+state=partial(disagreement fixture) → both top-level badges present and unedited.src/contracts/examples/carrying acompletenessblock (enumerated viareaddirSync, not hand-listed) renders through the panel with no thrown error.Class sweep: every field
PlanRequirementOutcomeRow/RequirementRefinementcarries is either a visible column, a disclosed field, or (forrefusal_basis) a deliberately unrendered machine field with a named boundary — none is silently absent from the row.refusal_basis(both the top-levelInvestigationResultfield and its mirroredcompleteness.refusal_basis) stays a machine fieldDeterministicAnswerViewdeliberately 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)
statebadge besideterminal_status, own title, exhaustive tone including a distinctnot_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 withnot_derived, nothing on the row silently dropped: confirmed —outcomes.mapwith no sort/filter, index-keyed, every row field either a column or a disclosed field, empty branch keyed offoutcomes.length === 0alone. (c) disagreement betweenstateandterminal_statusnever reconciled: confirmed — the two badges render from independent switches with no cross-reference between them. (d)refusal_basisuntouched: confirmed by grep — zero new references torefusal_basisin this diff. Both new tone switches were re-checked for exhaustiveness by removing a case in a scratch copy and confirmingtscreports a compile error on the resulting non-exhaustive switch, then restored.RISK-NOTES
CompletenessPanel; no existing DOM elements were removed, renamed, or restyled —completeness-chip-row,completeness-terminal-reasonand all prior test ids are unchanged, and the full pre-existingCompletenessPanel.test.tsx/DeterministicAnswerView.test.tsxsuites stay green with zero edits to their existing cases.state/outcomeson every one of today's 4 example fixtures happen to benot_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.<table>/<thead>/<tbody>,scope="col"headers, per-row<details>) follows the same accessible pattern already used byCohortRankingPanel/CoveragePanelelsewhere in this codebase.