feat(unic-pr-review): surface existing Human Threads as read-only review context#250
Conversation
…iew context Wire the already-built Human Thread classification, matcher, and Notice renderer into the orchestrator so reviewer discussion becomes read-only review context (ADR-0016). Human Threads were classified and emitted by the Fetcher but never consumed: the orchestrator never matched them to Findings, never injected the Notice, and never forwarded them to the Re-review Coordinator. Changes: - review-pr.md: new Step 1.8b (first-review) + Step 1.8b extension (re-review) run human-thread-matcher.mjs after the fan-out, annotate matched Findings, and add humanThreadsNotice to NOTICES_CONTEXT - review-pr.md: pass read-only humanThreads to the Re-review Coordinator - README.md: How-it-works prose + mermaid flowchart (classify step, Step 7b matcher, Coordinator humanThreads input, Notice at Step 8, w2 box clarified to bot threads only — never human) - matcher never suppresses or down-ranks a Finding; Confidence < 60 stays the sole filter; the ADO Writer never posts to a Human Thread Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Promote the Human Threads read-only context entry (#248) from Unreleased. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bad input - Validate findings/humanThreads are arrays after JSON.parse at CLI entry (non-array valid JSON now exits 1 with a clear message, not a cryptic TypeError) - Guard finding.startLine type before Math.abs comparison (missing startLine previously silently dropped the annotation via NaN) - Guard t.excerpt with null coalescing in notices.mjs (null/undefined excerpt from ADO data boundary previously threw TypeError) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔍 Comprehensive PR ReviewPR: #250 — feat(unic-pr-review): surface existing Human Threads as read-only review context SummaryClean, well-scoped ADR-0016 implementation. The new Verdict:
🟠 High IssueCLI entry-point has no subprocess tests📍
View recommended test file:
|
| # | Issue | Location | Suggestion |
|---|---|---|---|
| 1 | Asymmetric array guard: findings not validated (only humanThreads is) |
human-thread-matcher.mjs:67 |
Add if (!Array.isArray(findings)) return { annotatedFindings: [], unmatchedUnresolved: [] } before the humanThreads guard |
| 2 | filePath !== null misses undefined — garbles notice as `undefined:undefined` |
notices.mjs:90 |
One-char: !== null → != null |
| 3 | No try/catch around matcher call in CLI — raw stack trace on unexpected throw | human-thread-matcher.mjs:131 |
Leave as-is (stack trace more useful than generic message; behavior is correct) |
| 4 | HumanThreadEntry typedef omits status field |
notices.mjs:22-28 |
Leave as-is (intentionally narrow API for renderer) |
| 5 | excerpt: undefined entry not tested |
notices.mjs:91 |
Add one unit test with excerpt: undefined |
| 6 | Exactly-80-char excerpt boundary not tested (tests cover 14 and 100, not 80) | notices.mjs:92 |
Add 'A'.repeat(80) boundary test |
✅ What's Good
- Test suite is exemplary: 19 cases covering all four resolved statuses, ±10 proximity boundary (both sides), non-inline threads, no-mutation invariant, and PR #5570 golden regression scenario
- ADR-0016 invariants are airtight:
unmatchedUnresolvedfilter combines!RESOLVED_STATUSES.has(t.status) && !matchedThreadIds.has(t.threadId)in a single pass; Coordinator explicitly forbidsthreadActionsforhumanThreadsIDs - Orchestrator Step 1.8b is non-blocking: non-zero exit → continue with original findings; never hard-stops the run
?? '[]'env-var defaults: both env vars fall back safely to empty arrays- ADR-first implementation: ADR-0016 was accepted and merged before this PR; implementation matches consequences section point-for-point
- CHANGELOG entry is well-formed: correct
## [X.Y.Z] — YYYY-MM-DDem-dash format covering all three change areas
📋 Suggested Follow-up Issues (if not fixing in this PR)
| Issue Title | Priority |
|---|---|
Add CLI subprocess tests for human-thread-matcher.mjs |
P1 |
Align isMain guard to import.meta.url pattern |
P2 |
Reviewed by Archon comprehensive-pr-review workflow
Full artifacts: artifacts/runs/1e722a0fc56d0066498ee63c707874df/review/
- Replace endsWith isMain guard with canonical import.meta.url === pathToFileURL pattern - Add symmetric Array.isArray(findings) guard to matchHumanThreadsToFindings - Fix notices.mjs filePath null check: !== null → != null (covers undefined) - Add CLI subprocess test suite for human-thread-matcher.mjs (HIGH gap) - Add notices.test: exactly-80-char excerpt boundary and undefined excerpt tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
⚡ Self-Fix Report (Aggressive)Status: COMPLETE Fixes Applied (6 total)
View all fixes
Tests Added
Total: 695 tests passing (up from 689) Skipped (2)
Suggested Follow-up Issues(none — all actionable findings addressed) Validation✅ Type check | ✅ Lint (0 errors, 6 pre-existing infos) | ✅ Tests (695 passed) Self-fix by Archon · aggressive mode · fixes pushed to |
…ailure diagnostics Two findings from the PR #250 review of the Human Thread feature (ADR-0016): - notices.mjs pluralized the noun but not the verb, rendering "1 unresolved reviewer comment have no matching Finding" for a single thread. Make the verb agree (has/have) and assert both forms in notices.test.mjs. - review-pr.md Step 1.8b (first-review and re-review) substituted a generic "skipping thread annotations" message and discarded the matcher's own stderr, hiding whether the orchestrator serialized invalid JSON — a real bug that would silently disable Human-Thread context on every run. Relay the matcher's stderr verbatim, matching the file's convention elsewhere. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Why
unic-pr-reviewalready classified Human Threads (ADO Fetcher Step 3b) and emitted them ashumanThreads, and the matcher (human-thread-matcher.mjs) + Notice renderer (notices.mjs) were implemented and tested — but the orchestrator never consumed any of it. Reviewer discussion was invisible to a Review in every Mode. Found comparing legacypr-reviewvsunic-pr-reviewon ADO PR #5570: the legacy plugin cross-referenced a Finding to a still-open Human Thread and caught a thread marked fixed whose issue was still present. v2 surfaced none of it.This wires the feature together per ADR-0016 (Human Threads as read-only context). Closes #248.
What changed
commands/review-pr.mdhuman-thread-matcher.mjsafter the fan-out, annotate matched Findings, and addhumanThreadsNoticetoNOTICES_CONTEXT. Non-zero exit logs to stderr and continues — Human Threads are never a hard stop.humanThreadsto the Re-review Coordinator (Step 1.8a input).README.md: "How it works" prose + mermaid flowchart — three-way Thread classify, Step 7b matcher node, CoordinatorhumanThreadsread-only input, Human Thread Notice at Step 8, andw2writer box clarified to bot threads only — never human.CHANGELOG.md+ version bump 2.1.10 → 2.1.11.Invariants held
threadActionsfor them.SPAWN_TABLE/ ADR-0008 change.Verification
pnpm --filter unic-pr-review test— 687 pass / 0 failpnpm --filter unic-pr-review typecheck— cleanpnpm --filter unic-pr-review verify:changelog— ok → 2.1.11pnpm ci:check— clean🤖 Generated with Claude Code