Commit Graph

4 Commits

Author SHA1 Message Date
Tom Boucher
7372d99a26 enhance(#2800): derive reviewer flag lists and gate reviewer lane docs across locales (#2882)
* chore(#2800): derive reviewer flag lists and gate reviewer lane docs across locales

The reviewer lane roster was hand-enumerated across five documentation
surfaces and three workflow files that had drifted apart: --kimi-code was
missing from all four translated COMMANDS.md mirrors, --coderabbit from
every workflow forwarding list, and --antigravity from FEATURES.md.

Adds checkReviewerDocsParity, a second pure gate deliberately separate from
checkReviewerLaneParity so a stale doc cannot make the runtime checker look
red. Workflows now derive their flag lists from a new review-lane flags
query instead of hand-enumerating them, which also retires the unanchored
grep that matched --agy inside --antigravity.

Documents the previously absent reviewer body and hostBehaviors field in
the capability manifest reference.

Closes #2800
Closes #2781
Closes #2272

* fix(#2800): key the docs parity table arm on first-cell position

Review found the flag arm was file-scoped, so the forwarding row that lists
every flag in its third cell satisfied it on its own. Deleting a lane's own
reviewer-table row -- the #2781 regression this gate exists to prevent --
therefore passed undetected.

Arm 4 keys on the FIRST table cell, which separates a lane row from the
forwarding row structurally and in every locale. Regression test included.

* fix(#2800): shape-filter the flags subcommand output

All three consumers read review-lane flags through an unquoted command
substitution so the output word-splits into loop items. Phase 2 admits
third-party overlay lanes, so an overlay flag containing whitespace would
inject a second loop item and one containing a glob would expand against
the cwd. Emit only well-formed flags so neither reaches the shell.

* fix(#2800): remove the regex length ceiling and count only prose mentions

Review found two real defects in the docs parity gate.

The never-throws contract was false: building a RegExp from a declared flag
or section title throws SyntaxError past ~100k chars, and Phase 2 admits
overlay lanes whose declared strings are untrusted in length. Every one of
these matches is literal, so String.includes replaces the regex outright,
which also deletes escapeLiteral and the llama.cpp escaping it existed for.

Arm 1 was context-blind: a flag mentioned only inside a fenced example or a
commented-out row counted as documented. Both are stripped before matching.

Also advertises all 13 lane flags in the argument-hint and corrects a stale
eleven-lane count in the slug grammar note.

* test(#2800): repoint the convergence suite off deleted workflow text

The derived flag loop deleted the literal per-flag grep lines four tests
matched on. Two of those failed loudly. The behavioral and property tests
failed SILENTLY instead: their end marker no longer resolved, so the parse
block extracted empty and both passed vacuously, and the property test's
gsd_run stub had a no-op default that hid it.

All now share one extractor and execute the real deployed block through a
gsd_run shim backed by the actual binary. The whitelist assertions become an
anti-parity check: re-adding a hand-written flag list must fail.

Also repairs two vacuous cases in the docs parity suite. The unreadable-doc
test called its own mock rather than the reader, and the integration test
bounded nothing, so a doc losing its marker would have been silently skipped
and still passed green.

* fix(#2800): run the derived flag loop after the launcher preamble

The remote matrix caught a real runtime bug, not a test artifact. In
autonomous.md and plan-review-convergence.md the launcher preamble that
defines gsd_run lives in a separate, LATER bash fence than the derived loop.
Each fence is its own shell, so gsd_run was undefined where the loop ran:
the command substitution yielded nothing and zero reviewer flags would have
been forwarded. Worse than the drift this epic fixes, and silent.

The whole CONVERGENCE_ARGS construction moves as one unit, because the
--max-cycles append sits between the loop and the preamble and would
otherwise have run against an uninitialized variable and then been dropped
by the relocated initializer.

Also documents all 13 lane flags in help/modes/full.md, which the repo gates
bidirectionally against each command's argument-hint.

* test(#2800): repoint the two converge suites off deleted flag literals

Both asserted workflow.includes('--codex') against the hand-enumerated list
the derived loop removed. They now assert the derivation itself, keep --all
and --text (convergence controls, still literal), and add an anti-parity
guard so re-adding a hardcoded list fails.

The lost pass-through proof is replaced with a real one: every flag the
tests used to hardcode is asserted present in the actual roster emitted by
the binary, which is the property the old assertion was protecting.

* test(#2800): acknowledge the workflow byte growth from the derived flag loop

* chore(#2800): backfill changeset pr number to 2882

* fix(#2800): strip HTML comments to a fixed point in the parity gate

CodeQL js/incomplete-multi-character-sanitization (high) on PR #2882: the
single-pass <!--...--> strip can leave a live <!-- behind, so a join-trick
construction smuggles a commented-out row past the gate and it counts as
documented. Not an injection risk here since nothing is rendered, but it is
the exact false pass this helper exists to prevent.

Strips to a fixed point, then treats any surviving opener as unterminated so
the multi-line branch closes it on a later line. Terminates because every
pass strictly shortens the string.

* test(#2800): pin the comment-smuggling regression with a real reproducer

The obvious fixture for this class does not reproduce it: <!--<!---->-->
leaves a dangling --> rather than a live <!--, and is caught either way, so
it would have passed with and without the fix. The join-trick construction
(<!- + <!--DUMMY--> + -...-->), the <scr<script>ipt> shape, genuinely
regresses on the single-pass strip and is what the test now uses.

---------

Co-authored-by: Test <test@example.com>
2026-07-30 19:14:13 -04:00
Tom Boucher
352876ff0c fix(#2315): respect review.default_reviewers in bare convergence invocation (#2451)
* test(#2315): regression test for review.default_reviewers precedence

A bare /gsd-plan-review-convergence invocation (no reviewer flags) is supposed
to let users configure a persistent reviewer lineup via review.default_reviewers
and just run the loop. Instead, the orchestrator's argument parser silently
discards that configuration and forces --codex on every no-flag invocation —
with no warning that the configured reviewers were ignored.

This commit adds a regression test that fails against the pre-fix workflow
(the buggy unconditional --codex fallback is still present at this commit)
and passes after the fix lands:

- Structural: the workflow must NOT contain an unconditional
  'if [ -z "$REVIEWER_FLAGS" ]; then REVIEWER_FLAGS="--codex"; fi' line
  before the workflow.plan_review_convergence config gate.
- Structural: the workflow must query review.default_reviewers AFTER the config
  gate and document that empty REVIEWER_FLAGS lets gsd-review apply the default.
- Behavioral: matrix across {configured, unset, empty-array, explicit-flag}
  invoking the actual deployed parse + resolution blocks with a stubbed gsd_run.

Also updates two existing tests whose assertions the fix makes stale:
- #2293 behavioral: endMarker was the buggy unconditional fallback line; the
  bare invocation assertion was 'run("5") === "--codex"'. Both flip post-fix
  (endMarker is now the last --all grep line; bare invocation returns empty
  from the parse block, default applied later in step 1.5).
- command-default-claim: the pre-fix command documented '--codex (default if
  no reviewer specified)' which was the user-facing mirror of the bug. The
  assertion now requires the command to document the review.default_reviewers
  precedence.

* fix(#2315): respect review.default_reviewers in bare convergence invocation

Root cause: plan-review-convergence.md step 1 (Parse and Normalize Arguments)
contained an unconditional fallback that set REVIEWER_FLAGS=\"--codex\" whenever
no explicit reviewer flag was supplied. This value was then interpolated
verbatim into the gsd-review args, so gsd-review saw --codex as an explicit
flag (precedence rule 1) and never reached rule 3 (review.default_reviewers).
The same path silently dropped any configured review.reviewer_instances
(instances participate ONLY via review.default_reviewers per ADR-1517).

Fix:
- Remove the unconditional --codex fallback from step 1.
- Add a config-gated resolution in step 1.5 (after CONVERGENCE_ENABLED check)
  that queries review.default_reviewers and either leaves REVIEWER_FLAGS empty
  (letting gsd-review apply its own rule-3 default) or falls back to --codex
  when no default is configured — preserving the pre-fix default for
  unconfigured users (AC3).
- Replace the banner {REVIEWER_FLAGS} token with {REVIEWER_DISPLAY} so the
  startup banner reflects what will actually run (AC4), not a hardcoded value.

The fix upholds the documented precedence contract (ADR-0011, ADR-0015) that
the bug was actively violating. Explicit-flag invocations (--gemini, --all,
etc.) are unaffected (AC5).

References: #2315; ADR-0011 (review.default_reviewers precedence);
ADR-0015 (autonomous cross-AI convergence); ADR-1517 (reviewer instances).

* chore(#2315): bump plan-review-convergence.md baseline + changeset

- Bump plan-review-convergence.md size baseline 23713 → 25536 (the new
  step-1.5 default-resolution block).
- Add .changeset/plucky-yaks-roar.md documenting the user-visible change.

* fix(#2315): restore /gsd: colon syntax + skip behavioral test when jq missing

Two follow-ups to the #2315 fix discovered by gsd-test:

1. The fix commit accidentally regressed the slash-command namespace in the
   disabled-feature exit message: /gsd:plan-review-convergence (correct,
   from PR #3452) became /gsd-plan-review-convergence (retired dash syntax).
   The slash-command-namespace invariant test caught this. Restored the
   colon form.

2. The behavioral test exercises the deployed reviewer-resolution block,
   which pipes through jq. jq is a documented production dependency
   (review.md:244 "install jq if missing") and is present in every
   production deployment, but is NOT on PATH in the gsd-test linux-node{22,24}
   containers (same constraint as tests/opencode-review-reconstruction.
   property.test.cjs). Without jq, the printf|jq pipeline fails silently,
   the ||echo 0 fallback yields DEFAULT_REVIEWERS_COUNT=0, and the resolution
   falls through to the --codex branch — producing a false negative.
   Added a jqAvailable guard at module load (matching the existing pattern)
   and skip the behavioral test when jq is absent. The structural tests
   (no bash execution) still run and validate the fix.

* chore(#2315): regenerate golden-install-parity fixtures

Source changes to plan-review-convergence.md (workflow + skill mirror +
command doc) changed the install-tree hashes. Regenerated via
'npm run gen:golden' after rebuilding gsd-core/bin/lib/install-engine.cjs
('npm run build:lib') — the on-disk lib was stale relative to
src/install-engine.cts (isSymlinkedDestOptIn) and blocked fixture gen.

* test(#2315): address review findings — strengthen structural tests + property test

Code-review + security-review (isolated subagent passes) surfaced Low/Nit
findings; this commit addresses the test-side findings:

- Structural test 1 ("unconditional --codex one-liner") now asserts the
  buggy line is absent EVERYWHERE, not just before the config gate. The
  earlier assertion allowed a maintainer to re-add the line after the gate
  (passing the structural test) while the bug would still bite at runtime
  before the gate runs.
- Structural test 4 ("banner uses REVIEWER_DISPLAY") now asserts the
  LITERAL banner placeholder "Reviewers: {REVIEWER_DISPLAY}" and forbids
  "Reviewers: {REVIEWER_FLAGS}". The earlier workflow.includes(
  "REVIEWER_DISPLAY") was satisfied by a comment mention.
- New structural test for command/skill content parity: both files must
  document the review.default_reviewers precedence on the --codex flag
  (catches a manual edit to one that the gen:plugin-skills mirror misses).
- Behavioral test stub now passes default_reviewers via env var
  ($GSD_TEST_DEFAULT_REVIEWERS) instead of inline-interpolating into a
  bash single-quoted string. Removes the (currently-safe) fragility where
  a future test input containing a single quote would close the bash
  quote and execute as bash under execFileSync.
- New property test (fast-check, numRuns=25) for the JSON-classification
  contract: non-empty arrays of slugs -> empty REVIEWER_FLAGS; empty
  array / scalar JSON / malformed JSON -> --codex fallback. Locks the
  parser contract per CLAUDE.md mandate.

* fix(#2315): address review findings — defensive jq-missing warning + banner cleanup

Code-review surfaced two Low-severity workflow-side findings:

- Defensive jq-missing warning: if jq is not on PATH in production (it is
  a documented dependency per review.md:244, but the dependency can be
  absent in degraded environments), the printf|jq pipeline fails silently
  to "0" and a user with review.default_reviewers configured gets --codex
  with no indication their configured default was unreadable. Added a
  command -v jq guard at the top of the resolution that falls back to
  --codex AND emits a stderr warning explaining the reason. This makes the
  failure diagnosable instead of silently reproducing the #2315 override.

- Banner leading-space cleanup: REVIEWER_FLAGS accumulates with a leading
  space ("$REVIEWER_FLAGS --gemini" from ""), so the explicit-flag branch
  of REVIEWER_DISPLAY="$REVIEWER_FLAGS" rendered "Reviewers:  --gemini"
  (double space). Pre-existing but worth fixing alongside the AC4 banner
  work. Strip one leading space with the ${VAR# } parameter expansion in
  the explicit-flag branch only (the configured-default and --codex
  branches already produce clean strings).

* chore(#2315): bump plan-review-convergence.md baseline + regen golden fixtures

The defensive jq-missing warning grew plan-review-convergence.md
(25536 -> 26285 bytes). Bumps the per-file baseline snapshot and
regenerates the golden-install-parity fixtures for the resulting
install-tree hash changes.

* chore(#2315): backfill pr:2451 in .changeset/plucky-yaks-roar.md
2026-07-20 10:55:28 -04:00
Tom Boucher
1bb724048a fix(#2293): recognize --agy/--antigravity in plan-review-convergence whitelist (#2325)
* fix(#2293): recognize --agy/--antigravity in plan-review-convergence whitelist

The convergence reviewer-flag whitelist predated the 1.7.0 Antigravity CLI
adapter and silently dropped --agy/--antigravity, so convergence fell back to
--codex only and the working adapter was unreachable (worse after Gemini CLI's
upstream shutdown). Add both flags to the workflow grep whitelist, the command
argument-hint + flag docs, and the regenerated SKILL.md; they pass through to
/gsd-review unchanged. --gemini behavior is untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(#2293): backfill PR number 2325 into changeset

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 01:55:45 -04:00
Tom Boucher
da4a86d8c1 feat(#1596): ship GSD skills via .claude-plugin/plugin.json
Phase B-provide of epic #1258. Adds a build-generated skills/ dir +
a skills manifest field so plugin-installed GSD exposes gsd-core:<skill>
the native Claude Code way. Closes the gap where plugin-only installs
lacked the skill surface because bin/install.js never ran.

- scripts/gen-plugin-skills.cjs: build step converting commands/gsd/*.md
  to skills/gsd-<stem>/SKILL.md via convertClaudeCommandToClaudeSkill
- .claude-plugin/plugin.json: add "skills": "./skills/"
- package.json: add skills to files, gen:plugin-skills to build chain
- tests/issue-766-plugin-manifest.test.cjs: Section H conformance
  (manifest field + dir + frontmatter + count parity) + C2 skills symlink
- docs/adr/766-*.md: dated amendment adding skills surface row
- .changeset/rapid-bears-hum.md: type Added
- skills/: 69 generated gsd-<stem>/SKILL.md files (build-committed)

Closes #1596
2026-06-22 18:07:35 -04:00