feat(unic-pr-review): intent gathering via Atlassian (Pre-PR pasted URLs)#159
Conversation
…RLs) Pre-PR mode reviewed code quality with no notion of what the changed code was meant to do. This wires Work Item / Confluence intent into the Pre-PR path so aspect agents can flag intent gaps (issue #147). Changes: - Add scripts/atlassian-fetch.mjs: route pasted URLs by path, fetch Jira issues + Confluence pages via the REST APIs (built-in fetch), parse Story ACs and Bug repro/expected/actual from ADF, extract linked Confluence URLs - Add agents/intent-checker.md (Ariadne): synthesise an Intent Brief, emit per-AC verdicts, hard-stop when a promised Confluence page is unreachable - review-pr.md: Step 3.5 prompts for optional URLs (Enter to skip), Step 3.6 spawns the Intent Checker, Step 4 broadcasts the Brief to the code-reviewer, Step 5 forwards intentCheck via INTENT_CHECK_JSON - render-summary.mjs: parse + validate optional INTENT_CHECK_JSON, forward to the renderer (Intent Check block above the Severity sections) - code-reviewer.md: treat a provided Intent Brief as the authoritative ACs - Add tests/atlassian-fetch.test.mjs and extend tests/render-summary.test.mjs Fixes #147 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🔍 Comprehensive PR ReviewPR: #159 — feat(unic-pr-review): intent gathering via Atlassian (Pre-PR pasted URLs) SummaryWell-architected intent-gathering feature with injectable Verdict:
🟠 HIGH Issues (Should Fix Before Merge)1.
|
| Issue | Location | Agent | Suggestion |
|---|---|---|---|
| Exit 0 for per-URL auth errors is by design but undocumented | atlassian-fetch.mjs:813 |
code-review | Add comment explaining why exit 0 is correct for per-URL errors (JSON inspection is the intended path) |
extractConfluencePageId null fallback produces opaque HTTP 404 |
fetchConfluencePage |
error-handling | Guard: if (pageId === null) throw new FetchError(..., 'not-found', 'could not extract page ID') |
JSON.stringify(result) in main() unguarded |
atlassian-fetch.mjs:main |
error-handling | Wrap with descriptive error for future non-serialisable shapes |
collectIntent non-FetchError else branch is dead code |
atlassian-fetch.mjs:538 |
test-coverage | Skip — unreachable through production paths; acknowledged defensive guard |
parseJiraACs orderedList variant untested |
atlassian-fetch.mjs:204 |
test-coverage | Add one test with orderedList instead of bulletList |
customfield_10016 requested but never consumed |
atlassian-fetch.mjs:435 |
comment-quality | Add comment if intentional (story points placeholder); remove if accidental |
extractAbsoluteWikiUrls example too narrow |
atlassian-fetch.mjs:330 |
comment-quality | Broaden: "...or plain-text descriptions with bare links" |
docs/ structure section in README outdated |
README.md |
docs-impact | Replace docs/└── plans/ with actual 6-subdirectory layout |
✅ What's Good
- Injectable
fetch+depspattern — clean, consistent withdoctor.mjs, zero new testing infrastructure needed FetchErrortyped discriminator (unreachable | not-found | auth-error | parse-error) — intent-checker branches on kind without string-parsingcollectIntentnever throws — correct infallible boundary for a multi-URL dispatcherrender-summary.mjsINTENT_CHECK_JSON — six-layer defensive validation, all tested- Module docstring — names purpose, contract, credential source, HTTP approach, testability in under 10 lines
- ADR + AC back-references in inline comments (
// AC-7,// ADR-0004) — exactly the right "why" style CONTEXT.mdthorough — all domain terms pre-defined, zero documentation catch-up needed- 222 tests pass including all new intent-check rendering cases
📋 Suggested Follow-up Issues
| Issue Title | Priority |
|---|---|
"Surface parse-error warnings in Intent Brief" |
P2 |
"Classify internal errors in collectIntent as parse-error not unreachable" |
P2 |
"Add fetchConfluencePage timeout + parse-error tests" |
P3 |
Reviewed by Archon comprehensive-pr-review workflow · 5 parallel agents
Full artifacts: /Users/oriol.torrent/.archon/workspaces/unic/unic-agents-plugins/artifacts/runs/a9d5dfb5f5c1d6d5731cdc0c3457c672/review/
Fixed: - Relative linkedUrls now resolved to absolute URLs in fetchConfluencePage - Non-FetchError in collectIntent catch now emits parse-error (not unreachable) - extractConfluencePageId null fallback now throws FetchError not-found early - JSON.stringify in main() wrapped in try/catch with descriptive error - Exit-code comment added explaining why per-URL auth errors exit 0 - classifyIssueType JSDoc prose added (epic→story business rule) - customfield_10016 comment added (story points, intentional) - extractAbsoluteWikiUrls JSDoc example broadened - unic-pr-review added to root README plugins table and install block - README docs/ structure updated to reflect actual subdirectories - intent-checker.md Step 6 now surfaces parse-error/not-found warnings in brief Tests added: - fetchConfluencePage: auth-error (401), not-found (404), absolute linkedUrls - fetchJiraIssue: Epic→story, Defect→bug, Task→other classifications - mapFetchError: TimeoutError, generic Error, non-Error, timeout seconds - parseJiraACs: orderedList variant Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
⚡ Self-Fix Report (Aggressive)Status: COMPLETE Fixes Applied (16 total)
View all fixesHIGH
MEDIUM
LOW
Tests Added
11 new test cases — total now 233 (all passing) Skipped (1)
Suggested Follow-up Issues(none — all findings fully addressed) Validation✅ Type check | ✅ Lint (Biome + Prettier) | ✅ Tests (233 passed) Self-fix by Archon · aggressive mode · fixes pushed to |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…erage gap The two orchestrator-spawned agents were missing the `name:` and `color:` frontmatter fields. Their absence breaks tooling like the plugin-dev `validate-agent.sh`, which halts silently under `set -e` when `grep '^name:'` finds nothing — making the agents unloadable/unvalidatable. - code-reviewer (Pythia): add name/color:cyan/model:opus (opus for parity with upstream deep-review work); restore the genuine detection gap found vs the upstream pr-review-toolkit source — concurrency/resource bugs (race conditions, unawaited promises, resource/memory leaks). - intent-checker (Ariadne): add name/color:yellow/model:inherit (cheap fetch+synthesise work, no deep review). Removed the now-duplicated "Your colour is X" line from each prose body so the frontmatter owns that fact; personas are kept as they shape voice. JSON output contract, Confidence rubric, and "What NOT to look for" guardrails preserved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot review (PR #159) flagged that the IntentCheckItem validator accepted `null` verdicts (typeof null === 'object'), which then crashed the renderer in Object.entries(item.verdicts) and aborted the whole summary. Tighten the filter to require a non-null plain object and string id/title, so malformed items drop with a stderr note as intended. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ict value Two PR #159 review findings on the intent path: - Unrecognised pasted URLs (e.g. an ADO Boards link — ADO Work Item discovery is not yet wired into this path) were only warned to stderr and skipped, so a reviewer pasting only such a URL got silent empty intent. atlassian-fetch now records them as a soft `unsupported` error and the Intent Checker surfaces a warning line in the brief (not a hard-stop). - The AC verdict literal was `partial`, but the renderer surfaces verdicts verbatim and the PRD (§10) shows `partially addressed`. Standardize on the user-facing phrase in the renderer typedef and the agent contract. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The hard-stop fires for both `unreachable` and `auth-error` kinds (ADR-0004), but the user-facing message claimed the URL 'is unreachable', misleading the reviewer when the real cause was rejected credentials. Reword to 'could not be fetched (unreachable, or its credentials were rejected)'. (PR #159 review) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Multi-agent review (PR #159, Step 4) found that collectIntent called the credential loader outside any try/catch, so a present-but-malformed or unreadable ~/.unic-confluence.json threw straight out — violating the documented 'never throws' contract and bypassing the structured errors path. Guard the loader and surface a corrupt config as a global auth-error entry (exit 1), the same hard-stop as missing credentials. Also fix the stale collectIntent JSDoc (unsupported URLs are recorded, not silently skipped) and enrich the Confluence page-id not-found message with the offending URL. Adds tests for the credential-loader throw, 5xx -> unreachable, JSON parse-error, and /wiki/ URLs with no extractable page id -> not-found. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…otes Multi-agent review (PR #159, Step 4): - render-summary validated that `verdicts` is an object but never that its values are valid AcVerdicts, so an off-spec value rendered verbatim as garbage (e.g. `AC 1: [object Object]`) in the PR summary. Validate values against a single `AC_VERDICTS` source of truth now exported from review-summary-renderer.mjs and drop offending items with a stderr note. - The renderer dropped the optional `note` the Intent Checker emits for unreachable/parse-error items, so 'could not be fetched' items rendered with no explanation. Add `note` to IntentCheckItem and render it. - Dropped-item stderr warnings now name the offending id. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sue-147 # Conflicts: # apps/claude-code/unic-pr-review/CHANGELOG.md # apps/claude-code/unic-pr-review/agents/code-reviewer.md # apps/claude-code/unic-pr-review/commands/review-pr.md
Post-merge review (PR #159) found the merge of #158's multi-aspect fan-out and #159's intent gathering left two stale single-agent references: - Step 3.6 said 'Use the Task tool' to spawn the intent-checker, but the frontmatter allowed-tools declares only `Agent` and develop had deliberately aligned every body reference to 'Agent tool' — the merge reintroduced the exact mismatch (and a tool not in allowed-tools). Use 'Agent tool'. - The Step 3 large-diff warning and the CHANGELOG entry still described a single 'agent' / broadcasting 'to the code-reviewer'; Step 4 now fans out to every aspect agent in SPAWN_SET. Reword both to the plural fan-out reality. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Why
Pre-PR mode reviewed code quality alone — the review agents had no notion of what the changed code was supposed to do, so intent gaps went undetected. This wires Work Item / Confluence intent into the Pre-PR path (issue #147), closing that gap: the reviewer pastes the relevant Jira/Confluence URL(s), the plugin fetches them, and every aspect agent receives the extracted intent as context.
What
scripts/atlassian-fetch.mjs(new) — routes pasted URLs by path (/browse/→ Jira,/wiki/→ Confluence), fetches Work Items + pages via the Atlassian REST APIs using built-infetch(Node 22+), parses Story ACs and Bug repro/expected/actual from ADF, and extracts linked Confluence URLs. Credentials vialib/credentials.mjs(file + env-var override).fetchis injectable for tests.agents/intent-checker.md(new, Ariadne, yellow) — calls the fetch script via Bash, synthesises an Intent Brief, emits per-AC verdicts, and hard-stops when a promised Confluence page is unreachable (ADR-0004). No Bot Signature footer.commands/review-pr.md— Step 3.5 prompts for optional URLs (Enter to skip, US 30), Step 3.6 spawns the Intent Checker and aborts on hard-stop (US 29), Step 4 broadcasts the Brief verbatim to the code-reviewer, Step 5 forwardsintentCheckviaINTENT_CHECK_JSON.scripts/render-summary.mjs— parses + validates optionalINTENT_CHECK_JSON, drops malformed items with a stderr note, forwards survivors so the renderer surfaces the Intent Check block above the Severity sections (PRD §10).agents/code-reviewer.md— Step 3 now treats a provided Intent Brief as the authoritative source of acceptance criteria.tests/atlassian-fetch.test.mjs(routing, key/page-id extraction, credential resolution, Story/Bug ADF parsing, Confluence excerpt + link extraction, error classification,collectIntent/mainshape) and extendedtests/render-summary.test.mjs(Intent Check rendering / omission / malformed-input handling).Acceptance criteria
All nine ACs from #147 are covered — see the per-AC mapping in the implementation report. Empty intent omits the block (US 30); unreachable promised intent hard-stops naming the URL + setup command (AC-7).
Validation
pnpm --filter unic-pr-review typecheck✅pnpm --filter unic-pr-review test✅ 222 passed / 0 failedpnpm --filter unic-pr-review verify:changelog✅pnpm ci:check(repo-wide) ✅apps/claude-code/pr-review/.Fixes #147
🤖 Generated with Claude Code