Skip to content

feat(reporting): per-paragraph hierarchy-scoring report — endpoint + MCP tool + demo Scoring tab (WS2) - #425

Merged
thewrz merged 11 commits into
mainfrom
feat/hierarchy-scoring-report
Jul 9, 2026
Merged

feat(reporting): per-paragraph hierarchy-scoring report — endpoint + MCP tool + demo Scoring tab (WS2)#425
thewrz merged 11 commits into
mainfrom
feat/hierarchy-scoring-report

Conversation

@thewrz

@thewrz thewrz commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Why

Pressure-phase prep (WS2 of the robustness program; WS1 = #423, merged). Before WS3 automates a gold-corpus pass/fail gate, a human needs to eyeball inference quality: a worst-first, filterable view of every scored paragraph's hierarchy-inference confidence for a stored spec — the "report before the gate." Closes #424.

What

A stored spec's per-paragraph confidence becomes retrievable (REST + MCP) and browsable (demo), reading what getSpec already derives (meta.inference, ADR-055) — no change to the inference engine, parser, worker, or DB.

  • src/lib/hierarchy-report.tsbuildHierarchyReport(tree, source, threshold?): all scored structural paragraphs, worst-first, rich (label via canonical getLabel, preview, confidence, signalUsed/agreed, evidence, conflicts?) + counts + unscoredReason.
  • GET /specs/:id/hierarchy-reportApiResponse<HierarchyReport> (400/404/500), documented in openapi.yaml.
  • MCP get_hierarchy_report — read tier, contract-bound (ADR-044), never throws.
  • Demo "Scoring" tab — two-pane triage (worst-first list + filters all · <50% · <60% · document-order) beside the spec, click-to-jump (reuses audit.js's expandAncestors/hover-walker).

Design decisions

  • Server-side shared capability + demo view (not demo-only) — the scoring is a tested module boundary reusable beyond the demo.
  • DRY refinement (recorded): the spec proposed folding summarizeHierarchy into one shared walk; that module is instead left untouched (its flat tally-recurse differs from the report's ordinal-aware label walk — merging = a parameterized knot + touches a shipped contract). Consistency is pinned by a counts-equivalence invariant test. Same external behavior.
  • Sole risk — label non-drift — pinned by a test asserting every ScoredParagraph.label equals renderMarkdown's label for that node.

Testing

  • Unit tests pass (35 new backend tests + 9 demo model tests green locally; label-non-drift + counts-equivalence + never-throw all pinned)
  • Integration tests pass — CI-authoritative: REST contract gate + MCP contract test (INV-1/2/3) are DB-gated and were not run locally; route↔openapi↔MCP wiring hand-verified consistent
  • Manual verification: open the demo Scoring tab for a DOCX spec (worst-first list, filters, click-to-jump highlight) and a UFGS spec (confirms the unscoredReason header) — the one unexercised gate (needs the backend + Postgres running)
  • CI green

Deferred (non-blocking, reviewer-triaged)

Demo polish for a follow-up: a locateRow fallback when a nodeId isn't in the sheet (mirror audit.js's 3-tier), and a filter-switch left/right selection desync.

🤖 Co-authored by Claude Opus 4.8 (1M context). Closes #424.

Summary by CodeRabbit

  • New Features
    • Added a new Scoring view in the web UI with per-paragraph confidence, worst-first ordering, confidence filters, and click-to-jump highlighting.
    • Introduced a read-only hierarchy scoring report API endpoint for specs, including OpenAPI response/schema support, plus a matching MCP tool.
    • Displays report counts, preview text, and clearer handling for unscored and conflicts.
  • Documentation
    • Added design and end-to-end implementation docs for the hierarchy-scoring report workflow.
  • Tests
    • Added unit/integration coverage for report generation, REST handler behavior, UI helper logic, and contracts.

thewrz and others added 7 commits July 8, 2026 13:40
Brainstormed design for #424 — the "report before the gate": a stored
spec's per-paragraph hierarchy-inference confidence becomes retrievable
(REST + MCP) and browsable (demo two-pane "Scoring" tab), reading what
getSpec already derives (meta.inference, ADR-055).

Decisions: server-side shared/tested capability + demo view; all scored
paragraphs worst-first; rich human-readable entries; two-pane triage +
jump. DRY via one shared walk (summarizeHierarchy refactored to consume
it, output shape unchanged). Sole risk pinned: label non-drift vs
renderMarkdown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4-task TDD plan for #424: scoring module (buildHierarchyReport) →
REST endpoint + openapi → MCP tool → demo Scoring tab. Single PR.

Refines the spec's DRY mechanism: summarizeHierarchy stays untouched
(its walk is a flat tally-recurse; the report's must be ordinal-aware
for labels — merging them is the parameterized-knot the DRY rule warns
against, and it touches a shipped contract). Consistency is pinned by a
counts-equivalence invariant test instead. External contract unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rst-first)

Adds the pure scoring module for WS2 (#424): buildHierarchyReport() turns a
SpecTree into a per-paragraph hierarchy-inference report, worst-confidence
first, with the same CSI labels renderMarkdown emits. The label walk mirrors
markdown.ts's renderRoot/renderPart/renderArticle/renderPrNode chain
(position-based labeling, ordinal advanced only past consumesNumber
siblings); the counting mirrors hierarchy-summary.ts's vanish-prune + skip
discipline so the two independent implementations cannot silently drift (see
the counts-equivalence test) without touching that module's exports.

Also completes the generator barrel: renderMarkdown was only ever exported
from generator/markdown.ts directly, not re-exported through
generator/index.ts, forcing existing callers (fixture-snapshot.ts,
mcp/resources.ts) to reach past the module boundary. Re-exporting it from the
barrel lets this module's test (and future callers) respect the
sibling-barrel-only import rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wraps buildHierarchyReport (WS2, #424) in a REST endpoint so callers can
fetch the full per-paragraph hierarchy-inference scoring report,
worst-confidence-first, for a spec. Mirrors getSpecLineageHandler's
validate → fetch → 404/200 shape; documents the response as new
HierarchyReport/ScoredParagraph component schemas in openapi.yaml,
matching the existing HierarchySummary/SpecNodeInference conventions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the MCP counterpart to GET /specs/:id/hierarchy-report (WS2,
issue #424), mirroring the coordination_report template: a dedicated
handler+registration file, a read-tier capability, and a contract-map
entry so INV-1/2/3 route<->tool parity holds. The handler validates
its own specId shape (z.uuid()) before touching the DB, so a
syntactically invalid id short-circuits distinctly from a not-found
spec — matching the REST handler's z.uuid().safeParse gate. Never
throws; failures surface as { isError: true }.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the demo's Scoring tab, the final piece of WS2 (#424): a two-pane view
(worst-first paragraph list left, spec render right) over
GET /specs/:id/hierarchy-report, mirroring js/audit.js's expandAncestors /
createHoverWalker / click-to-jump machinery so paragraph confidence review
gets the same UX the coordination Report tab already has.

The filter/sort logic (all / <50% / <60% / document-order) lives in a new
pure js/scoring-filter.mjs, unit-tested in scoring.test.mjs (mirrors
compare-filter.test.mjs's model-only style) so the row-selection behavior is
verified without a DOM.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two non-blocking findings from the whole-branch review:
- Drop the vestigial `depth` field from the 'pr' LabelCtx variant —
  computeLabel never reads it (pr labels use index + node type only);
  the article/pr childContext branches collapse to one factory.
- Document the invariant behind the NON_STRUCTURAL recurse: passing ctx
  unchanged is safe only because parsers emit note/continuation with
  children:[] and 'spec' never appears in tree.parts, so no structural
  child inherits the constant ctx (which would collide labels).

No behavior change; 7/7 hierarchy-report tests green, tsc + eslint clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dfc93321-7976-4c59-b1eb-536e10e706b7

📥 Commits

Reviewing files that changed from the base of the PR and between 9d1b79e and 3c407c3.

📒 Files selected for processing (3)
  • docs/superpowers/specs/2026-07-08-hierarchy-scoring-report-design.md
  • examples/web_ui_demo/js/scoring.js
  • src/lib/hierarchy-report.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/superpowers/specs/2026-07-08-hierarchy-scoring-report-design.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/lib/hierarchy-report.ts
  • examples/web_ui_demo/js/scoring.js

📝 Walkthrough

Walkthrough

Adds a shared hierarchy-scoring report module, exposes it through a new REST endpoint and MCP tool, and wires a new Scoring view into the web demo. Supporting docs, OpenAPI contracts, and tests are added across the feature path.

Changes

Hierarchy scoring report feature

Layer / File(s) Summary
Design and implementation docs
docs/superpowers/plans/2026-07-08-hierarchy-scoring-report.md, docs/superpowers/specs/2026-07-08-hierarchy-scoring-report-design.md
Adds planning and design documents for the hierarchy-scoring report flow, backend/module boundaries, REST and MCP exposure, and demo UI behavior.
Shared hierarchy scoring module
src/lib/hierarchy-report.ts, src/lib/hierarchy-report.test.ts, src/generator/index.ts
Implements buildHierarchyReport, report types, traversal and label rules, worst-first ordering, unscored handling, and tests that cover labels, ordering, vanish pruning, truncation, and conflicts.
REST endpoint and OpenAPI contract
src/api/specs.ts, src/api/specs.test.ts, src/api/router.ts, src/api/contract.integration.test.ts, openapi.yaml
Adds GET /specs/:id/hierarchy-report, its handler, route registration, schema definitions, contract coverage, and handler tests.
MCP tool wiring
src/mcp/hierarchy-report-tools.ts, src/mcp/hierarchy-report-tools.test.ts, src/mcp/capabilities.ts, src/mcp/contract-map.ts, src/mcp/tools.ts
Adds the get_hierarchy_report tool, its validation and error handling, capability tier mapping, REST↔tool contract mapping, registration wiring, and tests.
Demo Scoring tab UI
examples/web_ui_demo/js/scoring.js, examples/web_ui_demo/js/scoring-filter.mjs, examples/web_ui_demo/js/api.js, examples/web_ui_demo/js/app.js, examples/web_ui_demo/index.html, examples/web_ui_demo/css/app.css, examples/web_ui_demo/scoring.test.mjs
Adds the Scoring tab, report fetching, filter helpers, row rendering, spec-pane highlighting, layout/styling, and helper tests.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ScoringJS
  participant ApiJS
  participant RestEndpoint
  participant HierarchyReportModule
  User->>ScoringJS: select spec / filter
  ScoringJS->>ApiJS: getHierarchyReport(specId)
  ApiJS->>RestEndpoint: GET /specs/:id/hierarchy-report
  RestEndpoint->>HierarchyReportModule: buildHierarchyReport(tree, source)
  HierarchyReportModule-->>RestEndpoint: HierarchyReport
  RestEndpoint-->>ApiJS: HierarchyReport JSON
  ApiJS-->>ScoringJS: report data
  ScoringJS->>ScoringJS: selectScoringRows(paragraphs, filter)
  ScoringJS-->>User: render rows + spec pane
  User->>ScoringJS: click row
  ScoringJS->>ScoringJS: locateNode / pulseNode
Loading
sequenceDiagram
  participant MCPClient
  participant ToolRegistry
  participant handleGetHierarchyReport
  participant DB
  participant HierarchyReportModule
  MCPClient->>ToolRegistry: call get_hierarchy_report(specId)
  ToolRegistry->>handleGetHierarchyReport: dispatch
  handleGetHierarchyReport->>handleGetHierarchyReport: validate specId (Zod)
  handleGetHierarchyReport->>DB: getSpecTree(specId)
  handleGetHierarchyReport->>DB: getSpecSource(specId)
  handleGetHierarchyReport->>HierarchyReportModule: buildHierarchyReport(tree, source)
  HierarchyReportModule-->>handleGetHierarchyReport: HierarchyReport
  handleGetHierarchyReport-->>MCPClient: ToolResult (ok or isError)
Loading

Possibly related PRs

  • wrzonance/SpecR#24: Extends the same MCP tool registration flow in src/mcp/tools.ts used here to wire get_hierarchy_report.
  • wrzonance/SpecR#334: Establishes the capability-tier and REST↔MCP contract-map framework that this PR adds the new hierarchy report tool into.
  • wrzonance/SpecR#418: Provides the underlying hierarchy-inference confidence data and threshold semantics consumed by buildHierarchyReport.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: a per-paragraph hierarchy-scoring report with endpoint, MCP tool, and demo tab.
Linked Issues check ✅ Passed The PR delivers the report module, REST endpoint, MCP tool, and demo Scoring tab with the required filters, ordering, and tests.
Out of Scope Changes check ✅ Passed The added docs, tests, and wiring all support the hierarchy-scoring feature; no unrelated code changes are evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/hierarchy-scoring-report

Comment @coderabbitai help to get the list of available commands.

@thewrz
thewrz marked this pull request as ready for review July 8, 2026 22:16
thewrz and others added 3 commits July 8, 2026 15:17
…verage

CI's contract gate (contract.integration.test.ts) requires every documented
success-JSON op to have a live response assertion OR a RESPONSE_ALLOWLIST
entry. The new hierarchy-report route had neither. Every sibling /specs/{}/*
GET (incl. get /specs/{} and get /specs/{}/lineage — the route this handler
mirrors) lives in RESPONSE_ALLOWLIST, not RESPONSE_COVERED, so this follows
that convention. Only surfaced in CI — this invariant is DB-gated and could
not run in the local (no-DATABASE_URL) sandbox.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…or-state consistency

Fixes three bugs a code review found in the web-UI demo's Scoring tab:
(1) clicking a scored part/article row silently failed to locate because
read-only sheets never stamp [data-node-id] on heading bars — now falls
back to the sheet head (mirroring audit.js's openSheet 3-tier degrade);
(2) the clear-selection branch of loadSelected didn't bump the stale-fetch
guard, letting an old in-flight request repopulate a pane that now has
nothing selected; (3) a load failure only replaced the right pane, leaving
the left rows/summary showing the previous spec while the picker and error
message referred to the new (failed) one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes the same-class hazard as the cleared-selection fix: the "spec no
longer loaded in the project" early-return in loadSelected returned without
invalidating the request token, so an in-flight hierarchy-report response
from a prior selection could still pass the stale guard and repopulate the
pane. Now calls requestGuard.bump() before returning, matching the other
early exits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/lib/hierarchy-report.ts (1)

46-46: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Minor asymmetry between NON_STRUCTURAL and consumesNumber for the 'spec' type.

NON_STRUCTURAL treats 'spec' as non-structural (skipped in visitNode), but consumesNumber (imported from ../ast/index.js) only excludes 'note'/'continuation'/vanish — it does not exclude 'spec'. If a 'spec' node were ever encountered as a sibling in walkSiblings, it would be skipped for labeling/tallying purposes but would still incorrectly advance ordinal on line 143. This is currently inert only because of the parser invariant already called out in the comment above visitNode ("'spec' never appears in tree.parts"), but the two sets diverging is a latent inconsistency if that invariant is ever relaxed.

♻️ Optional defensive tightening
 function walkSiblings(
   nodes: readonly SpecNode[],
   ctxAt: (index: number) => LabelCtx,
   acc: Acc,
   threshold: number
 ): void {
   let ordinal = 0;
   for (const node of nodes) {
     visitNode(node, ctxAt(ordinal), acc, threshold);
-    if (consumesNumber(node)) ordinal += 1;
+    // consumesNumber() doesn't exclude 'spec'; guard explicitly so a future
+    // parser relaxation can't silently shift labels via a non-structural sibling.
+    if (consumesNumber(node) && !NON_STRUCTURAL.has(node.type)) ordinal += 1;
   }
 }

Also applies to: 131-145

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/hierarchy-report.ts` at line 46, The handling of `'spec'` is
inconsistent between `NON_STRUCTURAL` and `consumesNumber`, which can let
`walkSiblings` advance `ordinal` for nodes that `visitNode` skips. Update the
sibling-numbering logic in `hierarchy-report` so the same node types are treated
as non-structural everywhere, using the existing
`NON_STRUCTURAL`/`consumesNumber` path around `visitNode`, `walkSiblings`, and
the ordinal increment. Keep the parser invariant comment intact, but make the
numbering behavior defensive against future `'spec'` siblings.
openapi.yaml (1)

5197-5242: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider composing ScoredParagraph from SpecNodeInference via allOf.

confidence, signalUsed, agreed, and evidence here are copy-pasted verbatim (including descriptions/enums) from SpecNodeInference (Lines 5167-5196). An allOf: [{ $ref: '#/components/schemas/SpecNodeInference' }, { type: object, properties: { nodeId, nodeType, ilvl, label, preview, conflicts } } ] would remove the duplication and prevent future drift between the two schemas.

Given the PR's stated intent to keep this report loosely coupled from existing summary shapes, this is optional — flagging only as a maintainability improvement.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openapi.yaml` around lines 5197 - 5242, ScoredParagraph duplicates several
inference fields from SpecNodeInference, so update the schema to compose from
SpecNodeInference using allOf and keep only the paragraph-specific properties in
ScoredParagraph. Locate the ScoredParagraph schema and move confidence,
signalUsed, agreed, evidence, and any shared inference metadata into the shared
SpecNodeInference reference, leaving nodeId, nodeType, ilvl, label, preview, and
conflicts as the report-specific additions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/specs/2026-07-08-hierarchy-scoring-report-design.md`:
- Around line 44-61: Update the design doc to match the locked implementation
plan by removing references to refactoring summarizeHierarchy or having it
consume walkScored, and adjust the architecture diagram, DRY section, and file
map so they only describe the new hierarchy-report path. Use the existing
symbols buildHierarchyReport, walkScored, summarizeHierarchy, and
src/lib/hierarchy-report.ts to locate the stale references, and ensure the text
reflects that summarizeHierarchy remains unchanged while the cohort only adds
the new report module and generator wiring.

In `@examples/web_ui_demo/js/scoring.js`:
- Around line 192-210: The .scoring-row items in makeRow are focusable but still
lack keyboard activation for Enter/Space, since createHoverWalker only covers
navigation keys and Escape. Update the scoring row behavior so a focused row
triggers locateRow on Enter and Space, either by adding a key handler in makeRow
or by using a button-like control with the same row styling and existing
dataset.nodeId hookup.

---

Nitpick comments:
In `@openapi.yaml`:
- Around line 5197-5242: ScoredParagraph duplicates several inference fields
from SpecNodeInference, so update the schema to compose from SpecNodeInference
using allOf and keep only the paragraph-specific properties in ScoredParagraph.
Locate the ScoredParagraph schema and move confidence, signalUsed, agreed,
evidence, and any shared inference metadata into the shared SpecNodeInference
reference, leaving nodeId, nodeType, ilvl, label, preview, and conflicts as the
report-specific additions.

In `@src/lib/hierarchy-report.ts`:
- Line 46: The handling of `'spec'` is inconsistent between `NON_STRUCTURAL` and
`consumesNumber`, which can let `walkSiblings` advance `ordinal` for nodes that
`visitNode` skips. Update the sibling-numbering logic in `hierarchy-report` so
the same node types are treated as non-structural everywhere, using the existing
`NON_STRUCTURAL`/`consumesNumber` path around `visitNode`, `walkSiblings`, and
the ordinal increment. Keep the parser invariant comment intact, but make the
numbering behavior defensive against future `'spec'` siblings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8bb9eb72-12f7-42dc-8b9f-9aefee51d8f6

📥 Commits

Reviewing files that changed from the base of the PR and between 3b588e0 and 9d1b79e.

📒 Files selected for processing (22)
  • docs/superpowers/plans/2026-07-08-hierarchy-scoring-report.md
  • docs/superpowers/specs/2026-07-08-hierarchy-scoring-report-design.md
  • examples/web_ui_demo/css/app.css
  • examples/web_ui_demo/index.html
  • examples/web_ui_demo/js/api.js
  • examples/web_ui_demo/js/app.js
  • examples/web_ui_demo/js/scoring-filter.mjs
  • examples/web_ui_demo/js/scoring.js
  • examples/web_ui_demo/scoring.test.mjs
  • openapi.yaml
  • src/api/contract.integration.test.ts
  • src/api/router.ts
  • src/api/specs.test.ts
  • src/api/specs.ts
  • src/generator/index.ts
  • src/lib/hierarchy-report.test.ts
  • src/lib/hierarchy-report.ts
  • src/mcp/capabilities.ts
  • src/mcp/contract-map.ts
  • src/mcp/hierarchy-report-tools.test.ts
  • src/mcp/hierarchy-report-tools.ts
  • src/mcp/tools.ts

Comment thread examples/web_ui_demo/js/scoring.js
…ard, doc sync

Two actionable + one nitpick from CodeRabbit on PR #425 (the openapi allOf
nitpick is intentionally skipped — the report shape is deliberately decoupled
and openapi is already accurate):

- scoring.js: rows are tabIndex=0 but createHoverWalker only binds Arrow/Escape,
  so a keyboard user who Tabs onto a row couldn't activate it. Add an Enter/Space
  keydown delegation mirroring the click one (preventDefault stops Space-scroll).
- hierarchy-report.ts: consumesNumber does not exclude 'spec', so a 'spec'
  sibling would advance the CSI ordinal while visitNode skips it. Guard the
  ordinal advance with !NON_STRUCTURAL — inert today ('spec' never in tree.parts)
  but keeps the advance-set identical to the skip-set. No real-tree label change
  (7/7 label/counts tests still green).
- design doc: sync the architecture diagram, DRY section, and file map to the
  locked decision (summarizeHierarchy left untouched; counts-equivalence test),
  which the plan already reversed — a future reader was being misled.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thewrz

thewrz commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

CodeRabbit triage (2 actionable + 2 nitpicks) — resolved in 3c407c3

Actionable (both fixed):

  • design.md stale content — the design doc still described refactoring summarizeHierarchy, which the plan reversed. Synced the architecture diagram, DRY section, and file map to the locked decision (summarizeHierarchy untouched; counts-equivalence test).
  • scoring.js keyboard activation — rows were tabIndex=0 but only Arrow/Escape were bound, so a Tab-focused row couldn't fire the jump. Added an Enter/Space keydown delegation mirroring the click handler.

Nitpicks:

  • hierarchy-report.ts 'spec' ordinal asymmetryfixed. consumesNumber doesn't exclude 'spec', so a 'spec' sibling would advance the ordinal though visitNode skips it. Guarded the advance with !NON_STRUCTURAL. Inert today ('spec' never in tree.parts) but keeps the advance-set == skip-set; 7/7 label/counts tests still green (no real-tree change).
  • openapi.yaml compose ScoredParagraph via allOfskipped (intentional). CR flagged this as optional; the report shape is deliberately decoupled from the summary schemas, and the openapi is already accurate (no drift). Composing via allOf would re-introduce the coupling the design chose to avoid.

Verification: tsc + eslint clean, hierarchy-report 7/7, scoring 17/17, full demo suite 107/107, build clean.

@thewrz
thewrz merged commit a4e6fc5 into main Jul 9, 2026
13 checks passed
@thewrz
thewrz deleted the feat/hierarchy-scoring-report branch July 9, 2026 05:45
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.

feat(reporting): per-paragraph hierarchy-scoring report — endpoint + MCP tool + demo view (pressure-phase WS2)

1 participant