Skip to content

feat(unic-pr-review): remaining five aspect agents + conditional spawning#158

Merged
orioltf merged 17 commits into
developfrom
archon/task-fix-issue-146
May 28, 2026
Merged

feat(unic-pr-review): remaining five aspect agents + conditional spawning#158
orioltf merged 17 commits into
developfrom
archon/task-fix-issue-146

Conversation

@orioltf
Copy link
Copy Markdown
Member

@orioltf orioltf commented May 28, 2026

Summary

  • Add five Review Aspect agents: silent-failure-hunter (Argus/red), type-design-analyzer (Euclid/magenta), pr-test-analyzer (Vesta/green), comment-analyzer (Scribe/yellow), code-simplifier (Occam/blue)
  • Rewrite changed-file-analyser.mjs to export decideSpawnSet(changedFiles) → Set<string> with a full table-driven spawn-decision implementation (ADR-0008)
  • Expand tests/changed-file-analyser.test.mjs to cover all six spawn predicates with positive and negative fixtures each

Why

Closes #146. Completes the six-agent fan-out specified in ADR-0008 — code-reviewer was the only aspect agent that existed; the other five conditional agents were deferred from issue #145.

Test plan

  • All spawn predicate tests pass (positive + negative for each of the five conditional agents)
  • Mixed-content diff test confirms all six agents are spawned when the diff touches code + tests + docs + types (≥3 source files)
  • pnpm --filter unic-pr-review test green
  • pnpm --filter unic-pr-review typecheck clean
  • pnpm ci:check clean
  • Each agent has a distinct name and colour (US 44)

🤖 Generated with Claude Code

orioltf and others added 3 commits May 28, 2026 20:21
…ning

Wire changed-file-analyser into the review-pr command (Step 4a/4b):
the orchestrator now calls decideSpawnSet to resolve which aspect agents
apply to the diff, then fans them all out in parallel via the Task tool,
merges findings before rendering. Adds a CLI entry point to
changed-file-analyser.mjs so the command can pipe git --name-only output
directly into node without a heredoc.

Closes #146

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@orioltf
Copy link
Copy Markdown
Member Author

orioltf commented May 28, 2026

🔍 Comprehensive PR Review

PR: #158 — feat(unic-pr-review): remaining five aspect agents + conditional spawning
Reviewed by: 4 specialized agents (code-review, error-handling, test-coverage, docs-impact)
Date: 2026-05-28


Summary

The PR cleanly implements the five remaining Review Aspect agents and rewrites changed-file-analyser.mjs to a table-driven spawn decision module. Logic, structure, and predicate unit tests are all solid. Two issues must be fixed before merge: the CLI guard uses a URL idiom that silently breaks on Windows CI (the review fans out with zero agents), and the CLI entry block — the actual interface review-pr.md calls — has no test coverage.

Verdict: REQUEST_CHANGES

Severity Count
🔴 HIGH 2
🟡 MEDIUM 8
🟢 LOW 2

🔴 HIGH Issues (Fix Before Merge)

1. CLI guard uses non-portable new URL() — silently breaks on Windows

📍 apps/claude-code/unic-pr-review/scripts/lib/changed-file-analyser.mjs:62

The guard if (import.meta.url === new URL(process.argv[1], 'file:').href) fails on Windows: backslashes in process.argv[1] make the URL comparison never match, so the CLI block never runs, the script exits 0 with empty stdout, and review-pr.md Step 4a parses [] — every review silently produces zero findings. Also missing the process.argv[1] && null guard (throws TypeError in REPL/unusual contexts).

Every other CLI script in this plugin already uses the correct pattern.

View fix
// top of file — add import
import { pathToFileURL } from 'node:url'

