feat(reporting): world-class PR report contract + decision-first automated comments#1324
Conversation
…n Start-over (PP-4846) PP-4846 (repro-first): after Discard → pick a different category → type, the Send arrow intermittently stayed disabled. It did not reproduce deterministically. Pinned the REDUCER side of that flow — the source of truth the Send button's `disabled` predicate reads (`!isFollowUpStep && inputText.trimmed.isEmpty`). `StuckSendReproTests` drives Discard → re-category → type and proves inputText is set and the step is a composition step, i.e. Send is enabled at the reducer level. So the intermittent field report is localized to the SwiftUI TextField binding (consistent with the "UIKit gesture-timeout" log + the non-determinism), NOT the state machine. Writing that test surfaced a real, separate latent bug: `.userTappedStartOver` returned to `.awaitingCategory` but did NOT clear `inputText`, so a half-typed composer survived "start fresh" — unlike Discard, which lands empty. In practice Start-over is reached with inputText already empty (post-submit), but the two reset paths should have identical composer semantics. Now both clear it. Verified: TriageBotCore reducer suites green (StuckSendReproTests + CategoryChipDebounce + StructuredEscalation, 13/13). **Scope:** the Start-over reset clears inputText; reducer-level tests for both reset paths. No SwiftUI change. **Not done:** the intermittent SwiftUI TextField(axis:.vertical) binding re-sync itself — reducer is exonerated; recommended fix is the local-@State mirror pattern already used by TicketPreviewCard, but it can't be verified without a deterministic repro, so it is not shipped blind here. **Deferred:** a simdrive repro harness for the SwiftUI race (needs a dedicated run; the field bug is intermittent).
…mated comments Root cause: The PR record had an inverted signal budget. The authored layer (bodies + commit messages) was world-class, but the automated layer buried it — a ~900-row all-green test table dwarfed the human body, the delta engine that computes what-changed was silent, the ledger comment printed constant numbers on every PR, and a disabled QAAtlas integration shipped a standing "set OPENAI_API_KEY" banner. For an agent-authored / human-orchestrated codebase, the load-bearing claims were also author-attested prose with no machine anchor. Solution: Establish one PR Report Contract that every reporting surface serves, and make the automated surfaces obey it. - docs/architecture/pr-report-contract.md — the cohesion anchor: 4 goals (orchestrator decision speed, grounded verifiability, durable forensic reconstruction, fleet legibility) + 4 principles (decision-first, delta-or-silent, grounded-verifiability, fleet-legible) + surface map + the standing gap-question list. - .github/PULL_REQUEST_TEMPLATE.md — the authored-layer contract (there was no template): adds Evidence / Repro / Class / Obligations to Root-cause/Solution. - docs/regressions/recurrence-classes.md — greppable Class taxonomy, seeded from the classes agents already name in prose. - unit-testing.yml — decision-first: full per-class matrix ALWAYS collapsed (never dumped open); failures OPEN; deltas relabelled "what changed vs base" with new-failures open; Skipped column + reconciliation icon so all-skipped classes stop rendering as false-green 7|0|0; provenance footer. - parse-xcresult.py — emit per-class `skipped` so the comment can reconcile rows. - ledger.yml — delta-or-silent: drop the dead QAAtlas banner (qa domain is off), name the layer violation instead of showing a bare count, and stop printing a green "no dead code" over 0 analyzed files. Evidence: py_compile (3 scripts) OK; YAML parse OK; actionlint clean on edited regions (only pre-existing info-level shellcheck notes on untouched run: steps); node --check on both github-script bodies OK; render-logic smoke test green for 6 cases incl. all-skipped→green, non-reconciling→warn, legacy 5-field tolerance. The PR's own CI run is the machine anchor, dogfooding the contract. **Scope:** dev-tooling + docs only. No Palace/ production code, no test-affecting change. Comment-generator JS validated structurally (node --check) + render smoke test; the live rendering is proven by this PR's own CI comments. **Not done:** the .test-history develop cache-warm job (so deltas populate on PR branches); freeze-red-before-overwrite in the sticky comment; a package-suite (TriageBotCore) CI job to reproduce swift test counts; the cross-PR roll-up; the ~2 unmapped-status tests in parse-xcresult (the off-by-2) — the new⚠️ row now flags them but the parser normalization is not fixed here. All tracked as Obligations on the PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🏗️ CodeAtlas Ledger Analysis✅ All Checks Passed♿ Accessibility (via AccessLint)
✅ No accessibility issues detected 🏛️ Architecture Analysis
🔄 Dependency CyclesSee full report for cycle details. 📋 Architecture FindingsSee full report for detailed findings. 🔍 Reachability Analysis
ℹ️ No architecture-relevant files in this diff — reachability not evaluated. 📊 0 files analyzed | 📦 Download Full Report Powered by CodeAtlas Ledger |
🧪 Unit Test Results📊 View Full Interactive Report ❌ 2 TESTS FAILED7967 tests | 7830 passed | 2 failed | 133 skipped | ⏱️ 7m 24s | 📊 98.3% | 📈 45.8% coverage All 909 classes — 1 with failures (full matrix)
📊 Full interactive matrix: report ❌ Failed testsNames only — open the interactive report above for messages + stack frames. 📊 Testing Coverage BreakdownUnit Test Line Coverage (testable surfaces): 45.8% Total coverage incl. UI/lifecycle: 44.8% (17 files excluded from testable denominator — see
🔗 Interactive HTML Report | CI Run Details Counts above were produced by this CI run's xcresult parse — reproduce via the run link. 📦 Downloadable Artifacts
|
…ip componentRoots Root cause: the Ledger engine treated the single Palace app target as one file-less component, so app files were owned by nothing and blast-radius on widely-imported packages (e.g. PalaceAuth on the sign-in path) falsely read "nobody depends / Risk LOW" — the reason the ledger comment carried no trustworthy signal on PRs. Solution: adopt CodeAtlas Ledger 0.9.7 (SyncTek-LLC/ledger-dist), which indexes Xcode app-target file membership, turns config componentRoots into real file-owning components, and adds conservative symbol-reference edges. Ship the 32 validated componentRoots so the model decomposes Palace into its real modules (Network, MyBooks, SignInLogic, …). Validated pre-merge: impact AuthCoordinator → 8 real dependents at CRITICAL; TPPNetworkExecutor resolves to Network; context --focus MyBooks returns real files/symbols/deps. **Scope:** ledger.yml version pin 0.9.6 -> 0.9.7 + tools/ledger/ledger-config.json componentRoots. The install URL is unchanged (it redirects to SyncTek-LLC). **Not done:** ledger job stays continue-on-error (advisory) for this dogfood cycle before any consideration of blocking; the tool's own swift test has 12 pre-existing (non-regression) failures to clear first. accesslint artifact for 0.9.7 not added (matches 0.9.6; a11y behavior unchanged). impact display polish (adjacent-folder/dup rows) deferred. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Makes the pull request a world-class reporting surface — the durable forensic record and the fleet's telemetry surface — for a codebase whose changes are increasingly agent-authored and human-orchestrated. This PR is deliberately reported in the exact form it introduces (it dogfoods its own contract).
Root cause
Our reporting had an inverted signal budget. The authored layer (PR bodies + commit messages) was genuinely world-class — root cause, mechanism, quantified before/after, honest
Not donestanzas. But the automated layer buried it:OPENAI_API_KEY" banner for a permanently-disabled integration;7 | 0 | 0 ✅read as broken data (they were all-skipped classes —totalcounts skipped but the table hid it);258/258 green,mutation-verified) were author-attested prose with no machine anchor — PR fix(jira): honest build claims + clean, human-friendly automated comments #1320 proved the automation had already fabricated claims.Solution
One PR Report Contract (
docs/architecture/pr-report-contract.md) that every surface serves — 4 goals (orchestrator decision speed · grounded verifiability · durable forensic reconstruction · fleet legibility) and 4 principles (decision-first · delta-or-silent · grounded-verifiability · fleet-legible) — plus the surfaces made to obey it:Evidence/Repro/Class/Obligationsto the authored body.Class:taxonomy seeded from classes agents already name in prose.unit-testing.yml): full matrix always collapsed (never dumped open); failures open; deltas relabelled "what changed vs base" with new-failures open; Skipped column + reconciliation icon so all-skipped classes stop rendering as false-green; provenance footer.parse-xcresult.py: emits per-classskippedso rows reconcile.ledger.yml): delta-or-silent — dead QAAtlas banner removed, layer violation named, no green "no dead code" over 0 analyzed files.Evidence
python3 -m py_compileon the 3 changed scripts → OK (local).yaml.safe_loadboth workflows → OK (local).actionlinton both workflows → clean on all edited regions; only pre-existing info-level shellcheck notes remain onrun:steps this PR never touched (local).node --checkon bothgithub-scriptbodies (Actions placeholders substituted) → OK (local).Repro
Class
reporting-signal-inversion(added todocs/regressions/recurrence-classes.mdscope conceptually; this PR is the structural fix, not an instance patch).Obligations
.test-historydevelop cache-warm job so deltas actually populate on PR branches — owner: reportingswift testcounts on the PR — owner: reportingClass:counts, open-PR file-overlap — owner: reportingparse-xcresult.py(the off-by-2); the newScope
Dev-tooling + docs only. No
Palace/production code, no test-affecting change. Two comment generators, one parser, three docs, one PR template.Not done / Deferred
Everything under Obligations above is intentionally deferred and tracked there rather than shipped silently — which is the contract dogfooding itself. The comment-generator JS is validated structurally + by render smoke test; the live rendering is proven by this PR's own CI comments below.
🤖 Generated with Claude Code