* 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
This commit is contained in:
5
.changeset/plucky-yaks-roar.md
Normal file
5
.changeset/plucky-yaks-roar.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 2451
|
||||
---
|
||||
**`/gsd-plan-review-convergence` no longer silently overrides configured reviewers with Codex** — a bare invocation (no reviewer flags) now respects `review.default_reviewers` (and, transitively, `review.reviewer_instances`) per ADR-0011/ADR-0015, instead of always injecting `--codex` and bypassing the configured default. Users without `review.default_reviewers` configured still get `--codex` as before. The startup banner now shows what will actually run.
|
||||
@@ -40,7 +40,7 @@ Replaces gsd-plan-phase's internal gsd-plan-checker with external AI reviewers (
|
||||
Phase number: extracted from $ARGUMENTS (required)
|
||||
|
||||
**Flags:**
|
||||
- `--codex` — Use Codex CLI as reviewer (default if no reviewer specified)
|
||||
- `--codex` — Use Codex CLI as reviewer (default if no reviewer flag given AND `review.default_reviewers` is unset; otherwise `review.default_reviewers` wins per ADR-0011 — #2315)
|
||||
- `--gemini` — Use Gemini CLI as reviewer
|
||||
- `--agy` / `--antigravity` — Use Antigravity CLI as reviewer (successor to the discontinued Gemini CLI)
|
||||
- `--claude` — Use Claude CLI as reviewer (separate session)
|
||||
|
||||
@@ -34,7 +34,9 @@ echo "$ARGUMENTS" | grep -q '\-\-ollama' && REVIEWER_FLAGS="$REVIEWER_FLAGS --ol
|
||||
echo "$ARGUMENTS" | grep -q '\-\-lm-studio' && REVIEWER_FLAGS="$REVIEWER_FLAGS --lm-studio"
|
||||
echo "$ARGUMENTS" | grep -q '\-\-llama-cpp' && REVIEWER_FLAGS="$REVIEWER_FLAGS --llama-cpp"
|
||||
echo "$ARGUMENTS" | grep -q '\-\-all' && REVIEWER_FLAGS="$REVIEWER_FLAGS --all"
|
||||
if [ -z "$REVIEWER_FLAGS" ]; then REVIEWER_FLAGS="--codex"; fi
|
||||
# #2315: do NOT default REVIEWER_FLAGS to --codex here. The default is resolved
|
||||
# against review.default_reviewers in step 1.5 (after the config gate) so a bare
|
||||
# invocation respects the configured reviewer lineup per ADR-0011 / ADR-0015.
|
||||
|
||||
MAX_CYCLES=$(echo "$ARGUMENTS" | grep -oE '\-\-max-cycles\s+[0-9]+' | awk '{print $2}')
|
||||
if [ -z "$MAX_CYCLES" ]; then MAX_CYCLES=3; fi
|
||||
@@ -63,6 +65,47 @@ Enable it with:
|
||||
Then re-run: /gsd:plan-review-convergence {PHASE}
|
||||
```
|
||||
|
||||
```bash
|
||||
# #2315: Resolve reviewer selection when no explicit flag was given.
|
||||
# The pre-fix bug unconditionally set REVIEWER_FLAGS="--codex" in step 1, BEFORE
|
||||
# the config gate — silently overriding any configured review.default_reviewers
|
||||
# (and, transitively, review.reviewer_instances). gsd-review sees the injected
|
||||
# --codex as an explicit flag (precedence rule 1) and never reaches rule 3
|
||||
# (review.default_reviewers). ADR-0011 and ADR-0015 both assume convergence
|
||||
# respects review.default_reviewers on the no-flag path.
|
||||
#
|
||||
# After the fix: leave REVIEWER_FLAGS empty when default_reviewers is configured
|
||||
# so gsd-review applies review.default_reviewers itself (rule 3). Only fall back
|
||||
# to --codex when no default is configured, preserving the pre-fix default for
|
||||
# unconfigured users (#2315 AC3). REVIEWER_DISPLAY mirrors the resolved value
|
||||
# so the startup banner reflects what will actually run (#2315 AC4).
|
||||
if [ -z "$REVIEWER_FLAGS" ]; then
|
||||
DEFAULT_REVIEWERS_JSON=$(gsd_run query config-get review.default_reviewers 2>/dev/null || echo "")
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
# jq is a documented production dependency (review.md:244 — "install jq if
|
||||
# missing"). If it is absent we cannot inspect the configured default, so
|
||||
# fail safe with --codex and surface the reason rather than silently
|
||||
# reproducing the #2315 override under degraded conditions.
|
||||
echo "WARNING: jq not on PATH — cannot read review.default_reviewers; falling back to --codex (#2315)" >&2
|
||||
REVIEWER_FLAGS="--codex"
|
||||
REVIEWER_DISPLAY="--codex (jq missing; cannot read review.default_reviewers)"
|
||||
else
|
||||
DEFAULT_REVIEWERS_COUNT=$(printf '%s' "$DEFAULT_REVIEWERS_JSON" | jq 'if type=="array" then length else 0 end' 2>/dev/null || echo 0)
|
||||
if [ "${DEFAULT_REVIEWERS_COUNT:-0}" -gt 0 ] 2>/dev/null; then
|
||||
: # leave REVIEWER_FLAGS empty — gsd-review applies review.default_reviewers itself
|
||||
REVIEWER_DISPLAY="review.default_reviewers ($(printf '%s' "$DEFAULT_REVIEWERS_JSON" | jq -r 'join(", ")' 2>/dev/null))"
|
||||
else
|
||||
REVIEWER_FLAGS="--codex"
|
||||
REVIEWER_DISPLAY="--codex (default; configure review.default_reviewers to change)"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# Strip the leading space accumulated by the parse block so the banner renders
|
||||
# "Reviewers: --gemini" not "Reviewers: --gemini" (#2315 review nit).
|
||||
REVIEWER_DISPLAY="${REVIEWER_FLAGS# }"
|
||||
fi
|
||||
```
|
||||
|
||||
## 2. Initialize
|
||||
|
||||
```bash
|
||||
@@ -91,7 +134,7 @@ Display startup banner:
|
||||
GSD ► PLAN CONVERGENCE — Phase {phase_number}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Reviewers: {REVIEWER_FLAGS}
|
||||
Reviewers: {REVIEWER_DISPLAY}
|
||||
Max cycles: {MAX_CYCLES}
|
||||
```
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Replaces gsd-plan-phase's internal gsd-plan-checker with external AI reviewers (
|
||||
Phase number: extracted from $ARGUMENTS (required)
|
||||
|
||||
**Flags:**
|
||||
- `--codex` — Use Codex CLI as reviewer (default if no reviewer specified)
|
||||
- `--codex` — Use Codex CLI as reviewer (default if no reviewer flag given AND `review.default_reviewers` is unset; otherwise `review.default_reviewers` wins per ADR-0011 — #2315)
|
||||
- `--gemini` — Use Gemini CLI as reviewer
|
||||
- `--agy` / `--antigravity` — Use Antigravity CLI as reviewer (successor to the discontinued Gemini CLI)
|
||||
- `--claude` — Use Claude CLI as reviewer (separate session)
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "4099ef6d0868de60",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "ab0b22244c3389aa",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "e9de7a96bbfff261",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "2d71ce5e1d327b5f",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "b06546c4bf47e521",
|
||||
"gsd-core/workflows/plant-seed.md": "6cf726be6427a35a",
|
||||
"gsd-core/workflows/pr-branch.md": "dc5598ae8accdecd",
|
||||
"gsd-core/workflows/profile-user.md": "355af92ac285567f",
|
||||
@@ -408,7 +408,7 @@
|
||||
"skills/gsd-pause-work/SKILL.md": "9d3cc6bd70b03df1",
|
||||
"skills/gsd-phase/SKILL.md": "00676bbea61410bf",
|
||||
"skills/gsd-plan-phase/SKILL.md": "03a8f71d4385a50d",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "e4d52b4473c6234b",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "79d264b132ca360d",
|
||||
"skills/gsd-pr-branch/SKILL.md": "5e050db73988f9a8",
|
||||
"skills/gsd-profile-user/SKILL.md": "10f2ff4be2e7d55f",
|
||||
"skills/gsd-progress/SKILL.md": "df0d06cf9d5963d0",
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
"commands/gsd-pause-work.md": "4fb032f72238fe33",
|
||||
"commands/gsd-phase.md": "4920d15d779329eb",
|
||||
"commands/gsd-plan-phase.md": "6b05898d574f3666",
|
||||
"commands/gsd-plan-review-convergence.md": "87def0355116452a",
|
||||
"commands/gsd-plan-review-convergence.md": "4523045c95d4cd52",
|
||||
"commands/gsd-pr-branch.md": "e168fcd545d72d0d",
|
||||
"commands/gsd-profile-user.md": "ffd9c2feb4c69f11",
|
||||
"commands/gsd-progress.md": "0b4d5b73a6c5e958",
|
||||
@@ -356,7 +356,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "b36f77ac7344a072",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "197c0590326371b2",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "49f58c3f75be3eb5",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "8b23bf9142cf7ec7",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "1c31d4905c1556c8",
|
||||
"gsd-core/workflows/plant-seed.md": "10b92ae08a6fdede",
|
||||
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
|
||||
"gsd-core/workflows/profile-user.md": "14263db831230142",
|
||||
@@ -499,7 +499,7 @@
|
||||
"skills/gsd-ns-workflow/skills/next/SKILL.md": "e7409245f1a0f9de",
|
||||
"skills/gsd-ns-workflow/skills/phase/SKILL.md": "fe5b26417ee466be",
|
||||
"skills/gsd-ns-workflow/skills/plan-phase/SKILL.md": "0dda686ce26d9171",
|
||||
"skills/gsd-ns-workflow/skills/plan-review-convergence/SKILL.md": "8181db9842a79620",
|
||||
"skills/gsd-ns-workflow/skills/plan-review-convergence/SKILL.md": "32b4344ec427afce",
|
||||
"skills/gsd-ns-workflow/skills/progress/SKILL.md": "68bda87136db9fb3",
|
||||
"skills/gsd-ns-workflow/skills/quick/SKILL.md": "014dec52d85dcb0e",
|
||||
"skills/gsd-ns-workflow/skills/spec-phase/SKILL.md": "2f88d0fc0c1abefe",
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
"commands/gsd-pause-work.md": "01dbaebfefacd252",
|
||||
"commands/gsd-phase.md": "e8c226d2694692a5",
|
||||
"commands/gsd-plan-phase.md": "7d14158eb377a248",
|
||||
"commands/gsd-plan-review-convergence.md": "c24bc978d9d743dc",
|
||||
"commands/gsd-plan-review-convergence.md": "cb6a8e074c0ab0cd",
|
||||
"commands/gsd-pr-branch.md": "382c23a6a644c0e4",
|
||||
"commands/gsd-profile-user.md": "adbc5b025b30e836",
|
||||
"commands/gsd-progress.md": "75a6dc71b74c54c2",
|
||||
@@ -355,7 +355,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "4099ef6d0868de60",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "b810f9f2374e23a5",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "e9de7a96bbfff261",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "71ba7c20f15bd5f6",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "7db990f90908a5e4",
|
||||
"gsd-core/workflows/plant-seed.md": "fbe964fcdb244802",
|
||||
"gsd-core/workflows/pr-branch.md": "513f6cff722eff2d",
|
||||
"gsd-core/workflows/profile-user.md": "3b34dcb337d50f4b",
|
||||
|
||||
@@ -284,7 +284,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "4099ef6d0868de60",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "197c0590326371b2",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "e9de7a96bbfff261",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "b80df3a60c2d3f7c",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "35fd32d839a2e4ba",
|
||||
"gsd-core/workflows/plant-seed.md": "af50e9f10d3cc6e1",
|
||||
"gsd-core/workflows/pr-branch.md": "ab157cd8e49621dd",
|
||||
"gsd-core/workflows/profile-user.md": "ff3820a27731ceb8",
|
||||
@@ -406,7 +406,7 @@
|
||||
"skills/gsd-pause-work/SKILL.md": "35e8a148e44f5361",
|
||||
"skills/gsd-phase/SKILL.md": "00be96e7ae36c6f0",
|
||||
"skills/gsd-plan-phase/SKILL.md": "f64953d1af7b0f34",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "a42e226d17b69108",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "53242a6abe90c405",
|
||||
"skills/gsd-pr-branch/SKILL.md": "c5e26f2c6dff1355",
|
||||
"skills/gsd-profile-user/SKILL.md": "894eb2850ecd2dde",
|
||||
"skills/gsd-progress/SKILL.md": "9b288501db99c7ea",
|
||||
|
||||
@@ -288,7 +288,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "b36f77ac7344a072",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "4b0a2cb0f4f28179",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "090c31e22b1508fe",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "08de1c65837a8146",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "cb03bd703057f395",
|
||||
"gsd-core/workflows/plant-seed.md": "a2cdd513663226f1",
|
||||
"gsd-core/workflows/pr-branch.md": "9923878a4f6a2d91",
|
||||
"gsd-core/workflows/profile-user.md": "26f74db0a7fcd268",
|
||||
@@ -403,7 +403,7 @@
|
||||
"skills/gsd-ns-workflow/skills/next/SKILL.md": "3856471d0f64bf09",
|
||||
"skills/gsd-ns-workflow/skills/phase/SKILL.md": "4e1363db6013a1e5",
|
||||
"skills/gsd-ns-workflow/skills/plan-phase/SKILL.md": "5796e8744314dcf5",
|
||||
"skills/gsd-ns-workflow/skills/plan-review-convergence/SKILL.md": "d44c5d4d78cbee13",
|
||||
"skills/gsd-ns-workflow/skills/plan-review-convergence/SKILL.md": "a7be67e1bd14b5fb",
|
||||
"skills/gsd-ns-workflow/skills/progress/SKILL.md": "493f467c22d55b6b",
|
||||
"skills/gsd-ns-workflow/skills/quick/SKILL.md": "605e596c680cbb1c",
|
||||
"skills/gsd-ns-workflow/skills/spec-phase/SKILL.md": "19726b83ca2c3d2d",
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
"commands/gsd-pause-work.md": "6caa75a7c2b4dd2d",
|
||||
"commands/gsd-phase.md": "e3ca4958ea20a935",
|
||||
"commands/gsd-plan-phase.md": "ad988a271bf20e9c",
|
||||
"commands/gsd-plan-review-convergence.md": "3d8837015534969f",
|
||||
"commands/gsd-plan-review-convergence.md": "da885320edff822b",
|
||||
"commands/gsd-pr-branch.md": "f5be514b9f69eaf5",
|
||||
"commands/gsd-profile-user.md": "7a9289910719d828",
|
||||
"commands/gsd-progress.md": "80f75f428fc0949d",
|
||||
@@ -356,7 +356,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "b36f77ac7344a072",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "197c0590326371b2",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "49f58c3f75be3eb5",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "8b23bf9142cf7ec7",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "1c31d4905c1556c8",
|
||||
"gsd-core/workflows/plant-seed.md": "10b92ae08a6fdede",
|
||||
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
|
||||
"gsd-core/workflows/profile-user.md": "4fa910d15dea5695",
|
||||
@@ -478,7 +478,7 @@
|
||||
"skills/gsd-pause-work/SKILL.md": "e2de20b7539e78d4",
|
||||
"skills/gsd-phase/SKILL.md": "f87211f779315ced",
|
||||
"skills/gsd-plan-phase/SKILL.md": "5f00d90224127bd5",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "bbad9a21eacdbfcf",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "1b1526f5d7a34306",
|
||||
"skills/gsd-pr-branch/SKILL.md": "87bb3306488565fb",
|
||||
"skills/gsd-profile-user/SKILL.md": "ee8e8547298006b8",
|
||||
"skills/gsd-progress/SKILL.md": "88c8229b673004e3",
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
".agents/skills/gsd-pause-work/SKILL.md": "b379469eed78a196",
|
||||
".agents/skills/gsd-phase/SKILL.md": "25477edc97a90c91",
|
||||
".agents/skills/gsd-plan-phase/SKILL.md": "0a7a22d226bcec30",
|
||||
".agents/skills/gsd-plan-review-convergence/SKILL.md": "9a7ce3391c33139d",
|
||||
".agents/skills/gsd-plan-review-convergence/SKILL.md": "c7d8a4db99e4f4b9",
|
||||
".agents/skills/gsd-pr-branch/SKILL.md": "6901da15e321913e",
|
||||
".agents/skills/gsd-profile-user/SKILL.md": "6259fabfb6afe7be",
|
||||
".agents/skills/gsd-progress/SKILL.md": "85d76286162b9189",
|
||||
@@ -391,7 +391,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "d838b87563feedf6",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "f10975692cbd036e",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "f5edc589cab52a7b",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "cfba777405d34c52",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "6f904fb1befa8991",
|
||||
"gsd-core/workflows/plant-seed.md": "5b07de07e4593281",
|
||||
"gsd-core/workflows/pr-branch.md": "d13e1cc81de40896",
|
||||
"gsd-core/workflows/profile-user.md": "05828c8cc61ef384",
|
||||
|
||||
@@ -286,7 +286,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "4099ef6d0868de60",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "bb052483744f0a6d",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "e9de7a96bbfff261",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "a258ddcf713699ee",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "0afd8eb4b0689bc9",
|
||||
"gsd-core/workflows/plant-seed.md": "21e461cd39e5181b",
|
||||
"gsd-core/workflows/pr-branch.md": "2833905f119b5722",
|
||||
"gsd-core/workflows/profile-user.md": "5cc032206c99ef71",
|
||||
@@ -381,7 +381,7 @@
|
||||
"skills/gsd-pause-work/SKILL.md": "4f0caa008a8001ff",
|
||||
"skills/gsd-phase/SKILL.md": "d38c7f9b1d0d2360",
|
||||
"skills/gsd-plan-phase/SKILL.md": "d1ba21e500bf62a8",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "d6b850077d62d723",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "29e903e25047ebd1",
|
||||
"skills/gsd-pr-branch/SKILL.md": "9cd9740db385d95a",
|
||||
"skills/gsd-profile-user/SKILL.md": "052a8e17ecda40f1",
|
||||
"skills/gsd-progress/SKILL.md": "28b94a19f2bccb78",
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
"commands/gsd-pause-work.md": "59630f05f95fff68",
|
||||
"commands/gsd-phase.md": "9a073dcd0f934f90",
|
||||
"commands/gsd-plan-phase.md": "d9bdd3726e9c3dff",
|
||||
"commands/gsd-plan-review-convergence.md": "04d89318e0e23a1b",
|
||||
"commands/gsd-plan-review-convergence.md": "ea3bec0ae494ecc8",
|
||||
"commands/gsd-pr-branch.md": "ef2eedb0ed4295da",
|
||||
"commands/gsd-profile-user.md": "0e99de36619c3b7d",
|
||||
"commands/gsd-progress.md": "44a7c784dc98f735",
|
||||
@@ -356,7 +356,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "e06ccd4d4c0703fb",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "197c0590326371b2",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "3bed01c3c906ac52",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "86e18a63ab7a0a6f",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "68139431c21ef46d",
|
||||
"gsd-core/workflows/plant-seed.md": "7bccd151ce7b69f2",
|
||||
"gsd-core/workflows/pr-branch.md": "c67d90c65da47168",
|
||||
"gsd-core/workflows/profile-user.md": "8c943983241260b5",
|
||||
@@ -456,7 +456,7 @@
|
||||
"skills/gsd-pause-work/SKILL.md": "7d310e7329b7ae7d",
|
||||
"skills/gsd-phase/SKILL.md": "49fca93e5f2ccc70",
|
||||
"skills/gsd-plan-phase/SKILL.md": "c5e1a4ab2ede399a",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "c2793cacf3f40d70",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "3e13dd1bf82f74c0",
|
||||
"skills/gsd-pr-branch/SKILL.md": "a8f8838a6aa5e1dd",
|
||||
"skills/gsd-profile-user/SKILL.md": "3be8d50399836ef2",
|
||||
"skills/gsd-progress/SKILL.md": "1245ea9e6af6ae25",
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "4099ef6d0868de60",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "9607e6d03e93c1c2",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "62f8e4f3b475fe5f",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "545fa880b6c3501f",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "d9c282754c05eb81",
|
||||
"gsd-core/workflows/plant-seed.md": "f862f77fca983749",
|
||||
"gsd-core/workflows/pr-branch.md": "ecabd55e4eabf229",
|
||||
"gsd-core/workflows/profile-user.md": "de5030437226cf2c",
|
||||
@@ -429,7 +429,7 @@
|
||||
"skills/gsd/gsd-ns-workflow/skills/next/SKILL.md": "1d30cf6061d15168",
|
||||
"skills/gsd/gsd-ns-workflow/skills/phase/SKILL.md": "610bd06d71198849",
|
||||
"skills/gsd/gsd-ns-workflow/skills/plan-phase/SKILL.md": "e564bd88707a0e69",
|
||||
"skills/gsd/gsd-ns-workflow/skills/plan-review-convergence/SKILL.md": "87e5f3a0c1f2a6af",
|
||||
"skills/gsd/gsd-ns-workflow/skills/plan-review-convergence/SKILL.md": "9f67e59967b3f5eb",
|
||||
"skills/gsd/gsd-ns-workflow/skills/progress/SKILL.md": "6ec0e4715dde7ee2",
|
||||
"skills/gsd/gsd-ns-workflow/skills/quick/SKILL.md": "65e377345e9c49a8",
|
||||
"skills/gsd/gsd-ns-workflow/skills/spec-phase/SKILL.md": "848cec32c341713c",
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
"command/gsd-pause-work.md": "04e993b1c9f8322b",
|
||||
"command/gsd-phase.md": "8f0e98dc6c223229",
|
||||
"command/gsd-plan-phase.md": "8c39408407b6bf9f",
|
||||
"command/gsd-plan-review-convergence.md": "45060e2c490fcae7",
|
||||
"command/gsd-plan-review-convergence.md": "b957f86414b6c73f",
|
||||
"command/gsd-pr-branch.md": "68e724607de3c480",
|
||||
"command/gsd-profile-user.md": "9704158b2d79cad2",
|
||||
"command/gsd-progress.md": "cba4805469307416",
|
||||
@@ -356,7 +356,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "4099ef6d0868de60",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "f10975692cbd036e",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "e9de7a96bbfff261",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "396dd0dbc4d7e405",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "232c93a9aed224fe",
|
||||
"gsd-core/workflows/plant-seed.md": "ffa5774304243649",
|
||||
"gsd-core/workflows/pr-branch.md": "ab157cd8e49621dd",
|
||||
"gsd-core/workflows/profile-user.md": "203ebe3f8f3876a8",
|
||||
@@ -480,7 +480,7 @@
|
||||
"skills/gsd-pause-work/SKILL.md": "34366b18a392a717",
|
||||
"skills/gsd-phase/SKILL.md": "64a241d4f8665aa2",
|
||||
"skills/gsd-plan-phase/SKILL.md": "c95d9376440c4725",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "fb7355db56f5eaec",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "db311b1dbb0024d1",
|
||||
"skills/gsd-pr-branch/SKILL.md": "a80da6aa95efc50d",
|
||||
"skills/gsd-profile-user/SKILL.md": "4ac2c5ea45d17a9d",
|
||||
"skills/gsd-progress/SKILL.md": "493f467c22d55b6b",
|
||||
|
||||
@@ -349,7 +349,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "b36f77ac7344a072",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "197c0590326371b2",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "49f58c3f75be3eb5",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "8b23bf9142cf7ec7",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "1c31d4905c1556c8",
|
||||
"gsd-core/workflows/plant-seed.md": "10b92ae08a6fdede",
|
||||
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
|
||||
"gsd-core/workflows/profile-user.md": "5abfae83739fa978",
|
||||
@@ -443,7 +443,7 @@
|
||||
"skills/gsd-pause-work/SKILL.md": "95017c70ae9dca0d",
|
||||
"skills/gsd-phase/SKILL.md": "31578c329cc2583e",
|
||||
"skills/gsd-plan-phase/SKILL.md": "83362394f648deeb",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "ab63cb338105d27f",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "fcac3c24e566438e",
|
||||
"skills/gsd-pr-branch/SKILL.md": "67f468db29ff2cf1",
|
||||
"skills/gsd-profile-user/SKILL.md": "19a1d3aba57f6c7c",
|
||||
"skills/gsd-progress/SKILL.md": "cc46a92b92b989ce",
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
"commands/gsd-pause-work.md": "bb5bf91a2e3e480e",
|
||||
"commands/gsd-phase.md": "6bcda1539f949d5a",
|
||||
"commands/gsd-plan-phase.md": "4e19ddc0ca22fbf6",
|
||||
"commands/gsd-plan-review-convergence.md": "88c4284aa8b3dd7e",
|
||||
"commands/gsd-plan-review-convergence.md": "348d02775fd62977",
|
||||
"commands/gsd-pr-branch.md": "31fca4f1d6c4ee62",
|
||||
"commands/gsd-profile-user.md": "725c14ae7203b5b6",
|
||||
"commands/gsd-progress.md": "001f2754c427a0a9",
|
||||
@@ -356,7 +356,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "4099ef6d0868de60",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "3a09141de7f3dedb",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "e9de7a96bbfff261",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "4d51320e4e3caddc",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "f91c297dce1253a2",
|
||||
"gsd-core/workflows/plant-seed.md": "b8dad652e31c2318",
|
||||
"gsd-core/workflows/pr-branch.md": "929b7cb0c99c7b9e",
|
||||
"gsd-core/workflows/profile-user.md": "248d59a31948e0ed",
|
||||
@@ -480,7 +480,7 @@
|
||||
"skills/gsd-pause-work/SKILL.md": "c7e9ba4f242c4648",
|
||||
"skills/gsd-phase/SKILL.md": "ee78c0c56c814d84",
|
||||
"skills/gsd-plan-phase/SKILL.md": "4b65f0a9d7d42ec7",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "78820e21dd05d5d4",
|
||||
"skills/gsd-plan-review-convergence/SKILL.md": "8338cb7e018e3713",
|
||||
"skills/gsd-pr-branch/SKILL.md": "8fa5a8fa217fe913",
|
||||
"skills/gsd-profile-user/SKILL.md": "3e6155a64523d59f",
|
||||
"skills/gsd-progress/SKILL.md": "1dfd9327510e500c",
|
||||
|
||||
2
tests/fixtures/golden-install-parity/pi.json
vendored
2
tests/fixtures/golden-install-parity/pi.json
vendored
@@ -252,7 +252,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "b36f77ac7344a072",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "197c0590326371b2",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "49f58c3f75be3eb5",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "8b23bf9142cf7ec7",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "1c31d4905c1556c8",
|
||||
"gsd-core/workflows/plant-seed.md": "10b92ae08a6fdede",
|
||||
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
|
||||
"gsd-core/workflows/profile-user.md": "1bac7f69142801ef",
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "4099ef6d0868de60",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "c22ff5ea46de665a",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "d050d8d551ed1756",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "f30e8ce168471fcd",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "7b887180677f11ac",
|
||||
"gsd-core/workflows/plant-seed.md": "0a92ba12993ac261",
|
||||
"gsd-core/workflows/pr-branch.md": "cef0f65b16d500b4",
|
||||
"gsd-core/workflows/profile-user.md": "263c0693563d98da",
|
||||
@@ -428,7 +428,7 @@
|
||||
"skills/gsd-ns-workflow/skills/next/SKILL.md": "13e394affe675498",
|
||||
"skills/gsd-ns-workflow/skills/phase/SKILL.md": "1ed640e5f06c7be6",
|
||||
"skills/gsd-ns-workflow/skills/plan-phase/SKILL.md": "ff46b5450c9c4313",
|
||||
"skills/gsd-ns-workflow/skills/plan-review-convergence/SKILL.md": "a42e226d17b69108",
|
||||
"skills/gsd-ns-workflow/skills/plan-review-convergence/SKILL.md": "53242a6abe90c405",
|
||||
"skills/gsd-ns-workflow/skills/progress/SKILL.md": "943538c4ac6bde19",
|
||||
"skills/gsd-ns-workflow/skills/quick/SKILL.md": "bd5e4cb79bc41611",
|
||||
"skills/gsd-ns-workflow/skills/spec-phase/SKILL.md": "7e4dfa2070b7d9d1",
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "e06ccd4d4c0703fb",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "778b73a8db6f7c32",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "619946c879f33b9d",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "7a527920ad606595",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "610d816af89b1b16",
|
||||
"gsd-core/workflows/plant-seed.md": "856ad565b2eb0c47",
|
||||
"gsd-core/workflows/pr-branch.md": "79fd55b88ea2db9c",
|
||||
"gsd-core/workflows/profile-user.md": "672821e6b1266645",
|
||||
@@ -400,7 +400,7 @@
|
||||
"skills/gsd-ns-workflow/skills/next/SKILL.md": "faadd9e2817e7324",
|
||||
"skills/gsd-ns-workflow/skills/phase/SKILL.md": "df3efcd61f7cc796",
|
||||
"skills/gsd-ns-workflow/skills/plan-phase/SKILL.md": "31962f06bc124c89",
|
||||
"skills/gsd-ns-workflow/skills/plan-review-convergence/SKILL.md": "5c4475320977818f",
|
||||
"skills/gsd-ns-workflow/skills/plan-review-convergence/SKILL.md": "b27aad9196476875",
|
||||
"skills/gsd-ns-workflow/skills/progress/SKILL.md": "37a37d2cdfba46ea",
|
||||
"skills/gsd-ns-workflow/skills/quick/SKILL.md": "6fd1b96274b23a9f",
|
||||
"skills/gsd-ns-workflow/skills/spec-phase/SKILL.md": "9291f835233fa8da",
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "e06ccd4d4c0703fb",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "80b1ba493a9a967f",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "3fed4740a91d0443",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "a7fde3dbf83628cc",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "27dcce3dbca22e3d",
|
||||
"gsd-core/workflows/plant-seed.md": "6cf61f540cdbd8b9",
|
||||
"gsd-core/workflows/pr-branch.md": "acd59f915d018ad4",
|
||||
"gsd-core/workflows/profile-user.md": "c4313672b81b5bcd",
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
"commands/gsd-pause-work.md": "40a953fcddbedb5d",
|
||||
"commands/gsd-phase.md": "5dd3d40e3461a973",
|
||||
"commands/gsd-plan-phase.md": "d6ba79bf0ede5bcd",
|
||||
"commands/gsd-plan-review-convergence.md": "551597f2add981cb",
|
||||
"commands/gsd-plan-review-convergence.md": "f3aab422e0708dae",
|
||||
"commands/gsd-pr-branch.md": "ab1fcffe92129061",
|
||||
"commands/gsd-profile-user.md": "37c9ef202669bfd3",
|
||||
"commands/gsd-progress.md": "13d7e5ba8acc2605",
|
||||
@@ -356,7 +356,7 @@
|
||||
"gsd-core/workflows/plan-phase/steps/closed-phase-gate.md": "b36f77ac7344a072",
|
||||
"gsd-core/workflows/plan-phase/steps/prd-express-path.md": "197c0590326371b2",
|
||||
"gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md": "49f58c3f75be3eb5",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "8b23bf9142cf7ec7",
|
||||
"gsd-core/workflows/plan-review-convergence.md": "1c31d4905c1556c8",
|
||||
"gsd-core/workflows/plant-seed.md": "10b92ae08a6fdede",
|
||||
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
|
||||
"gsd-core/workflows/profile-user.md": "e23bea0a69c0bb4b",
|
||||
@@ -471,7 +471,7 @@
|
||||
"skills/gsd-ns-workflow/skills/next/SKILL.md": "63659cd48a0276f9",
|
||||
"skills/gsd-ns-workflow/skills/phase/SKILL.md": "b377b03d13db8573",
|
||||
"skills/gsd-ns-workflow/skills/plan-phase/SKILL.md": "4564f415845d6c15",
|
||||
"skills/gsd-ns-workflow/skills/plan-review-convergence/SKILL.md": "12d1696eaaf60043",
|
||||
"skills/gsd-ns-workflow/skills/plan-review-convergence/SKILL.md": "84af9aa192ec349f",
|
||||
"skills/gsd-ns-workflow/skills/progress/SKILL.md": "fb24eab4a0e5f6a3",
|
||||
"skills/gsd-ns-workflow/skills/quick/SKILL.md": "48629fef2b9ac5b0",
|
||||
"skills/gsd-ns-workflow/skills/spec-phase/SKILL.md": "052cfb21eb0ee3d4",
|
||||
|
||||
@@ -31,6 +31,7 @@ const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execFileSync } = require('node:child_process');
|
||||
|
||||
const COMMAND_PATH = path.join(__dirname, '..', 'commands', 'gsd', 'plan-review-convergence.md');
|
||||
const WORKFLOW_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'plan-review-convergence.md');
|
||||
@@ -39,6 +40,15 @@ const PLAN_PHASE_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'pla
|
||||
const PLANNER_REVIEWS_PATH = path.join(__dirname, '..', 'gsd-core', 'references', 'planner-reviews.md');
|
||||
const PLAN_CHECKER_PATH = path.join(__dirname, '..', 'agents', 'gsd-plan-checker.md');
|
||||
|
||||
// #2315: the workflow's reviewer-resolution block pipes through `jq`, which is
|
||||
// a documented production dependency (review.md:244 "install jq if missing")
|
||||
// but is NOT present in every test container (gsd-test's linux-node{22,24}
|
||||
// images lack it; see tests/opencode-review-reconstruction.property.test.cjs
|
||||
// for the same skip pattern). Behavioral tests that exercise the deployed jq
|
||||
// pipeline skip when jq is absent; structural tests still run.
|
||||
let jqAvailable = false;
|
||||
try { execFileSync('jq', ['--version'], { stdio: 'ignore', timeout: 10000, killSignal: 'SIGKILL' }); jqAvailable = true; } catch { /* no jq on PATH */ }
|
||||
|
||||
// ─── Command source ────────────────────────────────────────────────────────
|
||||
|
||||
describe('plan-review-convergence command source (#2306)', () => {
|
||||
@@ -123,12 +133,16 @@ describe('plan-review-convergence command source (#2306)', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('--codex is the default reviewer when no flag is specified', () => {
|
||||
test('--codex is the default reviewer when no flag is given AND review.default_reviewers is unset (#2315)', () => {
|
||||
// #2315: a bare invocation now respects review.default_reviewers per
|
||||
// ADR-0011. The command must document that --codex is the default ONLY
|
||||
// when review.default_reviewers is unset; otherwise the configured default
|
||||
// wins. The pre-fix claim ("default if no reviewer specified") was the
|
||||
// user-facing mirror of the #2315 bug.
|
||||
assert.ok(
|
||||
command.includes('default if no reviewer specified') ||
|
||||
command.includes('default: --codex') ||
|
||||
command.includes('(default if no reviewer specified)'),
|
||||
'--codex must be documented as the default reviewer'
|
||||
command.includes('default if no reviewer flag given') &&
|
||||
command.includes('review.default_reviewers'),
|
||||
'command must document that --codex is the default ONLY when review.default_reviewers is unset (#2315)'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -159,17 +173,25 @@ describe('plan-review-convergence: --agy/--antigravity reviewer whitelist (#2293
|
||||
assert.ok(skill.includes('--agy'), 'generated SKILL.md must document --agy (regenerate via gen:plugin-skills)');
|
||||
});
|
||||
|
||||
// Behavioral: execute the ACTUAL deployed REVIEWER_FLAGS accumulation block and
|
||||
// assert --antigravity passes through instead of being dropped to the --codex
|
||||
// default. POSIX-only — the block is /bin/sh-style grep pipework; skip on Windows
|
||||
// where a bash shim is not guaranteed on PATH.
|
||||
test('[behavioral] the deployed extraction passes --antigravity through (not the --codex fallback)', (t) => {
|
||||
// Behavioral: execute the ACTUAL deployed REVIEWER_FLAGS parse block and
|
||||
// assert --antigravity passes through instead of being dropped. POSIX-only —
|
||||
// the block is /bin/sh-style grep pipework; skip on Windows where a bash
|
||||
// shim is not guaranteed on PATH.
|
||||
//
|
||||
// #2315: the parse block no longer applies a --codex default. The endMarker
|
||||
// was previously the unconditional `if [ -z "$REVIEWER_FLAGS" ]; then
|
||||
// REVIEWER_FLAGS="--codex"; fi` line; that line was the #2315 bug and is now
|
||||
// gone. Default resolution moved to step 1.5 after the config gate (see the
|
||||
// #2315 describe block below). The bare-invocation assertion now expects an
|
||||
// empty REVIEWER_FLAGS from the parse block — the default is applied later,
|
||||
// respecting review.default_reviewers.
|
||||
test('[behavioral] the deployed parse block passes --antigravity through (parse block no longer applies a --codex default — #2315)', (t) => {
|
||||
if (process.platform === 'win32') { t.skip('POSIX shell extraction; not run on Windows'); return; }
|
||||
const { execFileSync } = require('node:child_process');
|
||||
const startIdx = workflow.indexOf('REVIEWER_FLAGS=""');
|
||||
const endMarker = 'if [ -z "$REVIEWER_FLAGS" ]; then REVIEWER_FLAGS="--codex"; fi';
|
||||
const endMarker = "echo \"$ARGUMENTS\" | grep -q '\\-\\-all' && REVIEWER_FLAGS=\"$REVIEWER_FLAGS --all\"";
|
||||
const endIdx = workflow.indexOf(endMarker);
|
||||
assert.ok(startIdx !== -1 && endIdx !== -1, 'the REVIEWER_FLAGS accumulation block must exist in the workflow');
|
||||
assert.ok(startIdx !== -1 && endIdx !== -1, 'the REVIEWER_FLAGS parse block must exist in the workflow');
|
||||
const block = workflow.slice(startIdx, endIdx + endMarker.length) + '\nprintf "%s" "$REVIEWER_FLAGS"';
|
||||
const run = (args) => execFileSync('bash', ['-c', block], {
|
||||
env: { ...process.env, ARGUMENTS: args },
|
||||
@@ -179,12 +201,15 @@ describe('plan-review-convergence: --agy/--antigravity reviewer whitelist (#2293
|
||||
|
||||
const agy = run('5 --antigravity');
|
||||
assert.ok(agy.split(/\s+/).includes('--antigravity'), `--antigravity must pass through, got: "${agy}"`);
|
||||
assert.notStrictEqual(agy, '--codex', 'must NOT collapse to the --codex-only fallback when --antigravity is given');
|
||||
// The parse block must NOT inject --codex when an explicit flag is present.
|
||||
assert.notStrictEqual(agy, '--codex', 'must NOT produce --codex-only when --antigravity is given');
|
||||
|
||||
assert.ok(run('5 --agy').split(/\s+/).includes('--agy'), '--agy short form must pass through');
|
||||
|
||||
// Regressions: default + existing flags unchanged.
|
||||
assert.strictEqual(run('5'), '--codex', 'no reviewer flag → --codex default preserved');
|
||||
// #2315 regression: the parse block no longer applies a default. The bare
|
||||
// invocation (no flag) MUST yield an empty REVIEWER_FLAGS here; the default
|
||||
// is resolved later in step 1.5 against review.default_reviewers.
|
||||
assert.strictEqual(run('5'), '', 'no reviewer flag → empty REVIEWER_FLAGS from parse (default applied in step 1.5 per #2315)');
|
||||
const mixed = run('5 --codex --gemini');
|
||||
assert.ok(mixed.includes('--codex') && mixed.includes('--gemini'), 'existing flags still recognized');
|
||||
// --agy must not be spuriously matched by an unrelated flag (independence).
|
||||
@@ -192,6 +217,235 @@ describe('plan-review-convergence: --agy/--antigravity reviewer whitelist (#2293
|
||||
});
|
||||
});
|
||||
|
||||
// ─── #2315: bare invocation respects review.default_reviewers ──────────────
|
||||
|
||||
describe('plan-review-convergence: #2315 respects review.default_reviewers (no-flag default)', () => {
|
||||
const workflow = fs.readFileSync(WORKFLOW_PATH, 'utf8');
|
||||
const command = fs.readFileSync(COMMAND_PATH, 'utf8');
|
||||
const SKILL_PATH = path.join(__dirname, '..', 'skills', 'gsd-plan-review-convergence', 'SKILL.md');
|
||||
const skill = fs.readFileSync(SKILL_PATH, 'utf8');
|
||||
|
||||
// Pre-fix #2315: the workflow unconditionally set REVIEWER_FLAGS="--codex" in
|
||||
// step 1 (line 37) BEFORE the workflow.plan_review_convergence config gate.
|
||||
// gsd-review sees the injected --codex as an explicit flag (precedence rule 1)
|
||||
// and never reaches rule 3 (review.default_reviewers), silently overriding
|
||||
// any configured default — a violation of ADR-0011 and ADR-0015.
|
||||
//
|
||||
// The buggy one-liner must not appear ANYWHERE in the workflow — the post-fix
|
||||
// resolution lives in step 1.5 as a config-gated if/else/fi block, never as
|
||||
// the bare one-liner. (Earlier versions of this test only asserted the line
|
||||
// was not BEFORE the gate, which still allowed a re-introduction after the
|
||||
// gate; the assertion is now unconditional per review.)
|
||||
test('workflow does NOT contain the unconditional REVIEWER_FLAGS=--codex one-liner anywhere', () => {
|
||||
const buggyLine = 'if [ -z "$REVIEWER_FLAGS" ]; then REVIEWER_FLAGS="--codex"; fi';
|
||||
assert.strictEqual(
|
||||
workflow.indexOf(buggyLine),
|
||||
-1,
|
||||
'the unconditional --codex one-liner must not appear anywhere in the workflow (#2315); ' +
|
||||
'default resolution is conditional on review.default_reviewers in step 1.5 (if/else/fi block).'
|
||||
);
|
||||
});
|
||||
|
||||
test('workflow resolves REVIEWER_FLAGS against review.default_reviewers AFTER the config gate', () => {
|
||||
const resolutionIdx = workflow.indexOf('gsd_run query config-get review.default_reviewers');
|
||||
const configGateIdx = workflow.indexOf('CONVERGENCE_ENABLED=$(gsd_run query config-get workflow.plan_review_convergence');
|
||||
assert.ok(resolutionIdx !== -1, 'workflow must query review.default_reviewers to resolve the no-flag default (#2315)');
|
||||
assert.ok(
|
||||
resolutionIdx > configGateIdx,
|
||||
'review.default_reviewers resolution must come AFTER the config gate (only runs when convergence is enabled)'
|
||||
);
|
||||
});
|
||||
|
||||
test('workflow documents that empty REVIEWER_FLAGS lets gsd-review apply review.default_reviewers', () => {
|
||||
// After the fix, an empty REVIEWER_FLAGS is INTENTIONAL — it signals "let
|
||||
// gsd-review apply its own precedence (rule 3: review.default_reviewers)".
|
||||
// A comment must document this so a future maintainer does not re-add the
|
||||
// unconditional --codex fallback and resurrect the #2315 bug.
|
||||
assert.ok(
|
||||
/gsd-review applies.*review\.default_reviewers|review\.default_reviewers.*gsd-review applies/i.test(workflow),
|
||||
'workflow must document that empty REVIEWER_FLAGS lets gsd-review apply review.default_reviewers (prevent #2315 regression)'
|
||||
);
|
||||
});
|
||||
|
||||
test('startup banner uses REVIEWER_DISPLAY (not raw REVIEWER_FLAGS) so users see what will actually run', () => {
|
||||
// AC4 of #2315: banner must reflect actual reviewers, not a hardcoded value.
|
||||
// When REVIEWER_FLAGS is empty (because default_reviewers is configured),
|
||||
// the banner must show the resolved default — not an empty string and not
|
||||
// a misleading "--codex". Assert the literal banner placeholder so a
|
||||
// maintainer cannot satisfy this by defining REVIEWER_DISPLAY in a comment
|
||||
// while leaving the banner pointing at REVIEWER_FLAGS.
|
||||
assert.ok(
|
||||
workflow.includes('Reviewers: {REVIEWER_DISPLAY}'),
|
||||
'startup banner must use the {REVIEWER_DISPLAY} placeholder, not {REVIEWER_FLAGS} (#2315 AC4)'
|
||||
);
|
||||
assert.ok(
|
||||
!/\bReviewers:\s*\{REVIEWER_FLAGS\}/.test(workflow),
|
||||
'startup banner must NOT reference {REVIEWER_FLAGS} directly (#2315 AC4)'
|
||||
);
|
||||
});
|
||||
|
||||
test('command and skill doc both document the review.default_reviewers precedence (content parity)', () => {
|
||||
// The #2315 fix updated the --codex flag description in BOTH the command
|
||||
// (commands/gsd/plan-review-convergence.md) and the generated skill mirror
|
||||
// (skills/gsd-plan-review-convergence/SKILL.md). The two are kept in sync
|
||||
// by `npm run gen:plugin-skills`; this assertion catches a manual edit to
|
||||
// one that the other doesn't mirror. Both must mention review.default_reviewers
|
||||
// alongside the --codex default claim.
|
||||
const expected = 'review.default_reviewers';
|
||||
assert.ok(
|
||||
command.includes(expected),
|
||||
`command file must document the review.default_reviewers precedence on the --codex flag (#2315)`
|
||||
);
|
||||
assert.ok(
|
||||
skill.includes(expected),
|
||||
`skill file must mirror the command file's review.default_reviewers precedence (#2315)`
|
||||
);
|
||||
});
|
||||
|
||||
// Behavioral: extract the parse block + the post-config-gate resolution block
|
||||
// and execute them with a stubbed gsd_run. Proves the matrix:
|
||||
// - bare + default_reviewers configured → empty REVIEWER_FLAGS (gsd-review applies default)
|
||||
// - bare + default_reviewers unset → --codex fallback (pre-fix behavior preserved)
|
||||
// - bare + empty-array default → --codex fallback (defensive — schema would reject)
|
||||
// - explicit --gemini + default set → --gemini wins (explicit flags unaffected, #2315 AC5)
|
||||
test('[behavioral] no-flag invocation resolves to default_reviewers when configured, --codex otherwise', (t) => {
|
||||
if (process.platform === 'win32') { t.skip('POSIX shell extraction; not run on Windows'); return; }
|
||||
if (!jqAvailable) { t.skip('jq not on PATH — workflow resolution block pipes through jq (production dependency, review.md:244); structural tests above still validate the fix'); return; }
|
||||
const { execFileSync } = require('node:child_process');
|
||||
|
||||
// Parse block: from REVIEWER_FLAGS="" to the last grep line (--all).
|
||||
const parseStart = workflow.indexOf('REVIEWER_FLAGS=""');
|
||||
const parseEndMarker = "echo \"$ARGUMENTS\" | grep -q '\\-\\-all' && REVIEWER_FLAGS=\"$REVIEWER_FLAGS --all\"";
|
||||
const parseEnd = workflow.indexOf(parseEndMarker);
|
||||
assert.ok(parseStart !== -1 && parseEnd !== -1, 'parse block must exist');
|
||||
const parseBlock = workflow.slice(parseStart, parseEnd + parseEndMarker.length);
|
||||
|
||||
// Resolution block: the `if [ -z "$REVIEWER_FLAGS" ]; then` that appears
|
||||
// AFTER the config gate (CONVERGENCE_ENABLED=). This is the #2315 fix.
|
||||
// Extract to the closing fence of the enclosing ```bash block — the block
|
||||
// contains a nested if/else/fi, so a naive "first \nfi\n" match would stop
|
||||
// at the inner fi and yield unbalanced bash.
|
||||
const configGateIdx = workflow.indexOf('CONVERGENCE_ENABLED=$(gsd_run query config-get workflow.plan_review_convergence');
|
||||
const resolutionStart = workflow.indexOf('if [ -z "$REVIEWER_FLAGS" ]; then', configGateIdx);
|
||||
assert.ok(resolutionStart !== -1, 'post-config-gate REVIEWER_FLAGS resolution block must exist (#2315)');
|
||||
const closingFence = workflow.indexOf('\n```\n', resolutionStart);
|
||||
assert.ok(closingFence !== -1, 'could not locate closing fence of resolution block');
|
||||
const resolutionBlock = workflow.slice(resolutionStart, closingFence);
|
||||
|
||||
const run = ({ args, defaultReviewers }) => {
|
||||
// Stub gsd_run: only `query config-get review.default_reviewers` is exercised.
|
||||
// Empty/default → unset key (gsd_run returns nothing → empty stdout).
|
||||
//
|
||||
// The default_reviewers value is passed via env var ($GSD_TEST_DEFAULT_REVIEWERS)
|
||||
// rather than inline-interpolated into the bash script. This avoids a quoting
|
||||
// fragility: a future test input containing a single quote would otherwise
|
||||
// break the bash single-quoted string and execute as bash. With env-var
|
||||
// handoff, the value never crosses an interpreting shell context.
|
||||
const stub = `gsd_run() { case "$*" in *"config-get review.default_reviewers"*) printf '%s' "$GSD_TEST_DEFAULT_REVIEWERS";; *) return 0;; esac; }`;
|
||||
const script = `${stub}\n${parseBlock}\n${resolutionBlock}\nprintf 'REVIEWER_FLAGS=[%s] REVIEWER_DISPLAY=[%s]' "$REVIEWER_FLAGS" "$REVIEWER_DISPLAY"`;
|
||||
return execFileSync('bash', ['-c', script], {
|
||||
env: { ...process.env, ARGUMENTS: args, GSD_TEST_DEFAULT_REVIEWERS: defaultReviewers ?? '' },
|
||||
encoding: 'utf8',
|
||||
timeout: 5000,
|
||||
});
|
||||
};
|
||||
|
||||
// AC1: bare invocation with default_reviewers configured → empty REVIEWER_FLAGS
|
||||
// (gsd-review applies configured default per its rule 3) and banner shows the resolved default.
|
||||
let r = run({ args: '5', defaultReviewers: '["gemini","claude"]' });
|
||||
assert.ok(/REVIEWER_FLAGS=\[\s*\]/.test(r), `configured default → REVIEWER_FLAGS empty, got: "${r}"`);
|
||||
assert.ok(/review\.default_reviewers \(gemini, claude\)/.test(r), `banner shows configured default, got: "${r}"`);
|
||||
|
||||
// AC2: reviewer instances participate via default_reviewers — same path.
|
||||
r = run({ args: '5', defaultReviewers: '["opencode-deepseek","opencode-mimo"]' });
|
||||
assert.ok(/REVIEWER_FLAGS=\[\s*\]/.test(r), `instance default → REVIEWER_FLAGS empty, got: "${r}"`);
|
||||
|
||||
// AC3: bare invocation with default_reviewers unset → --codex fallback preserved.
|
||||
r = run({ args: '5', defaultReviewers: '' });
|
||||
assert.ok(/REVIEWER_FLAGS=\[--codex\]/.test(r), `unset default → --codex fallback, got: "${r}"`);
|
||||
|
||||
// AC3 defensive: empty-array default → --codex fallback (schema rejects this, but be safe).
|
||||
r = run({ args: '5', defaultReviewers: '[]' });
|
||||
assert.ok(/REVIEWER_FLAGS=\[--codex\]/.test(r), `empty-array default → --codex fallback, got: "${r}"`);
|
||||
|
||||
// AC5 (out of scope but must not regress): explicit --gemini overrides configured default.
|
||||
r = run({ args: '5 --gemini', defaultReviewers: '["claude"]' });
|
||||
assert.ok(/REVIEWER_FLAGS=\[.*--gemini.*\]/.test(r), `explicit flag wins over configured default, got: "${r}"`);
|
||||
});
|
||||
|
||||
// Property test — CLAUDE.md mandates at least one fast-check (fc) property
|
||||
// test for parsers and bijective contracts. The resolution block parses the
|
||||
// configured review.default_reviewers JSON and classifies it into one of two
|
||||
// outcomes: "non-empty array → delegate to gsd-review" (REVIEWER_FLAGS empty)
|
||||
// or "anything else → fall back to --codex" (defensive — schema rejects most
|
||||
// of these at config-set time, but corruption/edge cases must not crash or
|
||||
// misclassify). Locks the contract so a future change can't subtly narrow or
|
||||
// widen the accepted shape.
|
||||
const fc = require('fast-check');
|
||||
test('[property] resolution classifies arbitrary JSON values: non-empty array → empty flags, anything else → --codex fallback', (t) => {
|
||||
if (process.platform === 'win32') { t.skip('POSIX shell extraction; not run on Windows'); return; }
|
||||
if (!jqAvailable) { t.skip('jq not on PATH — workflow resolution block pipes through jq; structural tests above still validate the fix'); return; }
|
||||
|
||||
// Re-extract the blocks (the test above proved extraction works; we re-use
|
||||
// the same logic rather than promoting to a helper to keep the test scope local).
|
||||
const parseStart = workflow.indexOf('REVIEWER_FLAGS=""');
|
||||
const parseEndMarker = "echo \"$ARGUMENTS\" | grep -q '\\-\\-all' && REVIEWER_FLAGS=\"$REVIEWER_FLAGS --all\"";
|
||||
const parseEnd = workflow.indexOf(parseEndMarker);
|
||||
const parseBlock = workflow.slice(parseStart, parseEnd + parseEndMarker.length);
|
||||
const configGateIdx = workflow.indexOf('CONVERGENCE_ENABLED=$(gsd_run query config-get workflow.plan_review_convergence');
|
||||
const resolutionStart = workflow.indexOf('if [ -z "$REVIEWER_FLAGS" ]; then', configGateIdx);
|
||||
const closingFence = workflow.indexOf('\n```\n', resolutionStart);
|
||||
const resolutionBlock = workflow.slice(resolutionStart, closingFence);
|
||||
const { execFileSync } = require('node:child_process');
|
||||
const run = ({ args, defaultReviewers }) => {
|
||||
const stub = `gsd_run() { case "$*" in *"config-get review.default_reviewers"*) printf '%s' "$GSD_TEST_DEFAULT_REVIEWERS";; *) return 0;; esac; }`;
|
||||
const script = `${stub}\n${parseBlock}\n${resolutionBlock}\nprintf 'REVIEWER_FLAGS=[%s] REVIEWER_DISPLAY=[%s]' "$REVIEWER_FLAGS" "$REVIEWER_DISPLAY"`;
|
||||
return execFileSync('bash', ['-c', script], {
|
||||
env: { ...process.env, ARGUMENTS: args, GSD_TEST_DEFAULT_REVIEWERS: defaultReviewers ?? '' },
|
||||
encoding: 'utf8',
|
||||
timeout: 5000,
|
||||
});
|
||||
};
|
||||
|
||||
// Slug pattern mirrors the schema (ADR-0011: ^[a-zA-Z0-9_-]+$).
|
||||
const slug = fc.stringMatching(/^[a-zA-Z0-9_-]{1,8}$/);
|
||||
// Non-empty arrays of slugs → MUST classify as "use default" (REVIEWER_FLAGS empty).
|
||||
const nonEmptyArray = fc.array(slug, { minLength: 1, maxLength: 4 }).map((a) => JSON.stringify(a));
|
||||
// Defensive-corpus: empty array, scalar JSON, malformed JSON. All MUST fall
|
||||
// back to --codex. The schema rejects the first two at config-set time, but
|
||||
// a corruption/typo landing in the file directly would still reach this code.
|
||||
const emptyArray = fc.constant('[]');
|
||||
const scalarJson = fc.oneof(
|
||||
fc.string({ maxLength: 8 }).filter((s) => !s.includes('"')).map((s) => JSON.stringify(s)),
|
||||
fc.integer({ min: -10, max: 10 }).map((n) => JSON.stringify(n)),
|
||||
fc.constant('null'),
|
||||
fc.constant('true'),
|
||||
fc.constant('false')
|
||||
);
|
||||
const malformedJson = fc.oneof(
|
||||
fc.constant('[unclosed'),
|
||||
fc.constant('{bad json'),
|
||||
fc.constant('not json at all'),
|
||||
fc.constant('{"k":'),
|
||||
fc.string({ maxLength: 12 }).filter((s) => {
|
||||
try { JSON.parse(s); return false; } catch { return true; }
|
||||
})
|
||||
);
|
||||
|
||||
// Property A: any non-empty array of slugs → empty REVIEWER_FLAGS.
|
||||
fc.assert(
|
||||
fc.property(nonEmptyArray, (dr) => /REVIEWER_FLAGS=\[\s*\]/.test(run({ args: '5', defaultReviewers: dr }))),
|
||||
{ numRuns: 25 }
|
||||
);
|
||||
|
||||
// Property B: anything in the defensive corpus → --codex fallback.
|
||||
fc.assert(
|
||||
fc.property(fc.oneof(emptyArray, scalarJson, malformedJson), (dr) => /REVIEWER_FLAGS=\[--codex\]/.test(run({ args: '5', defaultReviewers: dr }))),
|
||||
{ numRuns: 25 }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Workflow: initialization ──────────────────────────────────────────────
|
||||
|
||||
describe('plan-review-convergence workflow: initialization (#2306)', () => {
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"pause-work.md": 14441,
|
||||
"plan-milestone-gaps.md": 11809,
|
||||
"plan-phase.md": 94391,
|
||||
"plan-review-convergence.md": 23713,
|
||||
"plan-review-convergence.md": 26285,
|
||||
"plant-seed.md": 11785,
|
||||
"pr-branch.md": 15963,
|
||||
"profile-user.md": 21246,
|
||||
|
||||
Reference in New Issue
Block a user