// line 62
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {

Pattern reference: apps/claude-code/unic-pr-review/scripts/lib/base-branch-resolver.mjs:67


2. CLI entry point is entirely untested

📍 apps/claude-code/unic-pr-review/scripts/lib/changed-file-analyser.mjs:62-77

The stdin→JSON pipeline — the exact interface invoked by review-pr.md via | node ... — has zero tests. The decideSpawnSet() unit tests are thorough but they bypass the CLI parsing entirely. A regression (e.g. JSON.stringify called on the Set instead of the spread) would produce empty output and zero agents with no test failure.

View recommended tests

Add a describe('CLI entry point') block to tests/changed-file-analyser.test.mjs:

import { spawnSync } from 'node:child_process'
import { fileURLToPath } from 'node:url'
import path from 'node:path'

const SCRIPT = path.resolve(fileURLToPath(import.meta.url), '../../scripts/lib/changed-file-analyser.mjs')

describe('CLI entry point', () => {
	it('emits a JSON array of agent names to stdout for a source file', () => {
		const result = spawnSync('node', [SCRIPT], { input: 'src/service.mjs\n', encoding: 'utf8' })
		assert.equal(result.status, 0)
		const agents = JSON.parse(result.stdout.trim())
		assert.ok(Array.isArray(agents))
		assert.ok(agents.includes('code-reviewer'))
	})

	it('emits an empty JSON array for empty stdin', () => {
		const result = spawnSync('node', [SCRIPT], { input: '', encoding: 'utf8' })
		assert.equal(result.status, 0)
		assert.deepEqual(JSON.parse(result.stdout.trim()), [])
	})

	it('emits an empty JSON array for whitespace-only stdin', () => {
		const result = spawnSync('node', [SCRIPT], { input: '   \n\n  \n', encoding: 'utf8' })
		assert.equal(result.status, 0)
		assert.deepEqual(JSON.parse(result.stdout.trim()), [])
	})

	it('produces valid JSON with a trailing newline', () => {
		const result = spawnSync('node', [SCRIPT], { input: 'src/a.mjs\n', encoding: 'utf8' })
		assert.ok(result.stdout.endsWith('\n'))
		assert.doesNotThrow(() => JSON.parse(result.stdout.trim()))
	})
})

🟡 Medium Issues (Needs Decision)

3. Test description contradicts its assertions

📍 tests/changed-file-analyser.test.mjs:162

Test named 'spawns only code-reviewer for a single non-typed non-doc source file' but body also asserts silent-failure-hunter. Fix: rename to 'spawns code-reviewer and silent-failure-hunter for a single plain source file'.

4. Missing stdin 'error' event handler

📍 scripts/lib/changed-file-analyser.mjs:65

'data' and 'end' handlers present but no 'error' handler. An EPIPE on stdin throws an uncaught exception bypassing the changed-file-analyser: <message> error format. Fix: add process.stdin.on('error', (err) => { process.stderr.write(...); process.exit(1) }).

5. .tsx files not matched by isTypeFile

📍 scripts/lib/changed-file-analyser.mjs:20-22

/\.ts$/ doesn't match .tsx. A PR changing only React component files will not spawn type-design-analyzer. Fix: change to /\.tsx?$/.

6. comment-analyzer predicate misses JSDoc rot in source files

📍 scripts/lib/changed-file-analyser.mjs:37

The agent's scope includes JSDoc @param/@returns rot in source files, but its predicate only fires for .md/docs changes. A pure-code PR silently skips comment rot. Consider extending to files.some(isDocFile) || files.some(isSourceFile) — but worth an ADR-0008 discussion first.

7. Windows path separator coverage missing in tests

📍 tests/changed-file-analyser.test.mjs

All tests use Unix forward-slash paths. The predicates have [/\\] for Windows backslashes but this branch is never exercised by tests. Add a few backslash-path variants.

8. ADR-0008 remains future-tense after full implementation

📍 docs/adr/0008-conditional-sub-agent-spawning.md

Consequences section says the plugin "needs" a changed-file analyser — it now has one. Spawn predicates for all 6 agents are not documented in the ADR.

9. "Review Aspect" and "Spawn Set" missing from CONTEXT.md vocabulary

📍 apps/claude-code/unic-pr-review/CONTEXT.md

Both terms appear throughout review-pr.md and ADR-0008 but have no glossary entries. Relationships section also incorrectly states agents receive an "Intent Brief" (not yet wired).

10. AGENTS.md missing two-part checklist for adding new Review Aspect agents

📍 apps/claude-code/unic-pr-review/AGENTS.md

The two-part contract (1: create agent .md in agents/, 2: add entry to SPAWN_TABLE) isn't documented. An agent file without a SPAWN_TABLE entry is silently never spawned.


🟢 Low Issues

View 2 low-priority suggestions
Issue Location Suggestion
Agent files use wrong-domain JSON examples agents/code-simplifier.md etc. Replace "Null pointer" example with a domain-appropriate finding per agent
README outdated — review-pr command missing README.md Add review-pr to Commands table; update Quick Start from "coming soon" to functional

✅ What's Good

  • Table-driven SPAWN_TABLE is clean and extensible — one entry per aspect agent
  • Predicate unit tests are excellent: all 6 entries have positive + negative fixtures, threshold boundary tests, and a mixed-content integration scenario
  • review-pr.md accurately reflects the implementation: invocation, spawn-set print, parallel launch, merge step, and agent-name-to-file mapping
  • Five new agent prompts follow the same structure as code-reviewer.md — consistent and immediately readable
  • decideSpawnSet returns Set<string> — O(1) membership checks, no duplicates
  • Zero new runtime dependencies — consistent with the plugin's zero-dep philosophy

Next Steps

  1. 🔴 Fix HIGH issues 1 & 2 (URL guard + CLI tests)
  2. 🟡 Fix trivial MEDIUMs in this PR: 3, 4, 5, 7, 10
  3. 📋 Create issues for 6 (predicate alignment), 8 (ADR), 9 (CONTEXT.md)

Reviewed by Archon comprehensive-pr-review workflow
Full artifacts: /Users/oriol.torrent/.archon/workspaces/unic/unic-agents-plugins/artifacts/runs/70d84536fd313df77c802e42714464e8/review/

Fixed:
- CLI guard: use pathToFileURL() instead of new URL() — portable on Windows
- isTypeFile: match .tsx files (was matching only .ts)
- stdin 'error' handler added to CLI entry block
- Test description: rename misleading test that listed wrong agents

Tests added:
- CLI entry point: 5 spawnSync integration tests covering stdin→stdout pipeline
- Windows backslash paths: 5 predicate tests exercising [/\\] branch

Docs updated:
- AGENTS.md: two-part checklist for adding new Review Aspect agents
- CONTEXT.md: glossary entries for Review Aspect and Spawn Set; accurate Intent Brief note
- ADR-0008: update future-tense Consequences, add Spawn Table for all 6 agents
- README: add review-pr row to Commands table, update Quick Start
- 5 agent .md files: replace wrong-domain JSON examples with domain-accurate ones

Skipped:
- Issue 6 (comment-analyzer predicate for source files): needs ADR-0008 owner sign-off

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@orioltf
Copy link
Copy Markdown
Member Author

orioltf commented May 28, 2026

⚡ Self-Fix Report (Aggressive)

Status: COMPLETE
Pushed: ✅ Changes pushed to archon/task-fix-issue-146 (commit 0caa345)
Philosophy: Fix everything unless clearly a new concern


Fixes Applied (11 total)

Severity Count
🔴 HIGH 2
🟠 MEDIUM 7
🟢 LOW 2
View all fixes
  • CLI guard non-portable new URL() (scripts/lib/changed-file-analyser.mjs:62) — Replaced with pathToFileURL(process.argv[1]).href; added process.argv[1] && guard. Portable on Windows.
  • CLI entry point entirely untested (tests/changed-file-analyser.test.mjs) — Added describe('CLI entry point') with 5 spawnSync integration tests covering stdin→stdout pipeline.
  • Test description contradicts assertions (tests/changed-file-analyser.test.mjs:162) — Renamed to 'spawns code-reviewer and silent-failure-hunter for a single plain source file'.
  • Missing stdin 'error' event handler (scripts/lib/changed-file-analyser.mjs:65) — Added process.stdin.on('error', …) with correct error format.
  • .tsx not matched by isTypeFile (scripts/lib/changed-file-analyser.mjs:22) — Changed /\.ts$//\.tsx?$/.
  • Windows path separator coverage missing (tests/changed-file-analyser.test.mjs) — Added 5 backslash-path tests in describe('Windows backslash paths').
  • ADR-0008 future-tense Consequences (docs/adr/0008-conditional-sub-agent-spawning.md) — Updated tense; added Spawn Table documenting all 6 agent predicates.
  • CONTEXT.md missing Review Aspect + Spawn Set (CONTEXT.md) — Added both glossary entries with Avoid terms; fixed Intent Brief Relationships note.
  • AGENTS.md missing two-part checklist (AGENTS.md) — Added agents/ to Layout tree; added "Adding a new Review Aspect" subsection with mandatory 2-step checklist.
  • README missing review-pr command + stale Quick Start (README.md) — Added review-pr row to Commands table; updated Quick Start from "coming soon" language.
  • Agent files use wrong-domain JSON examples (5 agents/*.md files) — Replaced generic null-pointer example with domain-accurate examples (simplification, JSDoc rot, swallowed error, uncovered branch, type invariant).

Tests Added

  • CLI entry point (5 tests): stdin→JSON for source file; empty stdin; whitespace-only stdin; trailing newline; 6-agent mixed diff
  • Windows backslash paths (5 tests): exercising [/\\] branch in all 4 predicates

Total: 211 tests, 0 failures


Skipped (1)

Finding Reason
comment-analyzer spawn predicate misses JSDoc rot in source files New concern: extending predicate scope to all source files requires ADR-0008 owner decision — could be intentional design

Suggested follow-up: "Align comment-analyzer spawn predicate with JSDoc-in-source use case" (P2)


Validation

✅ Type check | ✅ Lint (exit 0) | ✅ Tests (211 passed)


Self-fix by Archon · aggressive mode · fixes pushed to archon/task-fix-issue-146

…formatting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

orioltf and others added 3 commits May 28, 2026 21:25
Agent frontmatter carried only description; without an explicit name,
tooling that expects it (e.g. plugin-dev validate-agent.sh) chokes and
identity relied on filename derivation. Set name to match each filename.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rose

The colour was declared in each agent's prose body where nothing reads it
— agents emit JSON and the orchestrator renders. Moving it to the frontmatter
color field puts it where the UI consumes it and removes the duplicate that
would silently drift if only one copy were updated. Persona names are kept;
they shape voice and are distinct from the kebab-case name identifier.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…stream toolkit

The agents were ported from Anthropic's pr-review-toolkit but narrowed during
the port, dropping substantive checks the upstream versions detect. Restore the
genuine detection gaps while keeping unic's JSON output and confidence rubric:

- silent-failure-hunter: log-and-continue, silent optional-chaining, unexplained
  fallback chains, silent retry exhaustion, errors that should propagate
- type-design-analyzer: anemic models, mutable internals, doc-only invariants,
  inconsistent enforcement, missing construction-boundary validation + an
  explicit invariant-discovery step in the procedure
- code-simplifier: nested-ternary check + over-simplification guardrail
- pr-test-analyzer: trivial-accessor and unseen-coverage false-positive guardrails
- comment-analyzer: contradicted perf/complexity claims + transitional-state rot

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 10 comments.

Comment thread apps/claude-code/unic-pr-review/scripts/lib/changed-file-analyser.mjs Outdated
Comment thread apps/claude-code/unic-pr-review/README.md Outdated
Comment thread apps/claude-code/unic-pr-review/docs/adr/0008-conditional-sub-agent-spawning.md Outdated
Comment thread apps/claude-code/unic-pr-review/agents/code-reviewer.md Outdated
Comment thread apps/claude-code/unic-pr-review/agents/silent-failure-hunter.md Outdated
Comment thread apps/claude-code/unic-pr-review/agents/type-design-analyzer.md Outdated
Comment thread apps/claude-code/unic-pr-review/agents/pr-test-analyzer.md Outdated
Comment thread apps/claude-code/unic-pr-review/agents/comment-analyzer.md Outdated
Comment thread apps/claude-code/unic-pr-review/agents/code-simplifier.md Outdated
orioltf and others added 7 commits May 28, 2026 23:46
.d.ts declaration files end in .ts and were matching isSourceFile,
which incorrectly spawned silent-failure-hunter and counted toward the
code-simplifier 3-source-file threshold for type-only changes. They
remain type files (isTypeFile). A literal d.ts (no dot before d) stays
a source file. Tests lock both behaviours in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The CLI split stdin on '\n' only, leaving a trailing '\r' on each line
on CRLF platforms so extension/path regexes missed matches. Extract a
pure, exported parseStdin helper that splits on /\r?\n/ and trims each
line; the CLI now uses it. decideSpawnSet API is unchanged. Unit tests
cover the helper plus a CRLF spawn integration test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The README advertised /unic-pr-review:review-pr <ADO-PR-URL> as an
AI-powered review against an Azure DevOps PR, but the command prints
"ADO mode is not yet supported in this release" for a URL and only
Pre-PR mode (no argument) works today, per ADR-0009. Update the quick
start and commands table so users are pointed at the path that actually
runs and ADO URL support is marked coming soon.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…pawn predicate

The Decision narrative claimed silent-failure-hunter spawns "only when error
handling changed", but the Spawn Table and changed-file-analyser.mjs spawn it
when at least one non-test source file changed — a path/extension heuristic
with no diff-content inspection. Reword the narrative to match reality.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ect contract

The six aspect agent prompts each said on line 11 they emit Findings 'as
a JSON array', but every file's Output format section requires a JSON
object { findings, positiveObservations }. Reviewers flagged the
mismatch as a non-parseable-output risk. Reword line 11 in all six to
point at the JSON object (the real contract).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ged-file-analyser

Buffer.concat + parseStdin ran outside the try in the CLI stdin 'end'
handler, so a synchronous throw (e.g. Buffer.concat RangeError on huge
input) escaped both the local catch and the stream 'error' listener and
surfaced as an uncaught V8 stack trace. Moving them inside the try
routes every synchronous failure through the same tagged-stderr +
exit(1) path. Happy-path output is byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
orioltf and others added 2 commits May 29, 2026 00:08
The implemented isSourceFile predicate excludes .d.ts declaration files,
but the spawn table's silent-failure-hunter and code-simplifier rows did
not mention this load-bearing behaviour. Add the clarification so the
canonical reference matches the code.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…matter

The frontmatter declares the Agent tool (authoritative for permissions),
but Step 4b prose referred to the Task tool. Reconcile the body wording
to Agent so the command is internally consistent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@orioltf orioltf merged commit 7502686 into develop May 28, 2026
9 checks passed
@orioltf orioltf deleted the archon/task-fix-issue-146 branch May 28, 2026 22:19
orioltf added a commit that referenced this pull request May 28, 2026
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>
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.

[unic-pr-review] Remaining five aspect agents + conditional spawning

2 participants