feat(unic-spec-review): shared context (existing comments + Landscape Brief)#224
Conversation
…e detector Review agents need two shared inputs computed once per run: prior discussion already on the Confluence page (so resolved points are not re-raised), and the repo's technology landscape (so feasibility/testability is judged against the actual stack, not assumptions). Changes: - Add fetchConfluenceComments to atlassian-fetch (footer + inline, v1 REST API, injected fetch, paginated via _links.next, read-only) - Add scripts/lib/landscape-detector.mjs producing a LandscapeBrief from repo manifests + file listing + declared adjacent systems; stack never hardcoded - node:test unit tests for both modules with injected deps; no live services - CHANGELOG bullets under [Unreleased] Refs #204 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Promote [Unreleased] CHANGELOG section and bump plugin version for the shared-context slice (Confluence comment read + Landscape Brief). Refs #204 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… stub Biome noPrototypeBuiltins; behaviour unchanged. Refs #204 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sensitive The detector builds paths with node:path join, which emits backslashes on Windows, so the POSIX-keyed in-memory fs stub matched nothing and every detection assertion failed on windows-latest CI. Normalise both sides to forward slashes in the stub. POSIX behaviour unchanged. Refs #204 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🔍 Comprehensive PR ReviewPR: #224 — feat(unic-spec-review): shared context (existing comments + Landscape Brief) SummaryBoth new modules ( Verdict: ✅
🟡 Medium Issues (Recommended to Fix)M1: Unbounded Pagination Loop in
|
| # | Issue | Location | Agent | Suggestion |
|---|---|---|---|---|
| L1 | @vue/core is a non-existent npm package |
landscape-detector.mjs frameworkMap |
code-review | Remove dead key — vue already covers all real Vue projects |
| L2 | node:test detection loses to Playwright dep check in mixed projects |
landscape-detector.mjs detectTestRunner |
code-review | Move node --test script check to the top (before dep-presence checks) |
| L3 | Empty id string on malformed API response |
atlassian-fetch.mjs:553 |
error-handling | Add JSDoc note: always non-empty for real API responses |
| L4 | Broad catch {} swallows EACCES/EMFILE as if ENOENT |
landscape-detector.mjs:48-89 |
error-handling | Intentional design — leave as-is; future upgrade: injectable onError |
| L5 | reachableProd false-negative when readdirSync fails |
landscape-detector.mjs:222 |
error-handling | Replace readdirSync glob with 4× existsSync per config variant |
| L6 | Secondary detection branches untested (build.gradle, Django, Cypress, @jest/core) | landscape-detector.mjs:185-230 |
test-coverage | Add ~4 targeted it blocks matching existing test style |
| L7 | No network-error test for fetchConfluenceComments |
tests/atlassian-fetch.test.mjs |
test-coverage | Add one fetchThrows test mirroring the fetchConfluencePage pattern |
| L8 | "Pure function" overstates FP purity in detectLandscape JSDoc |
landscape-detector.mjs:230 |
comment-quality | Stateless, injectable — all I/O goes through deps; never throws. |
| L9 | Status note in AGENTS.md still describes S1 | CLAUDE.md (symlinked AGENTS.md) |
docs-impact | Extend to S3: URL classify → Confluence fetch → comments read → LandscapeBrief |
| L10 | Status note in CONTEXT.md still describes S1 | CONTEXT.md |
docs-impact | Same — mention fetchConfluenceComments + landscape-detector as implemented |
✅ What's Good
detectLandscapenever throws — defensive helpers mean no broken manifest crashes the pipelineadjacentSystemsdefensively copied — caller array mutation can't corruptLandscapeBrief; tested- Pagination
_links.nexthandled correctly —confluenceBaseprefix applied; while-loop terminates on falsy - Author fallback chain (
displayName ?? accountId ?? '') correct and tested includingcreatedBy: null Array.isArray(json?.results)guard — prevents crash on unexpected Confluence top-level shape- Separator-insensitive
stubFs— genuine cross-platform tests (macOS/Linux/Windows CI all pass) - Pagination call-count assertion — asserts both call count and returned comment identity, not just "no error"
- Comment quality high — cross-platform rationale, pagination mechanics, and typedef properties all documented correctly; no comment rot
- Typed
FetchErrorsurface —FetchErrorKindunion lets callers pattern-match on kind, not strings - No primitive duplication — all 4 new types fill genuine gaps in the existing type surface
- No new ADR needed — all design decisions already covered by ADRs 0001–0004
📋 Suggested Follow-up Issues
| Issue | Priority |
|---|---|
Fix node:test runner priority over Playwright dep (L2) |
P2 |
| Update AGENTS.md + CONTEXT.md status notes to S3 (L9, L10) | P2 |
| Add missing landscape-detector tests: build.gradle, Django, Cypress, @jest/core (L6) | P3 |
Add network-error test for fetchConfluenceComments (L7) |
P3 |
Fix reachableProd to use existsSync per config variant (L5) |
P3 |
Reviewed by Archon comprehensive-pr-review workflow (5 agents)
Full artifacts: /artifacts/runs/96b0b5f2b66d64a7b5f313b5643d51d4/review/
Resolve the actionable findings from the comprehensive review of the S3 slice (existing-comments read path + Landscape Brief): - prefer the `node --test` script over a Playwright dependency when selecting the test runner (mixed projects were mis-detected) - stop double-reporting Playwright in `testFrameworks` — it is already carried precisely by the `testRunner` field - drop the non-existent `@vue/core` framework key - detect `reachableProd` Playwright configs with per-variant `existsSync` instead of a directory-listing glob, so a `readdir` failure no longer yields a false negative - cap `fetchConfluenceComments` pagination at 50 pages as an infinite-loop guard against a misbehaving `_links.next` - tighten the JSDoc on `detectLandscape` (stateless/injectable, not "pure") and the `ConfluenceComment.id` typedef - add unit tests for the previously-untested landscape branches (build.gradle, Django, FastAPI, Flask, Cypress, `@jest/core`, TypeScript-without-tsconfig, node:test-over-Playwright) and a `fetchConfluenceComments` transport-error case Why: these are code-quality and test-coverage refinements to the unreleased 0.1.3 slice; folded into its CHANGELOG entry rather than a re-bump since 0.1.3 has no tag yet. Findings that named locked design decisions (vendoring duplication, broad ENOENT catch) were left as-is. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
✅ Review findings addressed — commit aa91f5dResolved the actionable findings from the comprehensive review. All 141 unit tests pass,
Plus: capped Intentionally left as-is (locked design)
Read-only/write-safety invariant untouched: this slice only adds read paths ( |
Second-pass cleanup after the re-review confirmed the prior fix commit
behaved correctly but introduced one regression and surfaced a latent
contract gap:
- Restore the `detectLandscape` never-throws contract: route every
existence check through a new `tryExists` helper. Previously the raw
`existsSync` (for tsconfig/pyproject/Cargo/go.mod/Gemfile/pom/gradle
and the new Playwright-config probes) ran outside the try/catch
readers, so a throwing `existsSync` (EACCES/EMFILE) escaped — the
documented contract was not actually upheld. Now covered by a
"does not throw when existsSync fails" test.
- Surface comment-pagination truncation: add a `truncated` flag to
`ConfluenceCommentsResult`, set when the 50-page cap fires, so a
capped (incomplete) comment set is observable instead of a silent
data loss. Covered by a runaway-`_links.next` test asserting the cap
at exactly 50 pages.
- Model `testRunner` as a `TestRunner` string-literal union instead of
bare `string`, per the repo convention (matches `FetchErrorKind`).
- Remove the now-dead `tryListDir` helper plus its `readdirSync`
dependency seam — orphaned by the previous commit's switch to
per-variant `existsSync` for reachableProd (it had started emitting a
Biome noUnusedVariables warning). Repoint its stale test.
- Add coverage for all four `playwright.config.{js,ts,mjs,cjs}`
variants.
Why: keeps the unreleased 0.1.3 slice honest — the never-throws claim
is now true, the pagination guard announces itself, and the new types
follow the union convention. Folded into the 0.1.3 CHANGELOG entry.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
🔁 Full re-review complete — commit 9ea627cRe-ran the 5-agent comprehensive review against the patched diff to confirm no regressions. The five original fixes were confirmed behaviorally correct. The re-review caught one regression my own fix introduced plus one latent contract gap — both now fixed. 145 tests pass, typecheck clean, Biome clean, all 8 CI checks green.
Confirmed correct / no action (per locked decisions)
|
What
Implements unic-spec-review S3 (#204): the two shared-context inputs the review reasons from, computed once per run and exposed for injection into the review agents.
atlassian-fetchwithfetchConfluenceComments(footer + inline) via the v1 REST API with injectedfetch, paginated through_links.next. Read-only, no writes. Inline comments carry the original selection text asanchor.scripts/lib/landscape-detector.mjsderives aLandscapeBrief(stack, test runner, test frameworks, tooling, reachable-prod flag, adjacent systems) from repo manifests + file listing + user-declared adjacent systems. The technology stack is never hardcoded; everything is read from the repo. Computed once and exported for injection (consumed fully in S4).Why
Review agents (Gaps, Testability, Feasibility, Non-functional, Spec-versus-Live) need prior page discussion (so resolved points are not re-raised) and the actual tech landscape (so feasibility/testability is judged against the real stack, not assumptions). Neither was available from S1+S2. Mirrors how
unic-pr-reviewcomputes Doc Context once and injects it.Scope / constraints honoured
apps/claude-code/pr-review/(deprecated v1, hook-blocked); no cross-plugin runtime imports.node:testunit tests touch no live services.Tests
fetchConfluenceComments: footer, inline+anchor, empty page,_links.nextpagination, HTML strip, author fallback (accountId / null), 401, 404, bad URL.detectLandscape: empty repo, Node+TS, React, jest/vitest/node:test runner selection, Playwright +playwright.config.jsreachableProd, Biome/ESLint tooling, Python/pytest, requirements.txt, Rust, Go, Ruby/Rails, Java, adjacentSystems passthrough + copy, malformed JSON, readdir failure.Verification
typechecktestverify:changelogci:checkCloses #204
🤖 Generated with Claude Code