* chore(#2994): fragmentize progress.md forensic audit onto the fragment model Extract the --forensic-gated forensic_audit step to workflows/progress/steps/forensic-audit.md behind a section marker, and repair progress.md's init line to forward --forensic so the atom is actually true in production rather than only under direct CLI tests. progress.md shrinks 32630 -> 27207 bytes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(#2994): fragmentize the four manifest-wired workflows new-project, quick, new-milestone and progress each already had a dedicated cmdInit* entry point but zero marked sections. Extract nine gated bodies to workflows/<wf>/steps/ behind section markers and repair each init line to forward its flags. Fold --full into the discuss/research/validate facts inside cmdInitQuick so the when= grammar never sees an OR, per the chunked-mode precedent. Fixes found while working, per the no-defer rule: - cmdInitProgress passed no phase info to buildSectionManifestField, so state:phase-mvp-mode was permanently false — an atom in the vocabulary whose fact could never be computed. - the quick init router folded flag tokens into the free-text description, which the new forwarding would have corrupted. - a #2508 dispatch note was nested inside quick.md's Agent(prompt=) fence, leaking orchestrator guidance into the subagent prompt. - progress.md had a 3-vs-4 backtick outer-fence imbalance. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(#2994): fragmentize verify-work.md and admit state:ui-phase-active Wire cmdInitVerifyWork to buildSectionManifestField — it was a dedicated entry point that never emitted a manifest — and mark two sections. state:ui-phase-active folds (plan:pre hooks include an active ui step) OR (the phase dir holds a *-UI-SPEC.md) into one boolean in init.cts, so the grammar still sees a single operator-free atom. The inner Playwright-MCP check stays as prose inside the fragment: it is live session state and no init seam can precompute it. The MVP false-branch note is a real fallback, not redundant prose, so it sits outside the marker — gating it away would delete the text needed precisely when MVP mode is off. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(#2994): follow moved workflow content in drift guards Retarget every guard that asserted on content this branch moved into workflows/<wf>/steps/, mirroring 815b3d897. Each retargeted assertion was verified to still fail when its step file is blanked, so none was weakened into vacuity. Three assertions in verify-mvp-uat were genuinely red. Three more were worse than red — passing for the wrong reason: - quick-commit-boundary and worktree-cleanup anchored on indexOf('Step 5.6'), which matched a later cross-reference and sliced 16069 chars that coincidentally held the asserted substrings. Replaced with an expandWorkflowSections helper that splices step content back in place. - phase6-review-capabilities lost its end boundary and widened to EOF. - playwright-ui-verify matched 'UI' in an unrelated bullet and 'fall back' in a subagent-dispatch line after the real content moved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(#2994): fragmentize code-review and complete-milestone, admit three atoms Add dedicated cmdInitCodeReview and cmdInitCompleteMilestone entry points alongside the shared generic ones rather than modifying them — init.phase-op and init.manager carry a CRITICAL blast radius (179 dependents, 24 processes) and stay byte-identical for their other callers. Admit flag:--fix, state:fallow-enabled and state:git-create-tag, each with a consuming section and a fact its own entry point computes. Both sections had the resolver-in-body hazard: the fallow config-gate and the git.create_tag check each sat inside the very block being gated, so gating would have disabled the resolver that decides the gate. Both are hoisted into init and the bodies now consume the resolved fact. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(#2994): retarget code-review and milestone drift guards, fix two red tests Retarget guards that asserted on content moved into steps/, proving non-vacuity by blanking each step file and confirming failure. Also fixes two genuinely red tests found while working, per the no-defer rule: - workflow-fragments' frozen-vocabulary lock was missing state:ui-phase-active, so commit 7ef7f8336 shipped red. Lint and build both passed over it, which is why neither is sufficient verification. - code-review's quick.md capability-hook assertion carried a stale delimiter after the 18ff35d20 extraction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(#2994): fragmentize autonomous.md and admit state:plan-strategy-converge Five sections share one atom, the pattern plan-phase already uses for flag:--research-phase. The atom folds --converge OR --cross-ai into a single boolean in cmdInitAutonomous so the grammar stays operator-free. cmdInitAutonomous is additive; init.milestone-op, init.manager and init.phase-op are untouched and still consumed. The $PLAN_STRATEGY bash resolver is deliberately retained — ungated local-planning bullets still read it, so the init-side fact supplements it rather than replacing it. converge-fail-fast required splitting one bash fence so the always-run CONVERGENCE_ARGS construction stays outside the marker. All three flag-absent fallbacks were left outside their markers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(#2994): fragmentize review and discuss-phase-assumptions Admit state:reviewer-instances-configured (two peripheral notes share it; the core reviewer-lane dispatch stays unmarked — it is the workflow's primary always-evaluated logic, not an optional branch) and state:auto-advance-active, which folds --auto OR two config keys into one boolean so the grammar stays operator-free. discuss-phase-assumptions was the highest-risk edit in this PR. Its auto_advance step is a full if/elif/else; gating it whole would have deleted the flag-absent fallback needed exactly when --auto is off. Split verified exact: resolvers 636-651 and the 'End here' fallback 668-669 both stay outside the marker; only 653-667 is gated. Adds emitted-drift acks for the two files that grew — review.md (+55 B) and autonomous.md (+737 B from 80799211c, which had none and would have red-gated the push. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(#2994): fragmentize docs-update, update, transition and new-milestone Part A Completes the 13-workflow rollout. Three of these had no init call at all and gained a dedicated entry point plus their first gsd_run query line. Admits state:is-monorepo and adds state:next-channel, state:workstream-active and state:flat-mode. Vocabulary 26 -> 30 atoms. Part A of new-milestone applies when NO workstream is active — the negation of state:workstream-active. Rather than teach the grammar negation, which is the Greenspun drift the frozen list exists to prevent, it gets a separate positively-phrased atom whose fact is the inverse. Part B, which always runs, stays outside the marker. flag:--verify-only is deliberately NOT admitted: docs-update has no contiguous purely-additive region for it, and an atom without a consuming section is dead vocabulary. Evidence recorded in the slice report. update.md reuses its existing resolved $GSD_TOOLS rather than prepending the canonical preamble, which would have clobbered it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(#2994): stop automated-ui-verification re-resolving its own gate, retire dead vocabulary Two defects the new tests caught. The automated-ui-verification step re-ran gsd_run loop render-hooks and recomputed UI_PHASE_ACTIVE inside a body that is only read when that fact is already true — the circular self-disabling pattern this design forbids, introduced by 3c654b168. cmdInitVerifyWork now exposes ui_phase_active and the step consumes it. Its launcher preamble goes too: no gsd_run remains. The Playwright-MCP check stays as prose — that is live session state. Dead vocabulary predating this PR: flag:--full and state:needs-codebase-map were admitted with a gate-1 claim that never materialized. flag:--full is removed, redundant once quick folds it into discuss/research/validate. state:needs-codebase-map gets the real consumer it always lacked, gating new-project's codebase-map offer. Vocabulary 30 -> 29, and no atom is now without a consuming section. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(#2994): add the atom-admission, inversion and resolver-hoist gates The two existing parity guards prove vocabulary/predicate symmetry but never that a fact is computed — an atom no cmdInit* assembles evaluates false forever. These close that hole: - per-atom satisfiability for all 29 atoms, plus an anti-vacuity assertion so the loop cannot silently cover zero atoms - dead-vocabulary check against the shipped manifest - inversion guard: the flag-absent fallbacks in discuss-phase-assumptions and verify-work must stay outside their markers - data-driven resolver-hoist guard over the shipped manifest, so a future extraction cannot reintroduce the circular class - compound-fold coverage (--full, --cross-ai, --rc, config-only --auto) - null-vs-[] degraded/computed distinction, and flag value shapes Also repairs the frozen-vocabulary lock, which was stale and red for the seven atoms earlier commits on this branch shipped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(#2994): add changeset for the fragment-model rollout Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(#2994): cite the issue on the two new allow-test-rule exemptions ADR-456 requires an issue ref on the same line as the annotation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(#2994): correct the atom-count claims after retiring flag:--full The vocabulary doc comments still said 30 entries; it is 29 since flag:--full was removed as dead vocabulary. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(#2994): dedupe the phase-fallback block and harden --ws parsing Review findings. MAJOR: the three new init entry points each pasted a verbatim copy of the guardedFindPhase/guardedGetRoadmapPhase fallback, taking the repo from four copies to seven — DEFECT.GENERATIVE-FIX. Extracted applyRoadmapFallback and folded six of the seven; each call site keeps its own field-set via a closure. Duplication removed rather than papered over with a parity test. cmdInitPhaseOp stays out: its fallback omits has_reviews, so it is not a byte-identical copy, and it is CRITICAL-radius. LOW, pre-existing: GSD_WS captured [^[:space:]]+ and expands unquoted, so a workstream name holding glob metacharacters would expand against the filesystem. Narrowed to [A-Za-z0-9._-]+. The unquoted expansion is kept — it must word-split into two args and vanish when empty. Also restores the vocabulary ordering convention, and fixes a masked test bug the mandated run surfaced: the flag-forwarding guard checked only the first init line per workflow, but new-milestone has two, so a real failure was reporting exit 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(#2994): drop the stale new-milestone emitted-drift ack new-milestone.md was acked for a +406 B growth measured against an intermediate commit. Net against origin/next it SHRANK by 8 bytes, so nothing needed the ack and it explained nothing — which the differential attribution check reports as a stale acknowledgment, not a pass. update.md's entry stays: it genuinely grew +703 B. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(#2994): resolve the 15 failures from the full matrix run All 15 were real and identical on both lanes. REAL REGRESSION: autonomous.md hit 41479 chars against the #2196 guard's 40960 cap — a CHARS cap distinct from the LARGE tier byte cap, which the five section stubs pushed it over. Extracted the 3a.5 UI Design Contract body to references/; now 39968 chars, and the file nets -795 B vs base, so its growth ack is deleted rather than left stale. REAL DEFECT: docs referenced /gsd-transition, which is not a live registered command. Reworded. STALE FIXTURE: the emission byte-identity test hardcoded two marked workflows; this branch legitimately marks fifteen. Fixture corrected — the source was right. The rest were drift guards over the eight workflows the earlier sweep did not cover, retargeted at where the content now lives with non-vacuity proven by blanking each step file and confirming failure. The GSD_WS forwarding guard was checked as a possible real break and is not one: the charclass narrowing is intact and forwarding works end to end. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(#2994): drop the ack for a newly-added reference file A new file's emitted ripple is attributable to the diff that adds it, so the acknowledgment explained nothing and the differential check reports it as stale. Removing the last entry removes the fragment — an empty one signals nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(#2994): retarget the UI-contract guards and clear two transitive advisories The §3a.5 extraction that brought autonomous.md under the #2196 char cap moved its body to references/autonomous-ui-design-contract.md, so ten guards in autonomous-ui-steps and check-ui-safety-gate were asserting it against the host. Retargeted via a combined read, each proven non-vacuous by blanking the reference file and confirming failure. This class had already bitten twice on this branch because each sweep was scoped to the workflows touched at that moment, so this one was exhaustive: ~70 test files across all 13 workflows, zero further broken or vacuous assertions found. Also clears two high transitive advisories the matrix flagged on one lane — fast-uri GHSA-7p8r-x3mc-p8w7 and three ip-address SSRF/trust-boundary issues. Both pre-date this branch: package-lock.json was untouched until now, so the production tree was byte-identical to the base. Lockfile-only, package.json unchanged, verified against a real npm ci install. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(#2994): backfill changeset pr number to 3030 --------- Co-authored-by: sim <sim@local> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
5
.changeset/proud-otters-wave.md
Normal file
5
.changeset/proud-otters-wave.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
type: Changed
|
||||||
|
pr: 3030
|
||||||
|
---
|
||||||
|
**Workflow guidance now loads only the branch your invocation actually took** — thirteen more large workflows moved onto the fragment model, so running `/gsd-code-review` without `--fix` no longer loads the fix-dispatch branch, `/gsd-progress` without `--forensic` no longer loads the forensic audit, and so on across every migrated workflow. (#2994)
|
||||||
@@ -501,10 +501,19 @@ node gsd-tools.cjs init quick <description>
|
|||||||
node gsd-tools.cjs init resume
|
node gsd-tools.cjs init resume
|
||||||
node gsd-tools.cjs init verify-work <phase>
|
node gsd-tools.cjs init verify-work <phase>
|
||||||
node gsd-tools.cjs init phase-op <phase>
|
node gsd-tools.cjs init phase-op <phase>
|
||||||
|
node gsd-tools.cjs init code-review <phase> [--fix]
|
||||||
|
node gsd-tools.cjs init review <phase>
|
||||||
|
node gsd-tools.cjs init discuss-phase-assumptions <phase> [--auto]
|
||||||
node gsd-tools.cjs init todos [area]
|
node gsd-tools.cjs init todos [area]
|
||||||
node gsd-tools.cjs init milestone-op
|
node gsd-tools.cjs init milestone-op
|
||||||
node gsd-tools.cjs init map-codebase
|
node gsd-tools.cjs init map-codebase
|
||||||
node gsd-tools.cjs init progress
|
node gsd-tools.cjs init progress
|
||||||
|
node gsd-tools.cjs init manager
|
||||||
|
node gsd-tools.cjs init complete-milestone
|
||||||
|
node gsd-tools.cjs init autonomous [--converge] [--cross-ai]
|
||||||
|
node gsd-tools.cjs init docs-update
|
||||||
|
node gsd-tools.cjs init update [--next] [--rc]
|
||||||
|
node gsd-tools.cjs init transition
|
||||||
|
|
||||||
# Workstream-scoped init (`--ws` flag)
|
# Workstream-scoped init (`--ws` flag)
|
||||||
node gsd-tools.cjs init execute-phase <phase> --ws <name>
|
node gsd-tools.cjs init execute-phase <phase> --ws <name>
|
||||||
|
|||||||
@@ -210,6 +210,7 @@
|
|||||||
"api-coverage.md",
|
"api-coverage.md",
|
||||||
"artifact-types.md",
|
"artifact-types.md",
|
||||||
"autonomous-smart-discuss.md",
|
"autonomous-smart-discuss.md",
|
||||||
|
"autonomous-ui-design-contract.md",
|
||||||
"checkpoints.md",
|
"checkpoints.md",
|
||||||
"common-bug-patterns.md",
|
"common-bug-patterns.md",
|
||||||
"context-budget.md",
|
"context-budget.md",
|
||||||
|
|||||||
@@ -46,9 +46,22 @@ gap fragment and composes back byte-identical to its source.
|
|||||||
|
|
||||||
## The frozen `when=` vocabulary
|
## The frozen `when=` vocabulary
|
||||||
|
|
||||||
`when=` takes exactly one of 19 atoms (widened from 4 to 14 via the ADR-1671
|
`when=` takes exactly one of 29 atoms (widened from 4 to 14 via the ADR-1671
|
||||||
amendment for #2992, epic #1671 Phase 6.1, then from 14 to 19 via the
|
amendment for #2992, epic #1671 Phase 6.1, then from 14 to 19 via the
|
||||||
ADR-1671 amendment for #2993, epic #1671 Phase 6.2):
|
ADR-1671 amendment for #2993, epic #1671 Phase 6.2, then from 19 to 20 via
|
||||||
|
the ADR-1671 amendment for #2994, epic #1671 Phase 6.3, then from 20 to 23
|
||||||
|
via a further #2994 amendment fragmentizing `code-review.md` and
|
||||||
|
`complete-milestone.md`, then from 23 to 24 via a still further #2994
|
||||||
|
amendment fragmentizing `autonomous.md`, then from 24 to 26 via a still
|
||||||
|
further #2994 amendment fragmentizing `review.md` and
|
||||||
|
`discuss-phase-assumptions.md`, then from 26 to 30 via the FINAL #2994
|
||||||
|
amendment fragmentizing `docs-update.md`, `update.md`, `transition.md`, and
|
||||||
|
`new-milestone.md` — the last four of the 13 workflows targeted by
|
||||||
|
ADR-1671 — then from 30 to 29 via a dead-vocabulary cleanup: `flag:--full`
|
||||||
|
was removed (no `when=` marker ever consumed it — `quick.md` folds `--full`
|
||||||
|
into the `--discuss`/`--research`/`--validate` facts before evaluation) and
|
||||||
|
`state:needs-codebase-map` gained its first real consumer,
|
||||||
|
`new-project.md`'s `codebase-map-offer` section):
|
||||||
|
|
||||||
| Value | Meaning |
|
| Value | Meaning |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -58,8 +71,8 @@ ADR-1671 amendment for #2993, epic #1671 Phase 6.2):
|
|||||||
| `state:has-prior-phases` | Applicable when prior phases (and their `VERIFICATION.md` files) exist. |
|
| `state:has-prior-phases` | Applicable when prior phases (and their `VERIFICATION.md` files) exist. |
|
||||||
| `flag:--auto` | Applicable when the workflow runs with `--auto`. |
|
| `flag:--auto` | Applicable when the workflow runs with `--auto`. |
|
||||||
| `flag:--discuss` | Applicable when the workflow runs with `--discuss`. |
|
| `flag:--discuss` | Applicable when the workflow runs with `--discuss`. |
|
||||||
|
| `flag:--fix` | Applicable when the workflow runs with `--fix` (`code-review.md`'s resolved fix decision — `--fix` itself, or `--all`/`--auto` implying it via `code-review-flags.cjs`). |
|
||||||
| `flag:--forensic` | Applicable when the workflow runs with `--forensic`. |
|
| `flag:--forensic` | Applicable when the workflow runs with `--forensic`. |
|
||||||
| `flag:--full` | Applicable when the workflow runs with `--full`. |
|
|
||||||
| `flag:--ingest` | Applicable when the workflow runs with `--ingest <path-or-glob>`. |
|
| `flag:--ingest` | Applicable when the workflow runs with `--ingest <path-or-glob>`. |
|
||||||
| `flag:--prd` | Applicable when the workflow runs with `--prd <file>`. |
|
| `flag:--prd` | Applicable when the workflow runs with `--prd <file>`. |
|
||||||
| `flag:--research` | Applicable when the workflow runs with `--research`. |
|
| `flag:--research` | Applicable when the workflow runs with `--research`. |
|
||||||
@@ -67,9 +80,19 @@ ADR-1671 amendment for #2993, epic #1671 Phase 6.2):
|
|||||||
| `flag:--reset-phase-numbers` | Applicable when the workflow runs with `--reset-phase-numbers`. |
|
| `flag:--reset-phase-numbers` | Applicable when the workflow runs with `--reset-phase-numbers`. |
|
||||||
| `flag:--reviews` | Applicable when the workflow runs with `--reviews`. |
|
| `flag:--reviews` | Applicable when the workflow runs with `--reviews`. |
|
||||||
| `flag:--validate` | Applicable when the workflow runs with `--validate`. |
|
| `flag:--validate` | Applicable when the workflow runs with `--validate`. |
|
||||||
|
| `state:auto-advance-active` | Applicable when `discuss-phase-assumptions.md`'s `auto_advance` step should dispatch — `--auto` flag OR a consolidated auto-mode config fact (see [Compound conditions are resolved in the fact, never the grammar](#compound-conditions-are-resolved-in-the-fact-never-the-grammar) below). |
|
||||||
| `state:chunked-mode` | Applicable when chunked planning mode is active — see [Compound conditions are resolved in the fact, never the grammar](#compound-conditions-are-resolved-in-the-fact-never-the-grammar) below. |
|
| `state:chunked-mode` | Applicable when chunked planning mode is active — see [Compound conditions are resolved in the fact, never the grammar](#compound-conditions-are-resolved-in-the-fact-never-the-grammar) below. |
|
||||||
|
| `state:fallow-enabled` | Applicable when `.planning/config.json`'s `code_quality.fallow.enabled` is `true` (fail-closed default `false`). |
|
||||||
|
| `state:flat-mode` | Applicable when NO workstream is active — the positively-phrased inverse of `state:workstream-active` (the grammar has no negation operator). |
|
||||||
|
| `state:git-create-tag` | Applicable when `.planning/config.json`'s `git.create_tag` is not `false` (fail-OPEN default `true`). |
|
||||||
|
| `state:is-monorepo` | Applicable when the project's `monorepo_workspaces` list is non-empty. |
|
||||||
| `state:needs-codebase-map` | Applicable when a codebase map is needed (init-computed). |
|
| `state:needs-codebase-map` | Applicable when a codebase map is needed (init-computed). |
|
||||||
|
| `state:next-channel` | Applicable when `update.md`'s release channel is `next` — the workflow runs with `--next`, or its documented alias `--rc`. |
|
||||||
| `state:phase-mvp-mode` | Applicable when the current phase's `ROADMAP.md` entry declares `**Mode:** mvp`. |
|
| `state:phase-mvp-mode` | Applicable when the current phase's `ROADMAP.md` entry declares `**Mode:** mvp`. |
|
||||||
|
| `state:plan-strategy-converge` | Applicable when `autonomous.md`'s planning step should route through plan-review convergence instead of `gsd-plan-phase` — the workflow runs with `--converge`, or its documented alias `--cross-ai` (`autonomous.md`'s own `PLAN_STRATEGY` resolver folds both). |
|
||||||
|
| `state:reviewer-instances-configured` | Applicable when `.planning/config.json`'s `review.reviewer_instances` is present AND non-empty. |
|
||||||
|
| `state:ui-phase-active` | Applicable when the phase's active `plan:pre` loop hooks include the `ui-phase` step, OR the phase directory already contains a `*-UI-SPEC.md` file — see [Compound conditions are resolved in the fact, never the grammar](#compound-conditions-are-resolved-in-the-fact-never-the-grammar) below. |
|
||||||
|
| `state:workstream-active` | Applicable when a workstream is active — `GSD_WORKSTREAM` env, falling back to the stored active-workstream pointer. |
|
||||||
| `state:worktrees-enabled` | Applicable when `.planning/config.json`'s `workflow.use_worktrees` is enabled. |
|
| `state:worktrees-enabled` | Applicable when `.planning/config.json`'s `workflow.use_worktrees` is enabled. |
|
||||||
|
|
||||||
This list is **closed by design** (Greenspun's Tenth Rule): left open-ended,
|
This list is **closed by design** (Greenspun's Tenth Rule): left open-ended,
|
||||||
@@ -97,13 +120,44 @@ required:
|
|||||||
section marked with it would silently never include: the exact
|
section marked with it would silently never include: the exact
|
||||||
silent-wrong-answer class this gate exists to prevent.
|
silent-wrong-answer class this gate exists to prevent.
|
||||||
|
|
||||||
Six further atoms (`flag:--converge`, `flag:--fix`, `flag:--verify-only`,
|
One further atom, `flag:--verify-only`, is surveyed but NOT admitted even now
|
||||||
`state:fallow-enabled`, `state:git-create-tag`, `state:is-monorepo`) satisfy
|
that `docs-update` has its own dedicated `cmdInit*` entry point
|
||||||
gate 1 but not yet gate 2 — their workflows (`autonomous`, `code-review`,
|
(`cmdInitDocsUpdate`) — it fails gate 1, not gate 2: the flag's control flow
|
||||||
`complete-milestone`, `docs-update`) route through shared generic init entry
|
is INTERLEAVED across three non-contiguous touch-points in `docs-update.md`
|
||||||
points invoked by 20+ other workflows, so a dedicated `cmdInit*` seam does
|
(an inline early-exit check in `init_context`, a "Skip condition" note
|
||||||
not yet exist to compute their facts. They are withheld pending that seam,
|
embedded in another step's body, and the `verify_only_report` step itself)
|
||||||
not rejected.
|
rather than a single contiguous, whole-line, purely-additive region.
|
||||||
|
Admitting the atom to gate only the `verify_only_report` step would leave
|
||||||
|
the other two touch-points as un-migrated raw `$ARGUMENTS` checks — an atom
|
||||||
|
whose consuming section covers only PART of the flag's real behavior is not
|
||||||
|
a clean win. `state:is-monorepo` IS admitted (`dispatch-monorepo-packages`
|
||||||
|
section, `cmdInitDocsUpdate`) — see [Piloted on execute-phase.md, then rolled
|
||||||
|
out across the wired
|
||||||
|
workflows](#piloted-on-execute-phasemd-then-rolled-out-across-the-wired-workflows)
|
||||||
|
below. `flag:--fix`, `state:fallow-enabled`, and
|
||||||
|
`state:git-create-tag` were withheld for the same reason until a further
|
||||||
|
#2994 amendment gave `code-review` and `complete-milestone` their own
|
||||||
|
dedicated `cmdInit*` entry points (`cmdInitCodeReview`,
|
||||||
|
`cmdInitCompleteMilestone`) — see [Piloted on execute-phase.md, then rolled
|
||||||
|
out across the wired
|
||||||
|
workflows](#piloted-on-execute-phasemd-then-rolled-out-across-the-wired-workflows)
|
||||||
|
below. A third atom, originally surveyed as `flag:--converge`, was withheld
|
||||||
|
for the same reason and never shipped under that name: a still further
|
||||||
|
#2994 amendment gave `autonomous` its own dedicated `cmdInit*` entry point
|
||||||
|
(`cmdInitAutonomous`), and the atom that shipped is
|
||||||
|
`state:plan-strategy-converge` instead — `--cross-ai` is a documented alias
|
||||||
|
for `--converge` (`autonomous.md`'s own `PLAN_STRATEGY` resolver folds
|
||||||
|
both), so a `flag:--converge`-only atom would have left a `--cross-ai`-only
|
||||||
|
invocation silently excluded from the same sections. `state:reviewer-instances-configured`
|
||||||
|
and `state:auto-advance-active` were withheld the same way until a still
|
||||||
|
further #2994 amendment gave `review` and `discuss-phase-assumptions` their
|
||||||
|
own dedicated `cmdInit*` entry points (`cmdInitReview`,
|
||||||
|
`cmdInitDiscussPhaseAssumptions`). The FINAL #2994 amendment gives
|
||||||
|
`docs-update`, `update`, `transition`, and `new-milestone` their own atoms —
|
||||||
|
`state:is-monorepo`, `state:next-channel`, `state:workstream-active`, and
|
||||||
|
`state:flat-mode` — backed by three brand-new dedicated entry points
|
||||||
|
(`cmdInitDocsUpdate`, `cmdInitUpdate`, `cmdInitTransition`) plus an extension
|
||||||
|
of the pre-existing `cmdInitNewMilestone`.
|
||||||
|
|
||||||
### Compound conditions are resolved in the fact, never the grammar
|
### Compound conditions are resolved in the fact, never the grammar
|
||||||
|
|
||||||
@@ -130,6 +184,35 @@ Greenspun's Tenth Rule drift [The frozen `when=`
|
|||||||
vocabulary](#the-frozen-when-vocabulary) above exists to prevent, regardless
|
vocabulary](#the-frozen-when-vocabulary) above exists to prevent, regardless
|
||||||
of how reasonable a single compound condition looks in isolation.
|
of how reasonable a single compound condition looks in isolation.
|
||||||
|
|
||||||
|
`state:ui-phase-active` (#2994) is the same shape: `verify-work.md`'s
|
||||||
|
`automated_ui_verification` step originally computed its own OR at RUNTIME
|
||||||
|
(`UI_PHASE_ACTIVE` from `gsd_run loop render-hooks plan:pre` OR a `*-UI-SPEC.md`
|
||||||
|
file check). `cmdInitVerifyWork` now resolves the identical disjunction ahead
|
||||||
|
of time — `resolveLoopHooks({point: 'plan:pre', ...}).activeHooks` filtered to
|
||||||
|
`kind === 'step' && ref.skill === 'ui-phase'`, OR'd with a `*-UI-SPEC.md`
|
||||||
|
existence check under the phase directory — into `InvocationFacts.uiPhaseActive`,
|
||||||
|
so `WHEN_PREDICATES['state:ui-phase-active']` again reads only that one field.
|
||||||
|
|
||||||
|
`state:plan-strategy-converge` (#2994) is the same shape again:
|
||||||
|
`autonomous.md`'s own bash `PLAN_STRATEGY` resolver already folds `--converge`
|
||||||
|
OR its documented alias `--cross-ai` into a single `"converge"`/`"local"`
|
||||||
|
value at the top of the `initialize` step. `cmdInitAutonomous` mirrors that
|
||||||
|
identical disjunction — `flags.has('--converge') || flags.has('--cross-ai')`
|
||||||
|
— into `InvocationFacts.planStrategyConverge`, so
|
||||||
|
`WHEN_PREDICATES['state:plan-strategy-converge']` reads only that one field,
|
||||||
|
never `--converge`/`--cross-ai` separately.
|
||||||
|
|
||||||
|
`state:auto-advance-active` (#2994) is the same shape once more:
|
||||||
|
`discuss-phase-assumptions.md`'s own `auto_advance` step already resolves
|
||||||
|
`--auto` OR a consolidated `check auto-mode --pick active` fact (itself
|
||||||
|
`workflow._auto_chain_active` OR `workflow.auto_advance`) via a runtime
|
||||||
|
`gsd_run` call before deciding whether to dispatch. `cmdInitDiscussPhaseAssumptions`
|
||||||
|
mirrors that identical disjunction — `options['auto'] === true ||
|
||||||
|
readConfigJsonBoolean(cwd, ['workflow', '_auto_chain_active']) ||
|
||||||
|
readConfigJsonBoolean(cwd, ['workflow', 'auto_advance'])` — into
|
||||||
|
`InvocationFacts.autoAdvanceActive`, so `WHEN_PREDICATES['state:auto-advance-active']`
|
||||||
|
reads only that one field, never the flag and the two config keys separately.
|
||||||
|
|
||||||
## Fails closed
|
## Fails closed
|
||||||
|
|
||||||
An authoring mistake throws at parse time, naming the source file and 1-based
|
An authoring mistake throws at parse time, naming the source file and 1-based
|
||||||
@@ -223,14 +306,19 @@ At init time, a separate pure evaluator, `src/section-manifest.cts`
|
|||||||
(`selectSections`), partitions a workflow's manifest sections into
|
(`selectSections`), partitions a workflow's manifest sections into
|
||||||
`included`/`excluded` id lists against one invocation's
|
`included`/`excluded` id lists against one invocation's
|
||||||
`InvocationFacts` — `{flags, phaseNumber, hasPriorPhases, needsCodebaseMap?,
|
`InvocationFacts` — `{flags, phaseNumber, hasPriorPhases, needsCodebaseMap?,
|
||||||
phaseMvpMode?, worktreesEnabled?, chunkedMode?}`. Only a workflow with a **dedicated
|
phaseMvpMode?, worktreesEnabled?, chunkedMode?, uiPhaseActive?, fallowEnabled?,
|
||||||
|
gitCreateTag?, planStrategyConverge?, reviewerInstancesConfigured?,
|
||||||
|
autoAdvanceActive?, isMonorepo?, nextChannel?, workstreamActive?, flatMode?}`.
|
||||||
|
Only a workflow with a **dedicated
|
||||||
`cmdInit*` entry point** in `src/init.cts` can have this evaluation run for
|
`cmdInit*` entry point** in `src/init.cts` can have this evaluation run for
|
||||||
it, because only that entry point can assemble `InvocationFacts` from its own
|
it, because only that entry point can assemble `InvocationFacts` from its own
|
||||||
parsed CLI options and `.planning/` state reads — this is admission gate 2
|
parsed CLI options and `.planning/` state reads — this is admission gate 2
|
||||||
from [The frozen `when=` vocabulary](#the-frozen-when-vocabulary) above,
|
from [The frozen `when=` vocabulary](#the-frozen-when-vocabulary) above,
|
||||||
applied per-workflow rather than per-atom. Six entry points are wired today:
|
applied per-workflow rather than per-atom. Fifteen entry points are wired
|
||||||
`execute-phase`, `plan-phase`, `new-project`, `new-milestone`, `quick`, and
|
today: `execute-phase`, `plan-phase`, `new-project`, `new-milestone`,
|
||||||
`progress`.
|
`quick`, `progress`, `verify-work`, `code-review`, `complete-milestone`,
|
||||||
|
`autonomous`, `review`, `discuss-phase-assumptions`, `docs-update`, `update`,
|
||||||
|
and `transition`.
|
||||||
|
|
||||||
`InvocationFacts.flags` is a `ReadonlySet<string>` of the literal `--<name>`
|
`InvocationFacts.flags` is a `ReadonlySet<string>` of the literal `--<name>`
|
||||||
tokens seen on the invocation, and **membership is token-presence, not
|
tokens seen on the invocation, and **membership is token-presence, not
|
||||||
@@ -245,13 +333,27 @@ own `false` into `undefined` (`namedArgs['wave'] || undefined`) before
|
|||||||
handing options to the facts builder, so `flags` only ever contains tokens
|
handing options to the facts builder, so `flags` only ever contains tokens
|
||||||
that were actually seen.
|
that were actually seen.
|
||||||
|
|
||||||
## Piloted on execute-phase.md, then rolled out to plan-phase.md
|
## Piloted on execute-phase.md, then rolled out across the wired workflows
|
||||||
|
|
||||||
Two workflows carry markers today: `gsd-core/workflows/execute-phase.md` (the
|
Fifteen workflows carry markers today, all of them the workflows with a
|
||||||
#2930/Phase-3 pilot) and `gsd-core/workflows/plan-phase.md` (#2993, epic
|
dedicated `cmdInit*` entry point (see [The manifest artifact](#the-manifest-artifact-and-per-workflow-keying)
|
||||||
#1671 Phase 6.2). The marker grammar and composer seam are general-purpose
|
above): `gsd-core/workflows/execute-phase.md` (the #2930/Phase-3 pilot),
|
||||||
across any workflow file; rollout to further LARGE/XL workflows remains
|
`gsd-core/workflows/plan-phase.md` (#2993, epic #1671 Phase 6.2),
|
||||||
sequenced as later work.
|
`gsd-core/workflows/progress.md`, `gsd-core/workflows/new-project.md`,
|
||||||
|
`gsd-core/workflows/quick.md`, `gsd-core/workflows/new-milestone.md` (those
|
||||||
|
four, #2994, epic #1671 Phase 6.3), `gsd-core/workflows/verify-work.md`
|
||||||
|
(also #2994, epic #1671 Phase 6.3), `gsd-core/workflows/code-review.md` /
|
||||||
|
`gsd-core/workflows/complete-milestone.md` (a further #2994 amendment, epic
|
||||||
|
#1671 Phase 6.3), `gsd-core/workflows/autonomous.md` (a still further
|
||||||
|
#2994 amendment, epic #1671 Phase 6.3), `gsd-core/workflows/review.md` /
|
||||||
|
`gsd-core/workflows/discuss-phase-assumptions.md` (a still further #2994
|
||||||
|
amendment, epic #1671 Phase 6.3), and finally
|
||||||
|
`gsd-core/workflows/docs-update.md` / `gsd-core/workflows/update.md` /
|
||||||
|
`gsd-core/workflows/transition.md` (the LAST #2994 amendment, epic #1671
|
||||||
|
Phase 6.3 — none of these three carried a `gsd_run query init.*` call before
|
||||||
|
this slice). Every one of the 13 workflows ADR-1671 targeted is now on the
|
||||||
|
fragment model; the marker grammar and composer seam remain general-purpose
|
||||||
|
across any future workflow file, but no further rollout is scheduled.
|
||||||
|
|
||||||
`execute-phase.md` marks three `<step>` blocks: `partial-wave`
|
`execute-phase.md` marks three `<step>` blocks: `partial-wave`
|
||||||
(`flag:--wave`), `gap-closure-artifacts` (`state:gap-closure-phase`), and
|
(`flag:--wave`), `gap-closure-artifacts` (`state:gap-closure-phase`), and
|
||||||
@@ -264,6 +366,233 @@ sequenced as later work.
|
|||||||
`RESEARCH_ONLY` condition, so they include/exclude together), and
|
`RESEARCH_ONLY` condition, so they include/exclude together), and
|
||||||
`chunked-planning-mode` (`state:chunked-mode`).
|
`chunked-planning-mode` (`state:chunked-mode`).
|
||||||
|
|
||||||
|
`progress.md` marks two sections: `forensic-audit` (`flag:--forensic`, #2994
|
||||||
|
forensic audit) and `mvp-display` (`state:phase-mvp-mode`). `mvp-display`'s
|
||||||
|
own body used to re-resolve its own gating fact via a `gsd_run query
|
||||||
|
phase.mvp-mode` call — circular, since a section's body re-deriving the exact
|
||||||
|
condition that gated its own inclusion is self-disabling the moment the init
|
||||||
|
seam's computation and the body's computation drift. `cmdInitProgress` now
|
||||||
|
computes `phaseMvpMode` for the CURRENT phase directly (threading a real
|
||||||
|
`phase_number` into `buildSectionManifestField`, where before it passed
|
||||||
|
`null` and the fact was permanently `false`) and exposes it as a top-level
|
||||||
|
`phase_mvp_mode` init-bundle field, so the step body consumes an
|
||||||
|
already-resolved fact instead of recomputing it.
|
||||||
|
|
||||||
|
`new-project.md` marks two sections, both `flag:--auto`: `auto-mode-detection`
|
||||||
|
(the `<auto_mode>` tag itself stays outside the marker — only its body is
|
||||||
|
extracted) and `auto-mode-config` (`## 2a. Auto Mode Config`).
|
||||||
|
|
||||||
|
`quick.md` marks five sections: `discussion-phase` (`flag:--discuss`),
|
||||||
|
`research-phase` (`flag:--research`), `plan-checker-loop` and
|
||||||
|
`quick-verification` (both `flag:--validate` — two consumers sharing one
|
||||||
|
atom, mirroring `plan-phase.md`'s `research-only-*` pair), and
|
||||||
|
`worktree-pre-dispatch-commit` (`state:worktrees-enabled`). `quick.md`'s
|
||||||
|
`--full` flag IMPLIES `--discuss`/`--research`/`--validate` — folded into the
|
||||||
|
facts inside `cmdInitQuick` (mirroring `state:chunked-mode`'s disjunction
|
||||||
|
fold) before `buildSectionManifestField` builds its flags Set, so a bare
|
||||||
|
`--full` invocation still includes the three flag-gated sections without the
|
||||||
|
grammar ever seeing an OR.
|
||||||
|
|
||||||
|
`new-milestone.md` marks two sections: `reset-phase-safety`
|
||||||
|
(`flag:--reset-phase-numbers`) and, from the final #2994 slice,
|
||||||
|
`project-md-milestone-write` (`state:flat-mode`) — Step 4's "Part A"
|
||||||
|
milestone-state write, which must run ONLY when NO workstream is active
|
||||||
|
(#2308: an active workstream's own `.planning/workstreams/<name>/STATE.md`/
|
||||||
|
`ROADMAP.md`/`REQUIREMENTS.md` already carry this milestone's state, so
|
||||||
|
writing the shared `## Current Milestone` heading here would clobber it).
|
||||||
|
Part A's true condition is the NEGATION of "a workstream is active", and the
|
||||||
|
grammar has no negation operator, so `state:flat-mode` is a SEPARATE,
|
||||||
|
positively-phrased atom (fact `= !workstreamActive`) rather than a negated
|
||||||
|
`state:workstream-active` — see `transition.md` below for the atom this one
|
||||||
|
inverts. Part B ("Evolution structural repair", always runs regardless of
|
||||||
|
workstream mode) stays OUTSIDE the marker, directly after it, for the same
|
||||||
|
reason every other flag-absent/state-false fallback in this document does.
|
||||||
|
`cmdInitNewMilestone` computes both `workstreamActive` and `flatMode` from
|
||||||
|
the SAME authoritative source `cmdInitProgress` already established
|
||||||
|
(`GSD_WORKSTREAM` env, falling back to the stored active-workstream
|
||||||
|
pointer). Because Step 4 (where the marker lives) runs well before
|
||||||
|
`new-milestone.md`'s pre-existing `init.new-milestone` call (Step 7, kept
|
||||||
|
AFTER Step 6's phase archival so its `phase_dir_count`/`roadmap_exists`
|
||||||
|
fields reflect POST-archival state), a SECOND, section-manifest-only
|
||||||
|
`init.new-milestone` call is added early in Step 1 — `init.new-milestone`
|
||||||
|
is a pure read with no mutation, so calling it twice is side-effect-free.
|
||||||
|
|
||||||
|
`verify-work.md` marks two sections: `automated-ui-verification`
|
||||||
|
(the new `state:ui-phase-active`, #2994 — see [Compound conditions are
|
||||||
|
resolved in the fact, never the grammar](#compound-conditions-are-resolved-in-the-fact-never-the-grammar)
|
||||||
|
above) and `mvp-uat-framing` (`state:phase-mvp-mode`, sharing the atom
|
||||||
|
already computed for `progress.md`'s `mvp-display`). `mvp-uat-framing`'s
|
||||||
|
extraction is narrower than `progress.md`'s `mvp-display`: only the
|
||||||
|
true-branch prose (the three ordered UAT sections plus the User Story format
|
||||||
|
guard) moves into the step file — the false-branch note ("When `MVP_MODE=false`
|
||||||
|
… fall back to the standard UAT generation path") stays OUTSIDE the marker,
|
||||||
|
directly after it, because gating it away with the rest of the section would
|
||||||
|
delete the exact text needed on every invocation where the atom is `false`
|
||||||
|
(the common, non-MVP case). Unlike `progress.md`'s `mvp-display`, `verify-work.md`
|
||||||
|
keeps its own `MVP_MODE=$(gsd_run query phase.mvp-mode ...)` runtime resolver
|
||||||
|
(in the unconditional `initialize` step, not inside the gated section) — it is
|
||||||
|
not circular/self-disabling the way `progress.md`'s inline resolver was,
|
||||||
|
because the un-marked false-branch note and the step-file prose both still
|
||||||
|
reference `$MVP_MODE` as a runtime variable, so the resolver keeps a live
|
||||||
|
consumer outside the gate.
|
||||||
|
|
||||||
|
`code-review.md` marks two sections: `structural-pre-pass` (`state:fallow-enabled`)
|
||||||
|
and `dispatch-fix` (`flag:--fix`). `structural-pre-pass`'s own body used to
|
||||||
|
re-resolve its own gating fact via four `gsd_run query config-get
|
||||||
|
code_quality.fallow.*` calls — circular, for the same reason `progress.md`'s
|
||||||
|
pre-hoist `mvp-display` was: a section's body re-deriving the exact condition
|
||||||
|
that gated its own inclusion is self-disabling the moment the init seam's
|
||||||
|
computation and the body's computation drift. `cmdInitCodeReview` now resolves
|
||||||
|
`code_quality.fallow.{enabled,scope,profile,mcp}` once (`detectFallowConfig`,
|
||||||
|
`src/init.cts`) and exposes them as top-level `fallow_enabled`/`fallow_scope`/
|
||||||
|
`fallow_profile`/`fallow_mcp`/`fallow_max_crap` init-bundle fields; the
|
||||||
|
unconditional part of the `structural_pre_pass` step now just parses those
|
||||||
|
fields, and only the fallow-binary-resolve-and-execute portion (which produces
|
||||||
|
`FALLOW.json`) is gated behind the marker — the `FALLOW_JSON_PATH=""`
|
||||||
|
disabled-path fallback stays OUTSIDE the marker, directly after it, for the
|
||||||
|
same reason `verify-work.md`'s MVP false-branch note does (deleting it would
|
||||||
|
break the common, fallow-disabled case). `dispatch-fix` moves the entire
|
||||||
|
`--fix`-gated step wholesale (mirroring `progress.md`'s `forensic-audit`
|
||||||
|
extraction) — `code-review.md`'s `initialize` step now resolves the RESOLVED
|
||||||
|
fix decision (`--fix` itself, or `--all`/`--auto` implying it, via
|
||||||
|
`code-review-flags.cjs`) before the `init.code-review` call, so the
|
||||||
|
section-manifest gate matches the flags module's own implication logic rather
|
||||||
|
than a raw `--fix` token scan.
|
||||||
|
|
||||||
|
`complete-milestone.md` marks one section: `git-tag` (`state:git-create-tag`).
|
||||||
|
The `git_tag` step's own `<config-check>` sub-tag used to re-resolve
|
||||||
|
`git.create_tag` via `gsd-tools.cjs query config-get` to decide whether to
|
||||||
|
skip the step — again a section (here, a whole step) gating its own inclusion
|
||||||
|
on a fact its own body computed. `cmdInitCompleteMilestone` now resolves it
|
||||||
|
once (`detectGitCreateTag`, `src/init.cts`, fail-OPEN default `true` — an
|
||||||
|
unset key means "create the tag", the inverse polarity of `detectFallowConfig`'s
|
||||||
|
fail-closed default, mirroring the two source resolvers' own opposite
|
||||||
|
defaults) and exposes it as the init-bundle's `git_create_tag` field; the
|
||||||
|
entire `git_tag` step moves to its step file wholesale, with no
|
||||||
|
`<config-check>` left to re-derive. `complete-milestone.md` gains an
|
||||||
|
ADDITIVE `init.complete-milestone` call (in the `handle_branches` step,
|
||||||
|
alongside its pre-existing `init.manager` and `init.execute-phase` calls,
|
||||||
|
neither of which is removed) purely to carry `git_create_tag` and
|
||||||
|
`section_manifest` — it has no phase-listing logic of its own to delegate.
|
||||||
|
|
||||||
|
`autonomous.md` marks five sections, all sharing the single
|
||||||
|
`state:plan-strategy-converge` atom (legal and precedented — `plan-phase.md`'s
|
||||||
|
`research-only-*` pair already shares `flag:--research-phase`): `converge-fail-fast`
|
||||||
|
(the `workflow.plan_review_convergence` feature-gate check, split out of the
|
||||||
|
surrounding `CONVERGENCE_ARGS` bash block — that block's reviewer-flag/`--max-cycles`
|
||||||
|
parsing stays UNGATED, directly before the marker, because it always needs to
|
||||||
|
run regardless of `PLAN_STRATEGY`, and only the `if [ "$PLAN_STRATEGY" =
|
||||||
|
"converge" ]` fail-fast check moves into the step file), `converge-banner`
|
||||||
|
(a single display line — still a legitimate section per [Marker
|
||||||
|
syntax](#marker-syntax) above; `scripts/gen-section-manifest.cjs`'s
|
||||||
|
`FAIL_MISSING_STEP_FILE` check requires a step file for every explicit marker
|
||||||
|
regardless of body size, so the stub+step-file round trip is not optional
|
||||||
|
here even though the body is trivially small), `converge-dispatch-bg` and
|
||||||
|
`converge-dispatch-inline` (the `PLAN_STRATEGY=converge` branch of step 3b's
|
||||||
|
background/inline `FLATTEN` dispatch — an ORTHOGONAL condition interleaved in
|
||||||
|
the same list; each converge branch is independently contiguous and the
|
||||||
|
sibling `- Otherwise, print: ...`/`- Otherwise (local planning):` fallback
|
||||||
|
bullets stay OUTSIDE the marker, immediately after it, because they are the
|
||||||
|
`PLAN_STRATEGY=local` default that must always render), and `converge-loop`
|
||||||
|
(the unconditional-`INTERACTIVE` bottom-of-3b convergence dispatch, with the
|
||||||
|
`PLAN_STRATEGY=local` regular-planner fallback again staying outside).
|
||||||
|
`autonomous.md` keeps its own bash `PLAN_STRATEGY` resolver (`"local"` vs.
|
||||||
|
`"converge"`, folding `--converge` OR `--cross-ai`) in the UNCONDITIONAL
|
||||||
|
`initialize` step — never moved or removed — because ungated content later
|
||||||
|
in the same step (the "local" planning bullets) still references
|
||||||
|
`$PLAN_STRATEGY` as a runtime variable, same discipline as
|
||||||
|
`verify-work.md`'s retained `$MVP_MODE` resolver. `cmdInitAutonomous` mirrors
|
||||||
|
the identical disjunction into `InvocationFacts.planStrategyConverge`, and
|
||||||
|
`autonomous.md` gains an ADDITIVE `init.autonomous` call (in the
|
||||||
|
`initialize` step, alongside its pre-existing `init.milestone-op`,
|
||||||
|
`init.manager`, and `init.phase-op` calls — CRITICAL blast radius, none
|
||||||
|
removed, none modified) purely to carry `section_manifest`; like
|
||||||
|
`complete-milestone.md`'s entry point, it has no phase-listing logic of its
|
||||||
|
own to delegate.
|
||||||
|
|
||||||
|
`review.md` marks two sections, both sharing the single
|
||||||
|
`state:reviewer-instances-configured` atom (legal and precedented —
|
||||||
|
`plan-phase.md`'s `research-only-*` pair already shares
|
||||||
|
`flag:--research-phase`): `reviewer-instances-note-1` and
|
||||||
|
`reviewer-instances-note-2`, two peripheral additive notes in the
|
||||||
|
`detect_clis` and `invoke_reviewers` steps respectively. Neither note is
|
||||||
|
part of the workflow's core reviewer-lane dispatch — that dispatch is the
|
||||||
|
workflow's primary always-evaluated logic and is never gated. `review.md`
|
||||||
|
previously routed through the shared, 20+-caller `init.phase-op`, reading
|
||||||
|
only 3 of its ~60 fields (`phase_dir`, `phase_number`, `padded_phase`);
|
||||||
|
`cmdInitReview` now resolves those 3 fields itself via the same
|
||||||
|
`guardedFindPhase`/`guardedGetRoadmapPhase` primitives, plus the
|
||||||
|
`review.reviewer_instances` config-presence fact (reusing
|
||||||
|
`readConfigJsonValue`, added for `detectFallowConfig` — no second config
|
||||||
|
reader).
|
||||||
|
|
||||||
|
`discuss-phase-assumptions.md` marks one section: `auto-advance-dispatch`
|
||||||
|
(`state:auto-advance-active`), inside the `auto_advance` step. The step's
|
||||||
|
own `--auto`-flag parse, chain-flag sync, and consolidated `AUTO_MODE`
|
||||||
|
resolver (all of which must always run) stay OUTSIDE the marker; only the
|
||||||
|
flag-present display-banner-and-launch body is gated, and the flag-absent
|
||||||
|
"End here" fallback stays OUTSIDE the marker too, directly after it — gating
|
||||||
|
the whole step would delete the fallback text needed exactly when `--auto`
|
||||||
|
is absent, the same class of hazard `verify-work.md`'s MVP false-branch note
|
||||||
|
and `code-review.md`'s fallow-disabled fallback both document. `discuss-phase-assumptions.md`
|
||||||
|
previously routed through `init.phase-op`, reading 14 of its fields;
|
||||||
|
`cmdInitDiscussPhaseAssumptions` now resolves those 14 fields itself (via
|
||||||
|
the same shared primitives, reproducing `cmdInitPhaseOp`'s archived/not-found
|
||||||
|
fallback shape), plus `state:auto-advance-active` — `--auto` flag OR a
|
||||||
|
consolidated auto-mode config fact, resolved to one boolean the same way
|
||||||
|
`state:chunked-mode` is.
|
||||||
|
|
||||||
|
`docs-update.md` marks one section: `dispatch-monorepo-packages`
|
||||||
|
(`state:is-monorepo`) — the whole `dispatch_monorepo_packages` `<step>`,
|
||||||
|
which already carried a hand-written `condition="monorepo_workspaces is
|
||||||
|
non-empty"` attribute identifying it as the gated region before this slice.
|
||||||
|
`docs-update.md` previously called `docs-init` (`cmdDocsInit`, `src/docs.cts`)
|
||||||
|
only — a SEPARATE, pre-existing entry point outside the `init.*` family,
|
||||||
|
left untouched. The new `cmdInitDocsUpdate` is purely ADDITIVE, carrying only
|
||||||
|
`section_manifest`; its `isMonorepo` fact reuses `detectMonorepoWorkspaces`
|
||||||
|
(exported from `src/docs.cts` for this purpose) rather than a second,
|
||||||
|
divergence-prone workspace-glob scan — the SAME detector that already backs
|
||||||
|
`docs-init`'s own `monorepo_workspaces` field.
|
||||||
|
|
||||||
|
`update.md` marks one section: `channel-banner` (`state:next-channel`) — the
|
||||||
|
"Only when `TAG=next`" channel-banner paragraph in `compare_versions`.
|
||||||
|
`update.md` previously called NO `init.*` command at all; it resolves
|
||||||
|
`gsd-tools.cjs` itself via a bespoke `PREFERRED_CONFIG_DIR`/
|
||||||
|
`PREFERRED_RUNTIME`-aware `$GSD_TOOLS` cascade (`get_installed_version`,
|
||||||
|
~lines 13-45) because the update workflow must run before any install can be
|
||||||
|
assumed resolvable. The new `init.update` call REUSES that already-resolved
|
||||||
|
`$GSD_TOOLS` (dual `.cjs`/PATH-shim invocation style, matching the
|
||||||
|
pre-existing `update-context` call) rather than copying the canonical
|
||||||
|
launcher preamble — both resolutions assign the identical `$GSD_TOOLS` shell
|
||||||
|
variable, and the canonical preamble would silently clobber the value later
|
||||||
|
steps (`backup_custom_files`, `restore_custom_files`) still depend on.
|
||||||
|
`cmdInitUpdate`'s `nextChannel` fact (`--next` OR its documented alias
|
||||||
|
`--rc`) is resolved in PARALLEL with, not in place of, `update.md`'s own
|
||||||
|
`TAG="next"`/`TAG="latest"` case-statement in `parse_update_channel`, which
|
||||||
|
stays byte-identical — issue #815's regression test
|
||||||
|
(`tests/issue-815-update-next-channel.test.cjs`) asserts that literal
|
||||||
|
case-statement text remains in the workflow, since the npm dist-tag
|
||||||
|
selection has to run in the workflow's own shell before any `gsd_run`
|
||||||
|
round-trip.
|
||||||
|
|
||||||
|
`transition.md` marks one section: `workstream-collision-check`
|
||||||
|
(`state:workstream-active`) — an internal workflow (it has no user-facing
|
||||||
|
slash command of its own) that previously called NO `init.*` command at
|
||||||
|
all. It already establishes `gsd_run()` via the canonical launcher preamble
|
||||||
|
in its `update_roadmap_and_state` step, before this call's insertion point
|
||||||
|
in `offer_next_phase`, so no second preamble copy is needed. The section's
|
||||||
|
body previously re-derived its own gating condition via an inline `gsd_run
|
||||||
|
query workstream.list --raw` call wrapped in `if [ -n "$GSD_WORKSTREAM" ]`
|
||||||
|
— the identical condition that now backs the section's own admission (the
|
||||||
|
same resolver-in-body hazard `code-review.md`'s `structural-pre-pass` and
|
||||||
|
`complete-milestone.md`'s `git-tag` documented). `cmdInitTransition` hoists
|
||||||
|
this via `getOtherActiveWorkstreamInventories` (`src/workstream-inventory.cts`
|
||||||
|
— the SAME primitive `workstream.list` itself calls), pre-filtered exactly
|
||||||
|
as the step's own prose described (excludes the current workstream and any
|
||||||
|
workstream whose status contains "milestone complete" or "archived",
|
||||||
|
case-insensitively), exposed as `other_active_workstreams`; the step body is
|
||||||
|
now a pure JSON consumer with no `gsd_run` call of its own.
|
||||||
|
|
||||||
**`plan-phase.md` was originally retargeted away from the #2930 pilot,
|
**`plan-phase.md` was originally retargeted away from the #2930 pilot,
|
||||||
then fragmentized here once the blocker cleared.** Issue #2930's own
|
then fragmentized here once the blocker cleared.** Issue #2930's own
|
||||||
motivating mutually-exclusive branches (`--prd`, `--ingest`, `--mvp`,
|
motivating mutually-exclusive branches (`--prd`, `--ingest`, `--mvp`,
|
||||||
@@ -304,5 +633,5 @@ question 1's resolution for the full record.
|
|||||||
`InvocationFacts`) consumed by the init seam.
|
`InvocationFacts`) consumed by the init seam.
|
||||||
- `scripts/gen-section-manifest.cjs` — generates the committed
|
- `scripts/gen-section-manifest.cjs` — generates the committed
|
||||||
`gsd-core/workflows/section-manifest.json` artifact from markers.
|
`gsd-core/workflows/section-manifest.json` artifact from markers.
|
||||||
- `src/init.cts` — `buildSectionManifestField` and the six wired
|
- `src/init.cts` — `buildSectionManifestField` and the fifteen wired
|
||||||
`cmdInit*` entry points.
|
`cmdInit*` entry points.
|
||||||
|
|||||||
42
gsd-core/references/autonomous-ui-design-contract.md
Normal file
42
gsd-core/references/autonomous-ui-design-contract.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# UI Design Contract (Frontend Phases) — Autonomous Mode
|
||||||
|
|
||||||
|
Step 3a.5 of `/gsd:autonomous`: resolve whether the current phase needs a UI-SPEC.md generated before planning, and generate one via active `plan:pre` step hooks if so. Always non-blocking — proceeds to step 3b (Plan) regardless of outcome.
|
||||||
|
|
||||||
|
**Inputs:** `PHASE_NUM`, `PHASE_DIR` from execute_phase.
|
||||||
|
|
||||||
|
Resolve active `plan:pre` hooks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
||||||
|
HOOKS_JSON=$(gsd_run loop render-hooks plan:pre --raw)
|
||||||
|
```
|
||||||
|
|
||||||
|
Read the `activeHooks` array directly from `HOOKS_JSON` (in-context — do NOT invoke a shell pipeline). **Compute the active UI step hooks** = entries from `activeHooks` where `kind == "step"` and `ref.skill` is set. **If there are NO active step hooks → skip silently to 3b.** (This covers `workflow.ui_phase=false` — including configurations where only a gate-only entry is present, e.g. `ui_phase=false` + `ui_safety_gate=true` produces `activeHooks=[{kind:"gate"}]`. Autonomous never runs the plan:pre gate — it is always pipeline mode — so a gate-only active set is equivalent to no active step and is silently skipped here. This matches OLD §3a.5 behaviour.)
|
||||||
|
|
||||||
|
(At least one active step hook ⇒ `workflow.ui_phase` is on.) Run the UI-SPEC gate:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GATE=$(gsd_run check ui-plan-gate "${PHASE_NUM}" --raw)
|
||||||
|
```
|
||||||
|
|
||||||
|
Read `frontend` and `hasUiSpec` from `GATE` (in-context).
|
||||||
|
|
||||||
|
**If `frontend` is false:** Skip silently to 3b.
|
||||||
|
|
||||||
|
**If `hasUiSpec` is true (UI-SPEC already exists):** Skip silently to 3b.
|
||||||
|
|
||||||
|
**Otherwise (frontend phase + no UI-SPEC):** For each active step hook (the `kind == "step"` set from above, in array order):
|
||||||
|
|
||||||
|
```
|
||||||
|
Skill(skill="gsd-${ref.skill}", args="${PHASE_NUM}")
|
||||||
|
```
|
||||||
|
|
||||||
|
(Prepend `gsd-` to `ref.skill` — so `ui-phase` → `gsd-ui-phase`. Bare `${PHASE_NUM}` args — autonomous style, same pattern as the verify:post dispatch.) Entries where `kind == "gate"` are silently ignored — autonomous is always pipeline mode, there is no blocking gate here.
|
||||||
|
|
||||||
|
After all step hooks return, re-read:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
||||||
|
```
|
||||||
|
|
||||||
|
**If `UI_SPEC_FILE` is still empty:** Display warning `Phase ${PHASE_NUM}: UI-SPEC generation did not produce output — continuing without design contract.` and proceed to 3b. NON-BLOCKING.
|
||||||
@@ -44,6 +44,15 @@ PLAN_STRATEGY="local"
|
|||||||
if echo "$ARGUMENTS" | grep -qE '(^|[[:space:]])\-\-(converge|cross-ai)([[:space:]]|$)'; then
|
if echo "$ARGUMENTS" | grep -qE '(^|[[:space:]])\-\-(converge|cross-ai)([[:space:]]|$)'; then
|
||||||
PLAN_STRATEGY="converge"
|
PLAN_STRATEGY="converge"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
CONVERGE_PARAM=""
|
||||||
|
if echo "$ARGUMENTS" | grep -qE '(^|[[:space:]])\-\-converge([[:space:]]|$)'; then
|
||||||
|
CONVERGE_PARAM="--converge"
|
||||||
|
fi
|
||||||
|
CROSS_AI_PARAM=""
|
||||||
|
if echo "$ARGUMENTS" | grep -qE '(^|[[:space:]])\-\-cross-ai([[:space:]]|$)'; then
|
||||||
|
CROSS_AI_PARAM="--cross-ai"
|
||||||
|
fi
|
||||||
```
|
```
|
||||||
|
|
||||||
When `--only` is set, also set `FROM_PHASE` to the same value so existing filter logic applies.
|
When `--only` is set, also set `FROM_PHASE` to the same value so existing filter logic applies.
|
||||||
@@ -58,8 +67,12 @@ Bootstrap via milestone-level init:
|
|||||||
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
||||||
INIT=$(gsd_run query init.milestone-op)
|
INIT=$(gsd_run query init.milestone-op)
|
||||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||||
|
INIT_AUTONOMOUS=$(gsd_run query init.autonomous $CONVERGE_PARAM $CROSS_AI_PARAM)
|
||||||
|
if [[ "$INIT_AUTONOMOUS" == @file:* ]]; then INIT_AUTONOMOUS=$(cat "${INIT_AUTONOMOUS#@file:}"); fi
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Extract `section_manifest` from `INIT_AUTONOMOUS` (used by the `converge-*` sections below and in step 3).
|
||||||
|
|
||||||
If `PLAN_STRATEGY` is `converge`, fail fast unless the existing convergence feature gate is enabled:
|
If `PLAN_STRATEGY` is `converge`, fail fast unless the existing convergence feature gate is enabled:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -79,23 +92,12 @@ if echo "$ARGUMENTS" | grep -qE '\-\-max-cycles\s+[0-9]+'; then
|
|||||||
MAX_CYCLES_ARG=$(echo "$ARGUMENTS" | grep -oE '\-\-max-cycles\s+[0-9]+' | awk '{print $2}')
|
MAX_CYCLES_ARG=$(echo "$ARGUMENTS" | grep -oE '\-\-max-cycles\s+[0-9]+' | awk '{print $2}')
|
||||||
CONVERGENCE_ARGS="${CONVERGENCE_ARGS} --max-cycles ${MAX_CYCLES_ARG}"
|
CONVERGENCE_ARGS="${CONVERGENCE_ARGS} --max-cycles ${MAX_CYCLES_ARG}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$PLAN_STRATEGY" = "converge" ]; then
|
|
||||||
CONVERGENCE_ENABLED=$(gsd_run query config-get workflow.plan_review_convergence 2>/dev/null || echo "false")
|
|
||||||
if [ "$CONVERGENCE_ENABLED" != "true" ]; then
|
|
||||||
printf '%s\n' \
|
|
||||||
'gsd-autonomous --converge is disabled (workflow.plan_review_convergence=false).' \
|
|
||||||
'' \
|
|
||||||
'Enable plan convergence with:' \
|
|
||||||
'' \
|
|
||||||
' gsd config-set workflow.plan_review_convergence true' \
|
|
||||||
'' \
|
|
||||||
'Then re-run the autonomous command with --converge.'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<!-- gsd:section id="converge-fail-fast" when="state:plan-strategy-converge" -->
|
||||||
|
If `section_manifest` is `null` or `"converge-fail-fast"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-fail-fast.md`. Otherwise skip — do not read the file.
|
||||||
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
Parse JSON for: `milestone_version`, `milestone_name`, `phase_count`, `completed_phases`, `roadmap_exists`, `state_exists`, `commit_docs`.
|
Parse JSON for: `milestone_version`, `milestone_name`, `phase_count`, `completed_phases`, `roadmap_exists`, `state_exists`, `commit_docs`.
|
||||||
|
|
||||||
**If `roadmap_exists` is false:** Error — "No ROADMAP.md found. Run `/gsd:new-milestone` first."
|
**If `roadmap_exists` is false:** Error — "No ROADMAP.md found. Run `/gsd:new-milestone` first."
|
||||||
@@ -116,7 +118,9 @@ If `ONLY_PHASE` is set, display: `Single phase mode: Phase ${ONLY_PHASE}`
|
|||||||
Else if `FROM_PHASE` is set, display: `Starting from phase ${FROM_PHASE}`
|
Else if `FROM_PHASE` is set, display: `Starting from phase ${FROM_PHASE}`
|
||||||
If `TO_PHASE` is set, display: `Stopping after phase ${TO_PHASE}`
|
If `TO_PHASE` is set, display: `Stopping after phase ${TO_PHASE}`
|
||||||
If `INTERACTIVE` is set, display: `Mode: Interactive (discuss inline, plan+execute inline — background on Codex only)`
|
If `INTERACTIVE` is set, display: `Mode: Interactive (discuss inline, plan+execute inline — background on Codex only)`
|
||||||
If `PLAN_STRATEGY` is `converge`, display: `Planning: Plan-review convergence enabled`
|
<!-- gsd:section id="converge-banner" when="state:plan-strategy-converge" -->
|
||||||
|
If `section_manifest` is `null` or `"converge-banner"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-banner.md`. Otherwise skip — do not read the file.
|
||||||
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
**Agent skills (delegated agents self-load):** This workflow delegates plan/execute/review via flat `Skill()` invocations rather than resolving `agent_skills` itself. Each consumer agent (`gsd-planner`, `gsd-executor`, `gsd-plan-checker`, `gsd-verifier`, …) self-loads its configured `.planning/config.json` `agent_skills` in its own mandatory init step per `@~/.claude/gsd-core/references/agent-skills-bootstrap.md`. This is the durable path that works on every runtime — including Cursor, where `Skill()`-delegated workflow bash init does not reliably execute. No per-delegation injection is needed here. See open-gsd/gsd-core#1866.
|
**Agent skills (delegated agents self-load):** This workflow delegates plan/execute/review via flat `Skill()` invocations rather than resolving `agent_skills` itself. Each consumer agent (`gsd-planner`, `gsd-executor`, `gsd-plan-checker`, `gsd-verifier`, …) self-loads its configured `.planning/config.json` `agent_skills` in its own mandatory init step per `@~/.claude/gsd-core/references/agent-skills-bootstrap.md`. This is the durable path that works on every runtime — including Cursor, where `Skill()`-delegated workflow bash init does not reliably execute. No per-delegation injection is needed here. See open-gsd/gsd-core#1866.
|
||||||
|
|
||||||
@@ -332,42 +336,11 @@ Check `has_context`. If false → go to handle_blocker: "Discuss for phase ${PHA
|
|||||||
|
|
||||||
**3a.5. UI Design Contract (Frontend Phases)**
|
**3a.5. UI Design Contract (Frontend Phases)**
|
||||||
|
|
||||||
Resolve active `plan:pre` hooks:
|
> Full instructions are in `gsd-core/references/autonomous-ui-design-contract.md`. Read that file now and follow it exactly.
|
||||||
|
|
||||||
```bash
|
**Inputs:** `PHASE_NUM`, `PHASE_DIR` from execute_phase. Resolves whether the phase needs a UI-SPEC.md generated before planning via active `plan:pre` step hooks. Always non-blocking — proceeds to 3b regardless of outcome.
|
||||||
UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
|
||||||
HOOKS_JSON=$(gsd_run loop render-hooks plan:pre --raw)
|
|
||||||
```
|
|
||||||
|
|
||||||
Read the `activeHooks` array directly from `HOOKS_JSON` (in-context — do NOT invoke a shell pipeline). **Compute the active UI step hooks** = entries from `activeHooks` where `kind == "step"` and `ref.skill` is set. **If there are NO active step hooks → skip silently to 3b.** (This covers `workflow.ui_phase=false` — including configurations where only a gate-only entry is present, e.g. `ui_phase=false` + `ui_safety_gate=true` produces `activeHooks=[{kind:"gate"}]`. Autonomous never runs the plan:pre gate — it is always pipeline mode — so a gate-only active set is equivalent to no active step and is silently skipped here. This matches OLD §3a.5 behaviour.)
|
Read and execute: `$HOME/.claude/gsd-core/references/autonomous-ui-design-contract.md`
|
||||||
|
|
||||||
(At least one active step hook ⇒ `workflow.ui_phase` is on.) Run the UI-SPEC gate:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
GATE=$(gsd_run check ui-plan-gate "${PHASE_NUM}" --raw)
|
|
||||||
```
|
|
||||||
|
|
||||||
Read `frontend` and `hasUiSpec` from `GATE` (in-context).
|
|
||||||
|
|
||||||
**If `frontend` is false:** Skip silently to 3b.
|
|
||||||
|
|
||||||
**If `hasUiSpec` is true (UI-SPEC already exists):** Skip silently to 3b.
|
|
||||||
|
|
||||||
**Otherwise (frontend phase + no UI-SPEC):** For each active step hook (the `kind == "step"` set from above, in array order):
|
|
||||||
|
|
||||||
```
|
|
||||||
Skill(skill="gsd-${ref.skill}", args="${PHASE_NUM}")
|
|
||||||
```
|
|
||||||
|
|
||||||
(Prepend `gsd-` to `ref.skill` — so `ui-phase` → `gsd-ui-phase`. Bare `${PHASE_NUM}` args — autonomous style, same pattern as the verify:post dispatch.) Entries where `kind == "gate"` are silently ignored — autonomous is always pipeline mode, there is no blocking gate here.
|
|
||||||
|
|
||||||
After all step hooks return, re-read:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
|
||||||
```
|
|
||||||
|
|
||||||
**If `UI_SPEC_FILE` is still empty:** Display warning `Phase ${PHASE_NUM}: UI-SPEC generation did not produce output — continuing without design contract.` and proceed to 3b. NON-BLOCKING.
|
|
||||||
|
|
||||||
**3b. Plan**
|
**3b. Plan**
|
||||||
|
|
||||||
@@ -379,15 +352,9 @@ FLATTEN=$(gsd_run query dispatch-should-flatten --raw 2>/dev/null || echo "true"
|
|||||||
|
|
||||||
- **If `FLATTEN` is `false`:** Dispatch plan as a background agent to keep the main context lean. While plan runs, the workflow can immediately start discussing the next phase (see step 4).
|
- **If `FLATTEN` is `false`:** Dispatch plan as a background agent to keep the main context lean. While plan runs, the workflow can immediately start discussing the next phase (see step 4).
|
||||||
|
|
||||||
- If `PLAN_STRATEGY=converge`, print: `◆ Spawning background plan-convergence loop for phase ${PHASE_NUM}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)`
|
<!-- gsd:section id="converge-dispatch-bg" when="state:plan-strategy-converge" -->
|
||||||
|
If `section_manifest` is `null` or `"converge-dispatch-bg"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md`. Otherwise skip — do not read the file.
|
||||||
```
|
<!-- /gsd:section -->
|
||||||
Agent(
|
|
||||||
description="Plan convergence phase ${PHASE_NUM}: ${PHASE_NAME}",
|
|
||||||
run_in_background=true,
|
|
||||||
prompt="Run plan convergence for phase ${PHASE_NUM}: Skill(skill=\"gsd-plan-review-convergence\", args=\"${PHASE_NUM} ${CONVERGENCE_ARGS}\")"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
- Otherwise, print: `◆ Spawning background planner for phase ${PHASE_NUM}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)`
|
- Otherwise, print: `◆ Spawning background planner for phase ${PHASE_NUM}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)`
|
||||||
|
|
||||||
@@ -403,11 +370,9 @@ FLATTEN=$(gsd_run query dispatch-should-flatten --raw 2>/dev/null || echo "true"
|
|||||||
|
|
||||||
- **Otherwise (`FLATTEN` is `true` — run inline):** Run plan **inline** (do NOT background) so the plan-checker runs. The next phase's discuss does not overlap planning here — correctness over overlap.
|
- **Otherwise (`FLATTEN` is `true` — run inline):** Run plan **inline** (do NOT background) so the plan-checker runs. The next phase's discuss does not overlap planning here — correctness over overlap.
|
||||||
|
|
||||||
- If `PLAN_STRATEGY=converge`:
|
<!-- gsd:section id="converge-dispatch-inline" when="state:plan-strategy-converge" -->
|
||||||
|
If `section_manifest` is `null` or `"converge-dispatch-inline"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md`. Otherwise skip — do not read the file.
|
||||||
```
|
<!-- /gsd:section -->
|
||||||
Skill(skill="gsd-plan-review-convergence", args="${PHASE_NUM} ${CONVERGENCE_ARGS}")
|
|
||||||
```
|
|
||||||
|
|
||||||
- Otherwise (local planning):
|
- Otherwise (local planning):
|
||||||
|
|
||||||
@@ -417,11 +382,9 @@ FLATTEN=$(gsd_run query dispatch-should-flatten --raw 2>/dev/null || echo "true"
|
|||||||
|
|
||||||
**If `INTERACTIVE` is NOT set (default):** Run plan inline.
|
**If `INTERACTIVE` is NOT set (default):** Run plan inline.
|
||||||
|
|
||||||
If `PLAN_STRATEGY=converge`, run the convergence loop:
|
<!-- gsd:section id="converge-loop" when="state:plan-strategy-converge" -->
|
||||||
|
If `section_manifest` is `null` or `"converge-loop"` is in its `included` list: read and execute `gsd-core/workflows/autonomous/steps/converge-loop.md`. Otherwise skip — do not read the file.
|
||||||
```
|
<!-- /gsd:section -->
|
||||||
Skill(skill="gsd-plan-review-convergence", args="${PHASE_NUM} ${CONVERGENCE_ARGS}")
|
|
||||||
```
|
|
||||||
|
|
||||||
If `PLAN_STRATEGY=local`, run the regular planner:
|
If `PLAN_STRATEGY=local`, run the regular planner:
|
||||||
|
|
||||||
|
|||||||
1
gsd-core/workflows/autonomous/steps/converge-banner.md
Normal file
1
gsd-core/workflows/autonomous/steps/converge-banner.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
If `PLAN_STRATEGY` is `converge`, display: `Planning: Plan-review convergence enabled`
|
||||||
11
gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md
Normal file
11
gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
## Converge Dispatch (Background)
|
||||||
|
|
||||||
|
If `PLAN_STRATEGY=converge`, print: `◆ Spawning background plan-convergence loop for phase ${PHASE_NUM}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)`
|
||||||
|
|
||||||
|
```
|
||||||
|
Agent(
|
||||||
|
description="Plan convergence phase ${PHASE_NUM}: ${PHASE_NAME}",
|
||||||
|
run_in_background=true,
|
||||||
|
prompt="Run plan convergence for phase ${PHASE_NUM}: Skill(skill=\"gsd-plan-review-convergence\", args=\"${PHASE_NUM} ${CONVERGENCE_ARGS}\")"
|
||||||
|
)
|
||||||
|
```
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
## Converge Dispatch (Inline)
|
||||||
|
|
||||||
|
If `PLAN_STRATEGY=converge`:
|
||||||
|
|
||||||
|
```
|
||||||
|
Skill(skill="gsd-plan-review-convergence", args="${PHASE_NUM} ${CONVERGENCE_ARGS}")
|
||||||
|
```
|
||||||
21
gsd-core/workflows/autonomous/steps/converge-fail-fast.md
Normal file
21
gsd-core/workflows/autonomous/steps/converge-fail-fast.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
## Converge Fail-Fast
|
||||||
|
|
||||||
|
When `PLAN_STRATEGY` is `converge`, fail fast unless the existing convergence feature gate is enabled:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
||||||
|
if [ "$PLAN_STRATEGY" = "converge" ]; then
|
||||||
|
CONVERGENCE_ENABLED=$(gsd_run query config-get workflow.plan_review_convergence 2>/dev/null || echo "false")
|
||||||
|
if [ "$CONVERGENCE_ENABLED" != "true" ]; then
|
||||||
|
printf '%s\n' \
|
||||||
|
'gsd-autonomous --converge is disabled (workflow.plan_review_convergence=false).' \
|
||||||
|
'' \
|
||||||
|
'Enable plan convergence with:' \
|
||||||
|
'' \
|
||||||
|
' gsd config-set workflow.plan_review_convergence true' \
|
||||||
|
'' \
|
||||||
|
'Then re-run the autonomous command with --converge.'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
```
|
||||||
7
gsd-core/workflows/autonomous/steps/converge-loop.md
Normal file
7
gsd-core/workflows/autonomous/steps/converge-loop.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
## Converge Loop
|
||||||
|
|
||||||
|
If `PLAN_STRATEGY=converge`, run the convergence loop:
|
||||||
|
|
||||||
|
```
|
||||||
|
Skill(skill="gsd-plan-review-convergence", args="${PHASE_NUM} ${CONVERGENCE_ARGS}")
|
||||||
|
```
|
||||||
@@ -19,7 +19,33 @@ Parse arguments and load project state:
|
|||||||
```bash
|
```bash
|
||||||
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
||||||
PHASE_ARG="${1}"
|
PHASE_ARG="${1}"
|
||||||
INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
|
|
||||||
|
# Parse all code-review flags into a structured IR via code-review-flags.cjs.
|
||||||
|
# This is the canonical flag-parsing surface — do not replicate inline bash parsing
|
||||||
|
# for --fix/--all/--auto here; the module handles all flag extraction and implication
|
||||||
|
# logic (e.g., --all and --auto imply --fix). Resolved BEFORE the init call below so
|
||||||
|
# the section-manifest gate forwards the RESOLVED (post-implication) fix decision, not
|
||||||
|
# just a literal --fix token check.
|
||||||
|
FLAGS_JSON=$(node -e "
|
||||||
|
const { parseCodeReviewFlags } = require('./gsd-core/bin/lib/code-review-flags.cjs');
|
||||||
|
const flags = parseCodeReviewFlags(process.argv.slice(1));
|
||||||
|
process.stdout.write(JSON.stringify(flags));
|
||||||
|
" -- "$@" 2>/dev/null)
|
||||||
|
|
||||||
|
# Extract individual flag values from the IR
|
||||||
|
FIX_FLAG=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).fix))")
|
||||||
|
FIX_ALL=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).all))")
|
||||||
|
FIX_AUTO=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).auto))")
|
||||||
|
DEPTH_OVERRIDE=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).depth)")
|
||||||
|
FILES_OVERRIDE=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).files)")
|
||||||
|
|
||||||
|
# Forward the resolved fix decision (--fix itself, or --all/--auto implying it) to
|
||||||
|
# init.code-review so section_manifest's flag:--fix gating (dispatch-fix section)
|
||||||
|
# matches code-review-flags.cjs's own implication logic rather than a raw token scan.
|
||||||
|
FIX_PARAM=""
|
||||||
|
if [ "$FIX_FLAG" = "true" ]; then FIX_PARAM="--fix"; fi
|
||||||
|
|
||||||
|
INIT=$(gsd_run query init.code-review "${PHASE_ARG}" $FIX_PARAM)
|
||||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||||
AGENT_SKILLS_REVIEWER=$(gsd_run query agent-skills gsd-code-reviewer)
|
AGENT_SKILLS_REVIEWER=$(gsd_run query agent-skills gsd-code-reviewer)
|
||||||
# #2072: resolve the routed model so model_overrides / models.verification are honored
|
# #2072: resolve the routed model so model_overrides / models.verification are honored
|
||||||
@@ -27,7 +53,7 @@ AGENT_SKILLS_REVIEWER=$(gsd_run query agent-skills gsd-code-reviewer)
|
|||||||
REVIEWER_MODEL=$(gsd_run query resolve-model gsd-code-reviewer --raw)
|
REVIEWER_MODEL=$(gsd_run query resolve-model gsd-code-reviewer --raw)
|
||||||
```
|
```
|
||||||
|
|
||||||
Parse from init JSON: `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `padded_phase`, `commit_docs`.
|
Parse from init JSON: `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `padded_phase`, `commit_docs`, `fallow_enabled`, `fallow_scope`, `fallow_profile`, `fallow_mcp`, `fallow_max_crap`.
|
||||||
|
|
||||||
**Input sanitization (defense-in-depth):**
|
**Input sanitization (defense-in-depth):**
|
||||||
```bash
|
```bash
|
||||||
@@ -46,27 +72,6 @@ Error: Phase ${PHASE_ARG} not found. Run /gsd:progress to see available phases.
|
|||||||
|
|
||||||
This runs BEFORE config gate check so user errors are surfaced immediately regardless of config state.
|
This runs BEFORE config gate check so user errors are surfaced immediately regardless of config state.
|
||||||
|
|
||||||
Parse optional flags from $ARGUMENTS using the typed flag parser:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Parse all code-review flags into a structured IR via code-review-flags.cjs.
|
|
||||||
# This is the canonical flag-parsing surface — do not replicate inline bash parsing
|
|
||||||
# for --fix/--all/--auto here; the module handles all flag extraction and implication
|
|
||||||
# logic (e.g., --all and --auto imply --fix).
|
|
||||||
FLAGS_JSON=$(node -e "
|
|
||||||
const { parseCodeReviewFlags } = require('./gsd-core/bin/lib/code-review-flags.cjs');
|
|
||||||
const flags = parseCodeReviewFlags(process.argv.slice(1));
|
|
||||||
process.stdout.write(JSON.stringify(flags));
|
|
||||||
" -- "$@" 2>/dev/null)
|
|
||||||
|
|
||||||
# Extract individual flag values from the IR
|
|
||||||
FIX_FLAG=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).fix))")
|
|
||||||
FIX_ALL=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).all))")
|
|
||||||
FIX_AUTO=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).auto))")
|
|
||||||
DEPTH_OVERRIDE=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).depth)")
|
|
||||||
FILES_OVERRIDE=$(echo "$FLAGS_JSON" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')).files)")
|
|
||||||
```
|
|
||||||
|
|
||||||
If FILES_OVERRIDE is set, split by comma into array:
|
If FILES_OVERRIDE is set, split by comma into array:
|
||||||
```bash
|
```bash
|
||||||
if [ -n "$FILES_OVERRIDE" ]; then
|
if [ -n "$FILES_OVERRIDE" ]; then
|
||||||
@@ -389,20 +394,7 @@ Exit workflow. Do NOT spawn agent or create REVIEW.md.
|
|||||||
<step name="structural_pre_pass">
|
<step name="structural_pre_pass">
|
||||||
Optional structural cross-module pass powered by fallow.
|
Optional structural cross-module pass powered by fallow.
|
||||||
|
|
||||||
Read fallow config gates:
|
Parse `fallow_enabled`, `fallow_scope`, `fallow_profile`, `fallow_mcp`, `fallow_max_crap` from the init JSON as `FALLOW_ENABLED`, `FALLOW_SCOPE`, `FALLOW_PROFILE`, `FALLOW_MCP`, `FALLOW_MAX_CRAP`. These are resolved once by `init.code-review` at init time — consuming the pre-resolved values here (instead of a `config-get` call inside this step) avoids gating this section's own inclusion on a fact its own body would otherwise compute (see `state:fallow-enabled` in docs/reference/workflow-fragments.md).
|
||||||
```bash
|
|
||||||
FALLOW_ENABLED=$(gsd_run query config-get code_quality.fallow.enabled 2>/dev/null || echo "false")
|
|
||||||
FALLOW_SCOPE=$(gsd_run query config-get code_quality.fallow.scope 2>/dev/null || echo "phase")
|
|
||||||
FALLOW_PROFILE=$(gsd_run query config-get code_quality.fallow.profile 2>/dev/null || echo "standard")
|
|
||||||
FALLOW_MCP=$(gsd_run query config-get code_quality.fallow.mcp 2>/dev/null || echo "false")
|
|
||||||
# profile maps to a --max-crap threshold since fallow has no native profile concept.
|
|
||||||
# minimal=50 (more lenient), standard=30 (default), strict=15 (tighter).
|
|
||||||
case "$FALLOW_PROFILE" in
|
|
||||||
minimal) FALLOW_MAX_CRAP=50 ;;
|
|
||||||
strict) FALLOW_MAX_CRAP=15 ;;
|
|
||||||
*) FALLOW_MAX_CRAP=30 ;; # standard (default)
|
|
||||||
esac
|
|
||||||
```
|
|
||||||
|
|
||||||
Defaults are fail-closed and opt-in:
|
Defaults are fail-closed and opt-in:
|
||||||
- `enabled=false` (skip entirely)
|
- `enabled=false` (skip entirely)
|
||||||
@@ -410,88 +402,9 @@ Defaults are fail-closed and opt-in:
|
|||||||
- `profile=standard` (maps to `--max-crap 30`; minimal=50, standard=30, strict=15 — fallow has no native profile concept)
|
- `profile=standard` (maps to `--max-crap 30`; minimal=50, standard=30, strict=15 — fallow has no native profile concept)
|
||||||
- `mcp=false`
|
- `mcp=false`
|
||||||
|
|
||||||
When `FALLOW_ENABLED=true`:
|
<!-- gsd:section id="structural-pre-pass" when="state:fallow-enabled" -->
|
||||||
|
If `section_manifest` is `null` or `"structural-pre-pass"` is in its `included` list: read and execute `gsd-core/workflows/code-review/steps/structural-pre-pass.md`. Otherwise skip — do not read the file.
|
||||||
1) Resolve binary via PATH first, then `node_modules/.bin/fallow`.
|
<!-- /gsd:section -->
|
||||||
```bash
|
|
||||||
FALLOW_BIN=$(FALLOW_CWD="$(pwd)" node -e "
|
|
||||||
const { resolveFallowBinary } = require('./gsd-core/bin/lib/fallow-runner.cjs');
|
|
||||||
const resolved = resolveFallowBinary({ cwd: process.env.FALLOW_CWD });
|
|
||||||
if (resolved) process.stdout.write(resolved);
|
|
||||||
")
|
|
||||||
```
|
|
||||||
|
|
||||||
2) If binary is missing, fail with actionable message:
|
|
||||||
```bash
|
|
||||||
if [ -z \"$FALLOW_BIN\" ]; then
|
|
||||||
echo \"Error: fallow is enabled but no binary was found.\"
|
|
||||||
echo \"Install fallow via \`npm install -D fallow\` or \`cargo install fallow\`.\"
|
|
||||||
# Exit workflow
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
3) Execute structural pass and persist JSON (bounded at 120s). Note: `fallow audit` exits 0 when clean and 1 when issues are found — BOTH are successful runs. Only a timeout (124), usage error (2), or crash yields no usable JSON; success is decided by whether the output parses as a valid fallow report, not by exit code:
|
|
||||||
```bash
|
|
||||||
FALLOW_JSON_PATH="${PHASE_DIR}/FALLOW.json"
|
|
||||||
FALLOW_STDERR_TMP=$(mktemp)
|
|
||||||
|
|
||||||
# Phase scope uses fallow's native changed-files scoping (--changed-since <base>).
|
|
||||||
# Derive the phase base commit; if none is found, fall back to repo scope (fallow
|
|
||||||
# auto-detects the base branch).
|
|
||||||
FALLOW_SCOPE_ARGS=()
|
|
||||||
if [ \"$FALLOW_SCOPE\" = \"phase\" ]; then
|
|
||||||
FALLOW_PHASE_COMMITS=$(git log --oneline --all --grep=\"${PADDED_PHASE}\" --format=\"%H\" 2>/dev/null)
|
|
||||||
if [ -n \"$FALLOW_PHASE_COMMITS\" ]; then
|
|
||||||
FALLOW_BASE=$(echo \"$FALLOW_PHASE_COMMITS\" | tail -1)^
|
|
||||||
FALLOW_SCOPE_ARGS=(--changed-since \"$FALLOW_BASE\")
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
gsd_run run-with-timeout 120 -- \"$FALLOW_BIN\" audit --format json --quiet --max-crap \"$FALLOW_MAX_CRAP\" \"${FALLOW_SCOPE_ARGS[@]+\"${FALLOW_SCOPE_ARGS[@]}\"}\" > \"${FALLOW_JSON_PATH}.tmp\" 2>\"$FALLOW_STDERR_TMP\"
|
|
||||||
FALLOW_EXIT=$?
|
|
||||||
|
|
||||||
# fallow exits 0 (clean) or 1 (issues found) — BOTH are successful runs that produce a
|
|
||||||
# valid JSON report. Only a timeout (124), usage error (2), or crash yields no usable JSON.
|
|
||||||
# Decide success by whether the output parses as a fallow report, not by exit code.
|
|
||||||
FALLOW_OK=$(FALLOW_TMP=\"${FALLOW_JSON_PATH}.tmp\" node -e \"
|
|
||||||
try {
|
|
||||||
const fs = require('fs');
|
|
||||||
const txt = fs.readFileSync(process.env.FALLOW_TMP, 'utf8');
|
|
||||||
const o = JSON.parse(txt);
|
|
||||||
process.stdout.write(o && typeof o === 'object' && 'verdict' in o ? '1' : '0');
|
|
||||||
} catch { process.stdout.write('0'); }
|
|
||||||
\")
|
|
||||||
if [ \"$FALLOW_OK\" != \"1\" ]; then
|
|
||||||
FALLOW_STDERR_SUMMARY=$(head -5 \"$FALLOW_STDERR_TMP\")
|
|
||||||
rm -f \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_STDERR_TMP\"
|
|
||||||
# #2667: distinguish a hard EXECUTION failure (the binary was found at step 1
|
|
||||||
# but would not run) from the binary-missing path (step 2). Exit 124 = timeout,
|
|
||||||
# 2 = usage error, 125 = spawn failure (e.g. Windows EINVAL on a .cmd shim —
|
|
||||||
# CVE-2024-27980, now mediated by run-with-timeout), 126/127 = not executable /
|
|
||||||
# not found. A non-zero exit here with a resolved binary means fallow is
|
|
||||||
# installed but did not produce a report — surface that loudly so a Windows
|
|
||||||
# user does not mistake it for "fallow absent".
|
|
||||||
case \"$FALLOW_EXIT\" in
|
|
||||||
124) FALLOW_FAIL_KIND=\"timed out\" ;;
|
|
||||||
2) FALLOW_FAIL_KIND=\"usage error\" ;;
|
|
||||||
125) FALLOW_FAIL_KIND=\"spawn failure (the binary was found but did not start — e.g. a Windows .cmd shim; run-with-timeout mediates this)\" ;;
|
|
||||||
126) FALLOW_FAIL_KIND=\"not executable\" ;;
|
|
||||||
127) FALLOW_FAIL_KIND=\"not found\" ;;
|
|
||||||
*) FALLOW_FAIL_KIND=\"crashed\" ;;
|
|
||||||
esac
|
|
||||||
echo \"WARNING: fallow structural pre-pass failed (${FALLOW_FAIL_KIND}, exit ${FALLOW_EXIT}): ${FALLOW_STDERR_SUMMARY}\"
|
|
||||||
FALLOW_JSON_PATH=\"\"
|
|
||||||
else
|
|
||||||
mv \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_JSON_PATH\"
|
|
||||||
rm -f \"$FALLOW_STDERR_TMP\"
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
On any failure of the structural pre-pass (binary missing at step 2, or an execution failure here — timeout, spawn failure, crash, empty output, or unparseable JSON), the workflow continues with no `<structural_findings>` injection; the reviewer agent receives a normal review request. The WARNING above names the failure KIND so a hard execution failure (e.g. a Windows `.cmd` spawn failure) is not mistaken for an absent optional dependency.
|
|
||||||
|
|
||||||
4) Optional MCP bridge path (runtime-dependent):
|
|
||||||
- If `FALLOW_MCP=true`, set reviewer input mode to MCP-backed structural findings.
|
|
||||||
- Otherwise pass static JSON findings from `FALLOW.json`.
|
|
||||||
|
|
||||||
When disabled, set:
|
When disabled, set:
|
||||||
```bash
|
```bash
|
||||||
@@ -636,45 +549,9 @@ fi
|
|||||||
```
|
```
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="dispatch_fix">
|
<!-- gsd:section id="dispatch-fix" when="flag:--fix" -->
|
||||||
If the `--fix` flag was passed (`FIX_FLAG=true`), delegate to the `code-review-fix.md` workflow
|
If `section_manifest` is `null` or `"dispatch-fix"` is in its `included` list: read and execute `gsd-core/workflows/code-review/steps/dispatch-fix.md`. Otherwise skip — do not read the file; proceed to `present_results`.
|
||||||
to auto-apply findings from the REVIEW.md that was just written (or that already existed).
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
This step runs AFTER `commit_review` so REVIEW.md is guaranteed to be on disk before the fixer
|
|
||||||
is invoked. If REVIEW.md was not created (agent failed, scope was empty, etc.), the `code-review-fix.md`
|
|
||||||
workflow handles the missing-review error and exits cleanly.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
if [ "$FIX_FLAG" = "true" ]; then
|
|
||||||
echo ""
|
|
||||||
echo "─────────────────────────────────────────────────────────────────"
|
|
||||||
echo " --fix: delegating to code-review-fix.md"
|
|
||||||
echo "─────────────────────────────────────────────────────────────────"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Build the fix sub-arguments: pass phase arg plus any --all/--auto flags
|
|
||||||
FIX_ARGS="${PHASE_ARG}"
|
|
||||||
if [ "$FIX_ALL" = "true" ]; then
|
|
||||||
FIX_ARGS="${FIX_ARGS} --all"
|
|
||||||
fi
|
|
||||||
if [ "$FIX_AUTO" = "true" ]; then
|
|
||||||
FIX_ARGS="${FIX_ARGS} --auto"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Load and execute the code-review-fix workflow.
|
|
||||||
# The fix workflow is the canonical implementation for all fix logic:
|
|
||||||
# gsd-code-fixer agent dispatch, --auto iteration loop, REVIEW-FIX.md commit,
|
|
||||||
# and result presentation. Do not duplicate that logic here.
|
|
||||||
Workflow(workflow="gsd-core/workflows/code-review-fix.md", args="${FIX_ARGS}")
|
|
||||||
|
|
||||||
# Exit after fix workflow completes — present_results is for review-only output.
|
|
||||||
# The fix workflow has its own present_results step.
|
|
||||||
# Exit workflow.
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
If `FIX_FLAG` is false, skip this step entirely and proceed to `present_results`.
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="present_results">
|
<step name="present_results">
|
||||||
Read the REVIEW.md YAML frontmatter to extract finding counts.
|
Read the REVIEW.md YAML frontmatter to extract finding counts.
|
||||||
|
|||||||
39
gsd-core/workflows/code-review/steps/dispatch-fix.md
Normal file
39
gsd-core/workflows/code-review/steps/dispatch-fix.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<step name="dispatch_fix">
|
||||||
|
If the `--fix` flag was passed (`FIX_FLAG=true`), delegate to the `code-review-fix.md` workflow
|
||||||
|
to auto-apply findings from the REVIEW.md that was just written (or that already existed).
|
||||||
|
|
||||||
|
This step runs AFTER `commit_review` so REVIEW.md is guaranteed to be on disk before the fixer
|
||||||
|
is invoked. If REVIEW.md was not created (agent failed, scope was empty, etc.), the `code-review-fix.md`
|
||||||
|
workflow handles the missing-review error and exits cleanly.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
if [ "$FIX_FLAG" = "true" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "─────────────────────────────────────────────────────────────────"
|
||||||
|
echo " --fix: delegating to code-review-fix.md"
|
||||||
|
echo "─────────────────────────────────────────────────────────────────"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Build the fix sub-arguments: pass phase arg plus any --all/--auto flags
|
||||||
|
FIX_ARGS="${PHASE_ARG}"
|
||||||
|
if [ "$FIX_ALL" = "true" ]; then
|
||||||
|
FIX_ARGS="${FIX_ARGS} --all"
|
||||||
|
fi
|
||||||
|
if [ "$FIX_AUTO" = "true" ]; then
|
||||||
|
FIX_ARGS="${FIX_ARGS} --auto"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load and execute the code-review-fix workflow.
|
||||||
|
# The fix workflow is the canonical implementation for all fix logic:
|
||||||
|
# gsd-code-fixer agent dispatch, --auto iteration loop, REVIEW-FIX.md commit,
|
||||||
|
# and result presentation. Do not duplicate that logic here.
|
||||||
|
Workflow(workflow="gsd-core/workflows/code-review-fix.md", args="${FIX_ARGS}")
|
||||||
|
|
||||||
|
# Exit after fix workflow completes — present_results is for review-only output.
|
||||||
|
# The fix workflow has its own present_results step.
|
||||||
|
# Exit workflow.
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
If `FIX_FLAG` is false, skip this step entirely and proceed to `present_results`.
|
||||||
|
</step>
|
||||||
83
gsd-core/workflows/code-review/steps/structural-pre-pass.md
Normal file
83
gsd-core/workflows/code-review/steps/structural-pre-pass.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
When `FALLOW_ENABLED=true`:
|
||||||
|
|
||||||
|
1) Resolve binary via PATH first, then `node_modules/.bin/fallow`.
|
||||||
|
```bash
|
||||||
|
FALLOW_BIN=$(FALLOW_CWD="$(pwd)" node -e "
|
||||||
|
const { resolveFallowBinary } = require('./gsd-core/bin/lib/fallow-runner.cjs');
|
||||||
|
const resolved = resolveFallowBinary({ cwd: process.env.FALLOW_CWD });
|
||||||
|
if (resolved) process.stdout.write(resolved);
|
||||||
|
")
|
||||||
|
```
|
||||||
|
|
||||||
|
2) If binary is missing, fail with actionable message:
|
||||||
|
```bash
|
||||||
|
if [ -z \"$FALLOW_BIN\" ]; then
|
||||||
|
echo \"Error: fallow is enabled but no binary was found.\"
|
||||||
|
echo \"Install fallow via \`npm install -D fallow\` or \`cargo install fallow\`.\"
|
||||||
|
# Exit workflow
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
3) Execute structural pass and persist JSON (bounded at 120s). Note: `fallow audit` exits 0 when clean and 1 when issues are found — BOTH are successful runs. Only a timeout (124), usage error (2), or crash yields no usable JSON; success is decided by whether the output parses as a valid fallow report, not by exit code:
|
||||||
|
```bash
|
||||||
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
||||||
|
FALLOW_JSON_PATH="${PHASE_DIR}/FALLOW.json"
|
||||||
|
FALLOW_STDERR_TMP=$(mktemp)
|
||||||
|
|
||||||
|
# Phase scope uses fallow's native changed-files scoping (--changed-since <base>).
|
||||||
|
# Derive the phase base commit; if none is found, fall back to repo scope (fallow
|
||||||
|
# auto-detects the base branch).
|
||||||
|
FALLOW_SCOPE_ARGS=()
|
||||||
|
if [ \"$FALLOW_SCOPE\" = \"phase\" ]; then
|
||||||
|
FALLOW_PHASE_COMMITS=$(git log --oneline --all --grep=\"${PADDED_PHASE}\" --format=\"%H\" 2>/dev/null)
|
||||||
|
if [ -n \"$FALLOW_PHASE_COMMITS\" ]; then
|
||||||
|
FALLOW_BASE=$(echo \"$FALLOW_PHASE_COMMITS\" | tail -1)^
|
||||||
|
FALLOW_SCOPE_ARGS=(--changed-since \"$FALLOW_BASE\")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
gsd_run run-with-timeout 120 -- \"$FALLOW_BIN\" audit --format json --quiet --max-crap \"$FALLOW_MAX_CRAP\" \"${FALLOW_SCOPE_ARGS[@]+\"${FALLOW_SCOPE_ARGS[@]}\"}\" > \"${FALLOW_JSON_PATH}.tmp\" 2>\"$FALLOW_STDERR_TMP\"
|
||||||
|
FALLOW_EXIT=$?
|
||||||
|
|
||||||
|
# fallow exits 0 (clean) or 1 (issues found) — BOTH are successful runs that produce a
|
||||||
|
# valid JSON report. Only a timeout (124), usage error (2), or crash yields no usable JSON.
|
||||||
|
# Decide success by whether the output parses as a fallow report, not by exit code.
|
||||||
|
FALLOW_OK=$(FALLOW_TMP=\"${FALLOW_JSON_PATH}.tmp\" node -e \"
|
||||||
|
try {
|
||||||
|
const fs = require('fs');
|
||||||
|
const txt = fs.readFileSync(process.env.FALLOW_TMP, 'utf8');
|
||||||
|
const o = JSON.parse(txt);
|
||||||
|
process.stdout.write(o && typeof o === 'object' && 'verdict' in o ? '1' : '0');
|
||||||
|
} catch { process.stdout.write('0'); }
|
||||||
|
\")
|
||||||
|
if [ \"$FALLOW_OK\" != \"1\" ]; then
|
||||||
|
FALLOW_STDERR_SUMMARY=$(head -5 \"$FALLOW_STDERR_TMP\")
|
||||||
|
rm -f \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_STDERR_TMP\"
|
||||||
|
# #2667: distinguish a hard EXECUTION failure (the binary was found at step 1
|
||||||
|
# but would not run) from the binary-missing path (step 2). Exit 124 = timeout,
|
||||||
|
# 2 = usage error, 125 = spawn failure (e.g. Windows EINVAL on a .cmd shim —
|
||||||
|
# CVE-2024-27980, now mediated by run-with-timeout), 126/127 = not executable /
|
||||||
|
# not found. A non-zero exit here with a resolved binary means fallow is
|
||||||
|
# installed but did not produce a report — surface that loudly so a Windows
|
||||||
|
# user does not mistake it for "fallow absent".
|
||||||
|
case \"$FALLOW_EXIT\" in
|
||||||
|
124) FALLOW_FAIL_KIND=\"timed out\" ;;
|
||||||
|
2) FALLOW_FAIL_KIND=\"usage error\" ;;
|
||||||
|
125) FALLOW_FAIL_KIND=\"spawn failure (the binary was found but did not start — e.g. a Windows .cmd shim; run-with-timeout mediates this)\" ;;
|
||||||
|
126) FALLOW_FAIL_KIND=\"not executable\" ;;
|
||||||
|
127) FALLOW_FAIL_KIND=\"not found\" ;;
|
||||||
|
*) FALLOW_FAIL_KIND=\"crashed\" ;;
|
||||||
|
esac
|
||||||
|
echo \"WARNING: fallow structural pre-pass failed (${FALLOW_FAIL_KIND}, exit ${FALLOW_EXIT}): ${FALLOW_STDERR_SUMMARY}\"
|
||||||
|
FALLOW_JSON_PATH=\"\"
|
||||||
|
else
|
||||||
|
mv \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_JSON_PATH\"
|
||||||
|
rm -f \"$FALLOW_STDERR_TMP\"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
On any failure of the structural pre-pass (binary missing at step 2, or an execution failure here — timeout, spawn failure, crash, empty output, or unparseable JSON), the workflow continues with no `<structural_findings>` injection; the reviewer agent receives a normal review request. The WARNING above names the failure KIND so a hard execution failure (e.g. a Windows `.cmd` spawn failure) is not mistaken for an absent optional dependency.
|
||||||
|
|
||||||
|
4) Optional MCP bridge path (runtime-dependent):
|
||||||
|
- If `FALLOW_MCP=true`, set reviewer input mode to MCP-backed structural findings.
|
||||||
|
- Otherwise pass static JSON findings from `FALLOW.json`.
|
||||||
@@ -592,9 +592,11 @@ Use `init milestone-op` for context, or load config directly:
|
|||||||
```bash
|
```bash
|
||||||
INIT=$(gsd_run query init.execute-phase "1")
|
INIT=$(gsd_run query init.execute-phase "1")
|
||||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||||
|
INIT_CM=$(gsd_run query init.complete-milestone)
|
||||||
|
if [[ "$INIT_CM" == @file:* ]]; then INIT_CM=$(cat "${INIT_CM#@file:}"); fi
|
||||||
```
|
```
|
||||||
|
|
||||||
Extract `branching_strategy`, `phase_branch_template`, `milestone_branch_template`, and `commit_docs` from init JSON.
|
Extract `branching_strategy`, `phase_branch_template`, `milestone_branch_template`, and `commit_docs` from init JSON. Extract `git_create_tag` and `section_manifest` from `INIT_CM` (used by the `git_tag` step below).
|
||||||
|
|
||||||
Detect base branch:
|
Detect base branch:
|
||||||
```bash
|
```bash
|
||||||
@@ -711,40 +713,9 @@ fi
|
|||||||
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="git_tag">
|
<!-- gsd:section id="git-tag" when="state:git-create-tag" -->
|
||||||
|
If `section_manifest` is `null` or `"git-tag"` is in its `included` list: read and execute `gsd-core/workflows/complete-milestone/steps/git-tag.md`. Otherwise skip — do not read the file; proceed to `git_commit_milestone`.
|
||||||
<config-check>
|
<!-- /gsd:section -->
|
||||||
Read `git.create_tag` via `gsd-tools.cjs query config-get git.create_tag 2>/dev/null || echo "true"`.
|
|
||||||
If the result is `false` → skip this step entirely and proceed to `git_commit_milestone`.
|
|
||||||
</config-check>
|
|
||||||
|
|
||||||
Create git tag:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Pre-check: skip if tag already exists (prevents silent failure on retry)
|
|
||||||
if git rev-parse "v[X.Y]" >/dev/null 2>&1; then echo "Tag v[X.Y] already exists, skipping"; exit 0; fi
|
|
||||||
git tag -a v[X.Y] -m "v[X.Y] [Name]
|
|
||||||
|
|
||||||
Delivered: [One sentence]
|
|
||||||
|
|
||||||
Key accomplishments:
|
|
||||||
- [Item 1]
|
|
||||||
- [Item 2]
|
|
||||||
- [Item 3]
|
|
||||||
|
|
||||||
See .planning/MILESTONES.md for full details."
|
|
||||||
```
|
|
||||||
|
|
||||||
Confirm: "Tagged: v[X.Y]"
|
|
||||||
|
|
||||||
Ask: "Push tag to remote? (y/n)"
|
|
||||||
|
|
||||||
If yes:
|
|
||||||
```bash
|
|
||||||
git push origin v[X.Y]
|
|
||||||
```
|
|
||||||
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="git_commit_milestone">
|
<step name="git_commit_milestone">
|
||||||
|
|
||||||
|
|||||||
29
gsd-core/workflows/complete-milestone/steps/git-tag.md
Normal file
29
gsd-core/workflows/complete-milestone/steps/git-tag.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<step name="git_tag">
|
||||||
|
|
||||||
|
Create git tag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pre-check: skip if tag already exists (prevents silent failure on retry)
|
||||||
|
if git rev-parse "v[X.Y]" >/dev/null 2>&1; then echo "Tag v[X.Y] already exists, skipping"; exit 0; fi
|
||||||
|
git tag -a v[X.Y] -m "v[X.Y] [Name]
|
||||||
|
|
||||||
|
Delivered: [One sentence]
|
||||||
|
|
||||||
|
Key accomplishments:
|
||||||
|
- [Item 1]
|
||||||
|
- [Item 2]
|
||||||
|
- [Item 3]
|
||||||
|
|
||||||
|
See .planning/MILESTONES.md for full details."
|
||||||
|
```
|
||||||
|
|
||||||
|
Confirm: "Tagged: v[X.Y]"
|
||||||
|
|
||||||
|
Ask: "Push tag to remote? (y/n)"
|
||||||
|
|
||||||
|
If yes:
|
||||||
|
```bash
|
||||||
|
git push origin v[X.Y]
|
||||||
|
```
|
||||||
|
|
||||||
|
</step>
|
||||||
@@ -64,7 +64,8 @@ Phase number from argument (required).
|
|||||||
```bash
|
```bash
|
||||||
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
||||||
RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --default "" 2>/dev/null || echo "")
|
RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --default "" 2>/dev/null || echo "")
|
||||||
INIT=$(gsd_run query init.phase-op "${PHASE}")
|
AUTO_PARAM=""; if [[ "$ARGUMENTS" =~ (^|[[:space:]])--auto([[:space:]]|$) ]]; then AUTO_PARAM="--auto"; fi
|
||||||
|
INIT=$(gsd_run query init.discuss-phase-assumptions "${PHASE}" $AUTO_PARAM)
|
||||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||||
AGENT_SKILLS_ANALYZER=$(gsd_run query agent-skills gsd-assumptions-analyzer)
|
AGENT_SKILLS_ANALYZER=$(gsd_run query agent-skills gsd-assumptions-analyzer)
|
||||||
# #2072: resolve the routed model so model_overrides / models.discuss are honored
|
# #2072: resolve the routed model so model_overrides / models.discuss are honored
|
||||||
@@ -649,21 +650,9 @@ Check for auto-advance trigger:
|
|||||||
gsd_run query config-set workflow._auto_chain_active true
|
gsd_run query config-set workflow._auto_chain_active true
|
||||||
```
|
```
|
||||||
|
|
||||||
**If `--auto` flag present OR `AUTO_MODE` is true:**
|
<!-- gsd:section id="auto-advance-dispatch" when="state:auto-advance-active" -->
|
||||||
|
If `section_manifest` is `null` or `"auto-advance-dispatch"` is in its `included` list: read and execute `gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md`. Otherwise skip — do not read the file.
|
||||||
Display banner:
|
<!-- /gsd:section -->
|
||||||
```text
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
GSD ► AUTO-ADVANCING TO PLAN
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
|
|
||||||
Context captured (assumptions mode). Launching plan-phase...
|
|
||||||
```
|
|
||||||
|
|
||||||
Launch: `Skill(skill="gsd-plan-phase", args="${PHASE} --auto")`
|
|
||||||
|
|
||||||
Handle return: PHASE COMPLETE / PLANNING COMPLETE / INCONCLUSIVE / GAPS FOUND
|
|
||||||
(identical handling to discuss-phase.md auto_advance step)
|
|
||||||
|
|
||||||
**If neither `--auto` nor config enabled:**
|
**If neither `--auto` nor config enabled:**
|
||||||
End here — `confirm_creation` already ran; do not route back to it.
|
End here — `confirm_creation` already ran; do not route back to it.
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
**If `--auto` flag present OR `AUTO_MODE` is true:**
|
||||||
|
|
||||||
|
Display banner:
|
||||||
|
```text
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
GSD ► AUTO-ADVANCING TO PLAN
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
Context captured (assumptions mode). Launching plan-phase...
|
||||||
|
```
|
||||||
|
|
||||||
|
Launch: `Skill(skill="gsd-plan-phase", args="${PHASE} --auto")`
|
||||||
|
|
||||||
|
Handle return: PHASE COMPLETE / PLANNING COMPLETE / INCONCLUSIVE / GAPS FOUND
|
||||||
|
(identical handling to discuss-phase.md auto_advance step)
|
||||||
@@ -18,6 +18,10 @@ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-pars
|
|||||||
INIT=$(gsd_run query docs-init)
|
INIT=$(gsd_run query docs-init)
|
||||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||||
AGENT_SKILLS=$(gsd_run query agent-skills gsd-doc-writer)
|
AGENT_SKILLS=$(gsd_run query agent-skills gsd-doc-writer)
|
||||||
|
# #2994: dedicated init.docs-update call — additive to docs-init above, carries
|
||||||
|
# only the section_manifest field (gates dispatch_monorepo_packages).
|
||||||
|
INIT_DOCS_UPDATE=$(gsd_run query init.docs-update)
|
||||||
|
if [[ "$INIT_DOCS_UPDATE" == @file:* ]]; then INIT_DOCS_UPDATE=$(cat "${INIT_DOCS_UPDATE#@file:}"); fi
|
||||||
```
|
```
|
||||||
|
|
||||||
Extract from init JSON:
|
Extract from init JSON:
|
||||||
@@ -27,6 +31,7 @@ Extract from init JSON:
|
|||||||
- `project_type` — object with boolean signals: `has_package_json`, `has_api_routes`, `has_cli_bin`, `is_open_source`, `has_deploy_config`, `is_monorepo`, `has_tests`
|
- `project_type` — object with boolean signals: `has_package_json`, `has_api_routes`, `has_cli_bin`, `is_open_source`, `has_deploy_config`, `is_monorepo`, `has_tests`
|
||||||
- `doc_tooling` — object with booleans: `docusaurus`, `vitepress`, `mkdocs`, `storybook`
|
- `doc_tooling` — object with booleans: `docusaurus`, `vitepress`, `mkdocs`, `storybook`
|
||||||
- `monorepo_workspaces` — array of workspace glob patterns (empty if not a monorepo)
|
- `monorepo_workspaces` — array of workspace glob patterns (empty if not a monorepo)
|
||||||
|
- `section_manifest` — parsed from `INIT_DOCS_UPDATE` (not `INIT`); gates the `dispatch-monorepo-packages` section below
|
||||||
- `project_root` — absolute path to the project root
|
- `project_root` — absolute path to the project root
|
||||||
- `response_language` — if set, present all user-facing questions, prompts, and explanations in this workflow in that language; technical terms, code, file paths, and subagent prompts stay in English
|
- `response_language` — if set, present all user-facing questions, prompts, and explanations in this workflow in that language; technical terms, code, file paths, and subagent prompts stay in English
|
||||||
</step>
|
</step>
|
||||||
@@ -710,57 +715,9 @@ If any agent failed or its file is missing, note the failure and continue. Missi
|
|||||||
Continue to dispatch_monorepo_packages (if monorepo_workspaces is non-empty) or commit_docs.
|
Continue to dispatch_monorepo_packages (if monorepo_workspaces is non-empty) or commit_docs.
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="dispatch_monorepo_packages" condition="monorepo_workspaces is non-empty">
|
<!-- gsd:section id="dispatch-monorepo-packages" when="state:is-monorepo" -->
|
||||||
After Wave 2 collection, generate per-package READMEs for each monorepo workspace.
|
If `section_manifest` (from `INIT_DOCS_UPDATE`) is `null` or `"dispatch-monorepo-packages"` is in its `included` list: read and execute `gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md`. Otherwise skip — do not read the file; continue to commit_docs.
|
||||||
|
<!-- /gsd:section -->
|
||||||
**Condition:** Only run this step if `monorepo_workspaces` from the init JSON is non-empty.
|
|
||||||
|
|
||||||
**Resolve workspace packages from glob patterns:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Expand workspace globs to actual package directories
|
|
||||||
for pattern in {monorepo_workspaces}; do
|
|
||||||
ls -d $pattern 2>/dev/null
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
**For each resolved directory that contains a `package.json`:**
|
|
||||||
|
|
||||||
Determine mode:
|
|
||||||
- If `{package_dir}/README.md` exists: mode = `update`, read existing content
|
|
||||||
- Else: mode = `create`
|
|
||||||
|
|
||||||
Spawn a `gsd-doc-writer` agent with `run_in_background=true`:
|
|
||||||
|
|
||||||
```
|
|
||||||
Agent(
|
|
||||||
subagent_type="gsd-doc-writer",
|
|
||||||
model="{doc_writer_model}",
|
|
||||||
run_in_background=true,
|
|
||||||
description="Generate per-package README for {package_dir}",
|
|
||||||
prompt="<doc_assignment>
|
|
||||||
type: readme
|
|
||||||
mode: {create|update}
|
|
||||||
scope: per_package
|
|
||||||
package_dir: {absolute path to package directory}
|
|
||||||
project_context: {INIT JSON with project_root set to package directory}
|
|
||||||
{existing_content: | (include full README.md content here if mode is update, else omit)}
|
|
||||||
</doc_assignment>
|
|
||||||
|
|
||||||
{AGENT_SKILLS}
|
|
||||||
|
|
||||||
Write {package_dir}/README.md directly. Return confirmation only — do not return doc content."
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
> **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling all per-package Agent() calls above with `run_in_background=true`, do NOT generate any package READMEs independently while the subagents are active. Wait for all agents to complete before proceeding. This prevents duplicate work and wasted context.
|
|
||||||
|
|
||||||
Collect confirmations by reading each package agent's `outputFile` once it reports completion — each `run_in_background=true` Agent call returns an `async_launched` result carrying an `outputFile` path (with `canReadOutputFile: true`). Note failures in the final report.
|
|
||||||
|
|
||||||
**Fallback when Task tool is unavailable:** Generate per-package READMEs sequentially inline after the `sequential_generation` step. For each package directory with a `package.json`, construct the equivalent `doc_assignment` block and generate the README following gsd-doc-writer instructions.
|
|
||||||
|
|
||||||
Continue to commit_docs.
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="sequential_generation" condition="Task tool is NOT available (e.g. Antigravity, Gemini CLI, Codex, Copilot)">
|
<step name="sequential_generation" condition="Task tool is NOT available (e.g. Antigravity, Gemini CLI, Codex, Copilot)">
|
||||||
**Read the work manifest first:** `Read .planning/tmp/docs-work-manifest.json` — use `canonical_queue` items for generation order. Update `status` after each doc is generated. Write the updated manifest back to disk after all docs are complete.
|
**Read the work manifest first:** `Read .planning/tmp/docs-work-manifest.json` — use `canonical_queue` items for generation order. Update `status` after each doc is generated. Write the updated manifest back to disk after all docs are complete.
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<step name="dispatch_monorepo_packages" condition="monorepo_workspaces is non-empty">
|
||||||
|
After Wave 2 collection, generate per-package READMEs for each monorepo workspace.
|
||||||
|
|
||||||
|
**Condition:** Only run this step if `monorepo_workspaces` from the init JSON is non-empty.
|
||||||
|
|
||||||
|
**Resolve workspace packages from glob patterns:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Expand workspace globs to actual package directories
|
||||||
|
for pattern in {monorepo_workspaces}; do
|
||||||
|
ls -d $pattern 2>/dev/null
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
**For each resolved directory that contains a `package.json`:**
|
||||||
|
|
||||||
|
Determine mode:
|
||||||
|
- If `{package_dir}/README.md` exists: mode = `update`, read existing content
|
||||||
|
- Else: mode = `create`
|
||||||
|
|
||||||
|
Spawn a `gsd-doc-writer` agent with `run_in_background=true`:
|
||||||
|
|
||||||
|
```
|
||||||
|
Agent(
|
||||||
|
subagent_type="gsd-doc-writer",
|
||||||
|
model="{doc_writer_model}",
|
||||||
|
run_in_background=true,
|
||||||
|
description="Generate per-package README for {package_dir}",
|
||||||
|
prompt="<doc_assignment>
|
||||||
|
type: readme
|
||||||
|
mode: {create|update}
|
||||||
|
scope: per_package
|
||||||
|
package_dir: {absolute path to package directory}
|
||||||
|
project_context: {INIT JSON with project_root set to package directory}
|
||||||
|
{existing_content: | (include full README.md content here if mode is update, else omit)}
|
||||||
|
</doc_assignment>
|
||||||
|
|
||||||
|
{AGENT_SKILLS}
|
||||||
|
|
||||||
|
Write {package_dir}/README.md directly. Return confirmation only — do not return doc content."
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
> **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling all per-package Agent() calls above with `run_in_background=true`, do NOT generate any package READMEs independently while the subagents are active. Wait for all agents to complete before proceeding. This prevents duplicate work and wasted context.
|
||||||
|
|
||||||
|
Collect confirmations by reading each package agent's `outputFile` once it reports completion — each `run_in_background=true` Agent call returns an `async_launched` result carrying an `outputFile` path (with `canReadOutputFile: true`). Note failures in the final report.
|
||||||
|
|
||||||
|
**Fallback when Task tool is unavailable:** Generate per-package READMEs sequentially inline after the `sequential_generation` step. For each package directory with a `package.json`, construct the equivalent `doc_assignment` block and generate the README following gsd-doc-writer instructions.
|
||||||
|
|
||||||
|
Continue to commit_docs.
|
||||||
|
</step>
|
||||||
@@ -32,9 +32,20 @@ Parse `GSD_WS` and `MILESTONE_ARG` using the established idiom (see `verify-work
|
|||||||
```bash
|
```bash
|
||||||
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
||||||
GSD_WS=""
|
GSD_WS=""
|
||||||
echo "$ARGUMENTS" | grep -qE -- '--ws[[:space:]]+[^[:space:]]+' && GSD_WS=$(echo "$ARGUMENTS" | grep -oE -- '--ws[[:space:]]+[^[:space:]]+')
|
echo "$ARGUMENTS" | grep -qE -- '--ws[[:space:]]+[A-Za-z0-9._-]+' && GSD_WS=$(echo "$ARGUMENTS" | grep -oE -- '--ws[[:space:]]+[A-Za-z0-9._-]+')
|
||||||
MILESTONE_ARG=$(echo "$ARGUMENTS" | sed -E 's/--ws[[:space:]]+[^[:space:]]+//g' | xargs)
|
MILESTONE_ARG=$(echo "$ARGUMENTS" | sed -E 's/--ws[[:space:]]+[A-Za-z0-9._-]+//g' | xargs)
|
||||||
RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --default "" 2>/dev/null || echo "")
|
RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --default "" 2>/dev/null || echo "")
|
||||||
|
# #2994: EARLY, section-manifest-only init.new-milestone call — needed here
|
||||||
|
# (before Step 4) to gate the project-md-milestone-write section. This is
|
||||||
|
# DELIBERATELY separate from Step 7's full init.new-milestone call below,
|
||||||
|
# which must stay AFTER Step 6's phase archival/phases.clear so its
|
||||||
|
# phase_dir_count / roadmap_exists / latest_completed_milestone fields
|
||||||
|
# reflect POST-archival state — moving that call here would compute those
|
||||||
|
# fields too early and corrupt the roadmapper's phase-numbering context.
|
||||||
|
# init.new-milestone is a pure read (no mutation), so calling it twice is
|
||||||
|
# safe; only `section_manifest` is consumed from this early call.
|
||||||
|
INIT_EARLY=$(gsd_run query init.new-milestone)
|
||||||
|
if [[ "$INIT_EARLY" == @file:* ]]; then INIT_EARLY=$(cat "${INIT_EARLY#@file:}"); fi
|
||||||
```
|
```
|
||||||
|
|
||||||
`GSD_WS` must chain to every downstream routing suggestion in this workflow (Step 4's shared-file guard, and the `/gsd:discuss-phase`/`/gsd:plan-phase` routing hints below) per the routing-propagation contract in `references/workstream-flag.md` — never let it silently drop.
|
`GSD_WS` must chain to every downstream routing suggestion in this workflow (Step 4's shared-file guard, and the `/gsd:discuss-phase`/`/gsd:plan-phase` routing hints below) per the routing-propagation contract in `references/workstream-flag.md` — never let it silently drop.
|
||||||
@@ -151,22 +162,9 @@ AskUserQuestion:
|
|||||||
|
|
||||||
PROJECT.md is shared across workstreams (`references/workstream-flag.md` marks it `# Shared` in the directory diagram). This step has two independently-scoped parts — only Part A is workstream-guarded.
|
PROJECT.md is shared across workstreams (`references/workstream-flag.md` marks it `# Shared` in the directory diagram). This step has two independently-scoped parts — only Part A is workstream-guarded.
|
||||||
|
|
||||||
**Part A — milestone-state write (skip when a workstream is active).** Skip Part A if `GSD_WS` is non-empty (parsed in Step 1). The active workstream's own `.planning/workstreams/<name>/STATE.md`/`ROADMAP.md`/`REQUIREMENTS.md` already carry this milestone's state. Writing a `## Current Milestone` heading here would clobber the shared file, and with parallel milestones across workstreams, whichever workstream runs `new-milestone` last would silently win the shared heading (#2308). In flat mode (`GSD_WS` empty), run Part A exactly as before:
|
<!-- gsd:section id="project-md-milestone-write" when="state:flat-mode" -->
|
||||||
|
If `section_manifest` (from `INIT_EARLY`) is `null` or `"project-md-milestone-write"` is in its `included` list: read and execute `gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md`. Otherwise (a workstream is active) skip — do not read the file; Part B below still runs regardless of `GSD_WS`.
|
||||||
Add/update:
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
```markdown
|
|
||||||
## Current Milestone: v[X.Y] [Name]
|
|
||||||
|
|
||||||
**Goal:** [One sentence describing milestone focus]
|
|
||||||
|
|
||||||
**Target features:**
|
|
||||||
- [Feature 1]
|
|
||||||
- [Feature 2]
|
|
||||||
- [Feature 3]
|
|
||||||
```
|
|
||||||
|
|
||||||
Update Active requirements section and "Last updated" footer.
|
|
||||||
|
|
||||||
**Part B — Evolution structural repair (always runs, regardless of `GSD_WS`).** `## Evolution` is a shared, idempotent structural section, not workstream state — a pre-Evolution project must be backfilled whether or not a workstream is active, so this part is NOT covered by Part A's skip. Ensure the `## Evolution` section exists in PROJECT.md. If missing (projects created before this feature), add it before the footer:
|
**Part B — Evolution structural repair (always runs, regardless of `GSD_WS`).** `## Evolution` is a shared, idempotent structural section, not workstream state — a pre-Evolution project must be backfilled whether or not a workstream is active, so this part is NOT covered by Part A's skip. Ensure the `## Evolution` section exists in PROJECT.md. If missing (projects created before this feature), add it before the footer:
|
||||||
|
|
||||||
@@ -273,7 +271,8 @@ gsd_run query commit "docs: start milestone v[X.Y] [Name]" --files .planning/PRO
|
|||||||
## 7. Load Context and Resolve Models
|
## 7. Load Context and Resolve Models
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
INIT=$(gsd_run query init.new-milestone)
|
RESET_PHASE_NUMBERS_PARAM=""; if [[ "$ARGUMENTS" =~ (^|[[:space:]])--reset-phase-numbers([[:space:]]|$) ]]; then RESET_PHASE_NUMBERS_PARAM="--reset-phase-numbers"; fi
|
||||||
|
INIT=$(gsd_run query init.new-milestone $RESET_PHASE_NUMBERS_PARAM)
|
||||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||||
AGENT_SKILLS_RESEARCHER=$(gsd_run query agent-skills gsd-project-researcher)
|
AGENT_SKILLS_RESEARCHER=$(gsd_run query agent-skills gsd-project-researcher)
|
||||||
AGENT_SKILLS_SYNTHESIZER=$(gsd_run query agent-skills gsd-research-synthesizer)
|
AGENT_SKILLS_SYNTHESIZER=$(gsd_run query agent-skills gsd-research-synthesizer)
|
||||||
@@ -296,25 +295,9 @@ Proceeding without research subagents — roadmap will be generated inline.
|
|||||||
```
|
```
|
||||||
Skip the parallel research spawn step and generate the roadmap inline.
|
Skip the parallel research spawn step and generate the roadmap inline.
|
||||||
|
|
||||||
## 7.5 Reset-phase safety (only when `--reset-phase-numbers`)
|
<!-- gsd:section id="reset-phase-safety" when="flag:--reset-phase-numbers" -->
|
||||||
|
If `section_manifest` is `null` or `"reset-phase-safety"` is in its `included` list: read and execute `gsd-core/workflows/new-milestone/steps/reset-phase-safety.md`. Otherwise skip — do not read the file.
|
||||||
If `--reset-phase-numbers` is active:
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
1. Set starting phase number to `1` for the upcoming roadmap.
|
|
||||||
2. If `phase_dir_count > 0`, archive the old phase directories before roadmapping so new `01-*` / `02-*` directories cannot collide with stale milestone directories.
|
|
||||||
|
|
||||||
If `phase_dir_count > 0` and `phase_archive_path` is available:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p "${phase_archive_path}"
|
|
||||||
find .planning/phases -mindepth 1 -maxdepth 1 -type d -exec mv {} "${phase_archive_path}/" \;
|
|
||||||
```
|
|
||||||
|
|
||||||
Then verify `.planning/phases/` no longer contains old milestone directories before continuing.
|
|
||||||
|
|
||||||
If `phase_dir_count > 0` but `phase_archive_path` is missing:
|
|
||||||
- Stop and explain that reset numbering is unsafe without a completed milestone archive target.
|
|
||||||
- Tell the user to complete/archive the previous milestone first, then rerun `/gsd:new-milestone --reset-phase-numbers ${GSD_WS}`.
|
|
||||||
|
|
||||||
## 8. Research Decision
|
## 8. Research Decision
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
**Part A — milestone-state write (skip when a workstream is active).** Skip Part A if `GSD_WS` is non-empty (parsed in Step 1). The active workstream's own `.planning/workstreams/<name>/STATE.md`/`ROADMAP.md`/`REQUIREMENTS.md` already carry this milestone's state. Writing a `## Current Milestone` heading here would clobber the shared file, and with parallel milestones across workstreams, whichever workstream runs `new-milestone` last would silently win the shared heading (#2308). In flat mode (`GSD_WS` empty), run Part A exactly as before:
|
||||||
|
|
||||||
|
Add/update:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Current Milestone: v[X.Y] [Name]
|
||||||
|
|
||||||
|
**Goal:** [One sentence describing milestone focus]
|
||||||
|
|
||||||
|
**Target features:**
|
||||||
|
- [Feature 1]
|
||||||
|
- [Feature 2]
|
||||||
|
- [Feature 3]
|
||||||
|
```
|
||||||
|
|
||||||
|
Update Active requirements section and "Last updated" footer.
|
||||||
19
gsd-core/workflows/new-milestone/steps/reset-phase-safety.md
Normal file
19
gsd-core/workflows/new-milestone/steps/reset-phase-safety.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
## 7.5 Reset-phase safety (only when `--reset-phase-numbers`)
|
||||||
|
|
||||||
|
If `--reset-phase-numbers` is active:
|
||||||
|
|
||||||
|
1. Set starting phase number to `1` for the upcoming roadmap.
|
||||||
|
2. If `phase_dir_count > 0`, archive the old phase directories before roadmapping so new `01-*` / `02-*` directories cannot collide with stale milestone directories.
|
||||||
|
|
||||||
|
If `phase_dir_count > 0` and `phase_archive_path` is available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p "${phase_archive_path}"
|
||||||
|
find .planning/phases -mindepth 1 -maxdepth 1 -type d -exec mv {} "${phase_archive_path}/" \;
|
||||||
|
```
|
||||||
|
|
||||||
|
Then verify `.planning/phases/` no longer contains old milestone directories before continuing.
|
||||||
|
|
||||||
|
If `phase_dir_count > 0` but `phase_archive_path` is missing:
|
||||||
|
- Stop and explain that reset numbering is unsafe without a completed milestone archive target.
|
||||||
|
- Tell the user to complete/archive the previous milestone first, then rerun `/gsd:new-milestone --reset-phase-numbers ${GSD_WS}`.
|
||||||
@@ -15,38 +15,9 @@ Valid GSD subagent types (use exact names — do not fall back to 'general-purpo
|
|||||||
|
|
||||||
<auto_mode>
|
<auto_mode>
|
||||||
|
|
||||||
## Auto Mode Detection
|
<!-- gsd:section id="auto-mode-detection" when="flag:--auto" -->
|
||||||
|
If `section_manifest` is `null` or `"auto-mode-detection"` is in its `included` list: read and execute `gsd-core/workflows/new-project/steps/auto-mode-detection.md`. Otherwise skip — do not read the file.
|
||||||
Check if `--auto` flag is present in $ARGUMENTS.
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
**If auto mode:**
|
|
||||||
|
|
||||||
- Skip brownfield mapping offer (assume greenfield)
|
|
||||||
- Skip deep questioning (extract context from provided document)
|
|
||||||
- Config: YOLO mode is implicit (skip that question), but ask granularity/git/agents FIRST (Step 2a)
|
|
||||||
- After config: run Steps 6-9 automatically with smart defaults:
|
|
||||||
- Research: Always yes
|
|
||||||
- Requirements: Include all table stakes + features from provided document
|
|
||||||
- Requirements approval: Auto-approve
|
|
||||||
- Roadmap approval: Auto-approve
|
|
||||||
|
|
||||||
**Document requirement:**
|
|
||||||
Auto mode requires an idea document — either:
|
|
||||||
|
|
||||||
- File reference: `/gsd:new-project --auto @prd.md`
|
|
||||||
- Pasted/written text in the prompt
|
|
||||||
|
|
||||||
If no document content provided, error:
|
|
||||||
|
|
||||||
```
|
|
||||||
Error: --auto requires an idea document.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
/gsd:new-project --auto @your-idea.md
|
|
||||||
/gsd:new-project --auto [paste or write your idea here]
|
|
||||||
|
|
||||||
The document should describe what you want to build.
|
|
||||||
```
|
|
||||||
|
|
||||||
</auto_mode>
|
</auto_mode>
|
||||||
|
|
||||||
@@ -58,7 +29,8 @@ The document should describe what you want to build.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
||||||
INIT=$(gsd_run query init.new-project)
|
AUTO_PARAM=""; if [[ "$ARGUMENTS" =~ (^|[[:space:]])--auto([[:space:]]|$) ]]; then AUTO_PARAM="--auto"; fi
|
||||||
|
INIT=$(gsd_run query init.new-project $AUTO_PARAM)
|
||||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||||
AGENT_SKILLS_RESEARCHER=$(gsd_run query agent-skills gsd-project-researcher)
|
AGENT_SKILLS_RESEARCHER=$(gsd_run query agent-skills gsd-project-researcher)
|
||||||
AGENT_SKILLS_SYNTHESIZER=$(gsd_run query agent-skills gsd-research-synthesizer)
|
AGENT_SKILLS_SYNTHESIZER=$(gsd_run query agent-skills gsd-research-synthesizer)
|
||||||
@@ -132,202 +104,15 @@ All subsequent references to the project instruction file use `$INSTRUCTION_FILE
|
|||||||
|
|
||||||
**If auto mode:** Skip to Step 4 (assume greenfield, synthesize PROJECT.md from provided document).
|
**If auto mode:** Skip to Step 4 (assume greenfield, synthesize PROJECT.md from provided document).
|
||||||
|
|
||||||
**If `needs_codebase_map` is true** (from init — existing code detected but no codebase map):
|
<!-- gsd:section id="codebase-map-offer" when="state:needs-codebase-map" -->
|
||||||
|
If `section_manifest` is `null` or `"codebase-map-offer"` is in its `included` list: read and execute `gsd-core/workflows/new-project/steps/codebase-map-offer.md`. Otherwise skip — do not read the file.
|
||||||
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
|
<!-- /gsd:section -->
|
||||||
Use AskUserQuestion:
|
|
||||||
|
|
||||||
- header: "Codebase"
|
|
||||||
- question: "I detected existing code in this directory. Would you like to map the codebase first?"
|
|
||||||
- options:
|
|
||||||
- "Map codebase first" — Run /gsd:map-codebase to understand existing architecture (Recommended)
|
|
||||||
- "Skip mapping" — Proceed with project initialization
|
|
||||||
|
|
||||||
**If "Map codebase first":**
|
|
||||||
|
|
||||||
```
|
|
||||||
Run `/gsd:map-codebase` first, then return to `/gsd:new-project`
|
|
||||||
```
|
|
||||||
|
|
||||||
Exit command.
|
|
||||||
|
|
||||||
**If "Skip mapping" OR `needs_codebase_map` is false:** Continue to Step 3.
|
**If "Skip mapping" OR `needs_codebase_map` is false:** Continue to Step 3.
|
||||||
|
|
||||||
## 2a. Auto Mode Config (auto mode only)
|
<!-- gsd:section id="auto-mode-config" when="flag:--auto" -->
|
||||||
|
If `section_manifest` is `null` or `"auto-mode-config"` is in its `included` list: read and execute `gsd-core/workflows/new-project/steps/auto-mode-config.md`. Otherwise skip — do not read the file.
|
||||||
**If auto mode:** Collect config settings upfront before processing the idea document.
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
YOLO mode is implicit (auto = YOLO). Ask remaining config questions:
|
|
||||||
|
|
||||||
**Round 1 — Core settings (3 questions, no Mode question):**
|
|
||||||
|
|
||||||
```
|
|
||||||
AskUserQuestion([
|
|
||||||
{
|
|
||||||
header: "Granularity",
|
|
||||||
question: "How finely should scope be sliced into phases?",
|
|
||||||
multiSelect: false,
|
|
||||||
options: [
|
|
||||||
{ label: "Coarse (Recommended)", description: "Fewer, broader phases (3-5 phases, 1-3 plans each)" },
|
|
||||||
{ label: "Standard", description: "Balanced phase size (5-8 phases, 3-5 plans each)" },
|
|
||||||
{ label: "Fine", description: "Many focused phases (8-12 phases, 5-10 plans each)" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Execution",
|
|
||||||
question: "Run plans in parallel?",
|
|
||||||
multiSelect: false,
|
|
||||||
options: [
|
|
||||||
{ label: "Parallel (Recommended)", description: "Independent plans run simultaneously" },
|
|
||||||
{ label: "Sequential", description: "One plan at a time" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Git Tracking",
|
|
||||||
question: "Commit planning docs to git?",
|
|
||||||
multiSelect: false,
|
|
||||||
options: [
|
|
||||||
{ label: "Yes (Recommended)", description: "Planning docs tracked in version control" },
|
|
||||||
{ label: "No", description: "Keep .planning/ local-only (add to .gitignore)" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
])
|
|
||||||
```
|
|
||||||
|
|
||||||
**Round 2 — Workflow agents (same as Step 5):**
|
|
||||||
|
|
||||||
```
|
|
||||||
AskUserQuestion([
|
|
||||||
{
|
|
||||||
header: "Research",
|
|
||||||
question: "Research before planning each phase? (adds tokens/time)",
|
|
||||||
multiSelect: false,
|
|
||||||
options: [
|
|
||||||
{ label: "Yes (Recommended)", description: "Investigate domain, find patterns, surface gotchas" },
|
|
||||||
{ label: "No", description: "Plan directly from requirements" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Plan Check",
|
|
||||||
question: "Verify plans will achieve their goals? (adds tokens/time)",
|
|
||||||
multiSelect: false,
|
|
||||||
options: [
|
|
||||||
{ label: "Yes (Recommended)", description: "Catch gaps before execution starts" },
|
|
||||||
{ label: "No", description: "Execute plans without verification" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Verifier",
|
|
||||||
question: "Verify work satisfies requirements after each phase? (adds tokens/time)",
|
|
||||||
multiSelect: false,
|
|
||||||
options: [
|
|
||||||
{ label: "Yes (Recommended)", description: "Confirm deliverables match phase goals" },
|
|
||||||
{ label: "No", description: "Trust execution, skip verification" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: "Drift Guard",
|
|
||||||
question: "Enable the plan drift-guard? It verifies that symbols your plans cite (decorators, classes, functions, CLI flags) actually exist in your source at review time, catching hallucinated names before execution. [Y/n]",
|
|
||||||
multiSelect: false,
|
|
||||||
options: [
|
|
||||||
{ label: "Yes (Recommended)", description: "Resolve symbol references against live source during plan review — catches hallucinated names before execution" },
|
|
||||||
{ label: "No", description: "Skip symbol grounding — plan review proceeds without source verification" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// Model profile uses a two-question split because AskUserQuestion enforces a hard
|
|
||||||
// 4-option cap and there are 5 valid profiles (quality, balanced, budget, adaptive,
|
|
||||||
// inherit). Q1 routes between adaptive/standard-tier/inherit; Q2 (shown only when
|
|
||||||
// Q1 = "Standard tier…") picks among the three standard profiles. Mirrors the
|
|
||||||
// /gsd:settings split (#3784, #1516).
|
|
||||||
AskUserQuestion([
|
|
||||||
{
|
|
||||||
header: "AI Models",
|
|
||||||
question: "Which AI models for planning agents?",
|
|
||||||
multiSelect: false,
|
|
||||||
options: [
|
|
||||||
{ label: "Adaptive (Recommended)", description: "Role-based cost optimization: heavy roles use the highest-tier model available on the active runtime, light roles use the cheapest. Best balance of quality and cost across all supported runtimes (Claude, Codex, Gemini, OpenRouter, local)." },
|
|
||||||
{ label: "Standard tier…", description: "Choose Quality, Balanced, or Budget — flat tier applied to all agents" },
|
|
||||||
{ label: "Inherit", description: "Use the current session model for all agents (required for non-Claude runtimes: Codex, Gemini CLI, OpenCode /model, OpenRouter, local models)" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
**Conditional visibility — model_profile (Q2):**
|
|
||||||
Only ask this question when Q1's answer is "Standard tier…".
|
|
||||||
If Q1 = "Adaptive (Recommended)" → write model_profile=adaptive and SKIP Q2.
|
|
||||||
If Q1 = "Inherit" → write model_profile=inherit and SKIP Q2.
|
|
||||||
If user cancels Q2 after picking "Standard tier…" → leave existing model_profile value unchanged.
|
|
||||||
|
|
||||||
AskUserQuestion([
|
|
||||||
{
|
|
||||||
question: "Which standard profile? (Quality / Balanced / Budget)",
|
|
||||||
header: "Model Tier",
|
|
||||||
multiSelect: false,
|
|
||||||
options: [
|
|
||||||
{ label: "Quality", description: "Opus everywhere except verification (highest cost) — Claude only" },
|
|
||||||
{ label: "Balanced", description: "Opus for planning, Sonnet for research/execution/verification — Claude only" },
|
|
||||||
{ label: "Budget", description: "Sonnet for writing, Haiku for research/verification (lowest cost) — Claude only" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// Map UI choices → config values:
|
|
||||||
// Q1 "Adaptive (Recommended)" → model_profile = "adaptive"
|
|
||||||
// Q1 "Inherit" → model_profile = "inherit"
|
|
||||||
// Q1 "Standard tier…" + Q2 "Quality" → model_profile = "quality"
|
|
||||||
// Q1 "Standard tier…" + Q2 "Balanced" → model_profile = "balanced"
|
|
||||||
// Q1 "Standard tier…" + Q2 "Budget" → model_profile = "budget"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Round 3 — PR body onboarding:**
|
|
||||||
|
|
||||||
Ask which optional PRD-style sections `/gsd:ship` should append to generated PR bodies. These map to `ship.pr_body_sections`; selected sections are written with `"enabled": true`, unselected seeded sections are written with `"enabled": false` so the project can enable them later without editing `ship.md`.
|
|
||||||
|
|
||||||
Prefer lean/agile PRD sections that make the delivered increment clear: user stories, acceptance criteria, Definition of Done or release criteria, risks, dependencies, and stakeholder review.
|
|
||||||
|
|
||||||
```
|
|
||||||
AskUserQuestion([
|
|
||||||
{
|
|
||||||
header: "PR Body",
|
|
||||||
question: "Which optional PRD-style sections should /gsd:ship include in PR bodies?",
|
|
||||||
multiSelect: true,
|
|
||||||
options: [
|
|
||||||
{ label: "User Stories & Acceptance Criteria", description: "Append user-facing stories and acceptance checks from REQUIREMENTS.md" },
|
|
||||||
{ label: "Risks & Dependencies", description: "Append rollout risks, dependencies, and rollback notes from PLAN.md" },
|
|
||||||
{ label: "Success Metrics & Release Criteria", description: "Append measurable Definition of Done and release checks for stakeholder review" },
|
|
||||||
{ label: "Stakeholder Review & Approval", description: "Append approval checklist for projects that need sign-off traceability" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
])
|
|
||||||
```
|
|
||||||
|
|
||||||
Build `ship.pr_body_sections` from those choices. For selected options, set `enabled: true`; for seeded but unselected options, set `enabled: false`. If the user selects none, use `"ship":{"pr_body_sections":[]}`.
|
|
||||||
|
|
||||||
Create `.planning/config.json` with all settings (CLI fills in remaining defaults automatically):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p .planning
|
|
||||||
gsd_run query config-new-project '{"mode":"yolo","granularity":"[selected]","parallelization":true|false,"commit_docs":true|false,"model_profile":"quality|balanced|budget|adaptive|inherit","workflow":{"research":true|false,"plan_check":true|false,"verifier":true|false,"nyquist_validation":true|false,"auto_advance":true},"plan_review":{"source_grounding":true|false},"ship":{"pr_body_sections":[{"heading":"User Stories & Acceptance Criteria","enabled":true|false,"source":"REQUIREMENTS.md ## User Stories || REQUIREMENTS.md ## Acceptance Criteria","fallback":"- Acceptance criteria are covered by the linked requirements and verification evidence."},{"heading":"Risks & Dependencies","enabled":true|false,"source":"PLAN.md ## Risks || PLAN.md ## Dependencies","fallback":"- No known high-risk rollout dependencies."},{"heading":"Success Metrics & Release Criteria","enabled":true|false,"source":"REQUIREMENTS.md ## Definition of Done || VERIFICATION.md ## Release Criteria","fallback":"- Release when automated verification and required manual checks pass."},{"heading":"Stakeholder Review & Approval","enabled":true|false,"template":"- Product owner approval pending for {phase_name}."}]}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
**If commit_docs = No:** Add `.planning/` to `.gitignore`.
|
|
||||||
|
|
||||||
**Commit config.json:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p .planning
|
|
||||||
gsd_run query commit "chore: add project config" --files .planning/config.json
|
|
||||||
```
|
|
||||||
|
|
||||||
**Persist auto-advance chain flag to config (survives context compaction):**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gsd_run query config-set workflow._auto_chain_active true
|
|
||||||
```
|
|
||||||
|
|
||||||
Proceed to Step 4 (skip Steps 3 and 5).
|
|
||||||
|
|
||||||
## 2b. Prior Spike/Sketch Detection
|
## 2b. Prior Spike/Sketch Detection
|
||||||
|
|
||||||
|
|||||||
176
gsd-core/workflows/new-project/steps/auto-mode-config.md
Normal file
176
gsd-core/workflows/new-project/steps/auto-mode-config.md
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
## 2a. Auto Mode Config (auto mode only)
|
||||||
|
|
||||||
|
**If auto mode:** Collect config settings upfront before processing the idea document.
|
||||||
|
|
||||||
|
YOLO mode is implicit (auto = YOLO). Ask remaining config questions:
|
||||||
|
|
||||||
|
**Round 1 — Core settings (3 questions, no Mode question):**
|
||||||
|
|
||||||
|
```
|
||||||
|
AskUserQuestion([
|
||||||
|
{
|
||||||
|
header: "Granularity",
|
||||||
|
question: "How finely should scope be sliced into phases?",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "Coarse (Recommended)", description: "Fewer, broader phases (3-5 phases, 1-3 plans each)" },
|
||||||
|
{ label: "Standard", description: "Balanced phase size (5-8 phases, 3-5 plans each)" },
|
||||||
|
{ label: "Fine", description: "Many focused phases (8-12 phases, 5-10 plans each)" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Execution",
|
||||||
|
question: "Run plans in parallel?",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "Parallel (Recommended)", description: "Independent plans run simultaneously" },
|
||||||
|
{ label: "Sequential", description: "One plan at a time" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Git Tracking",
|
||||||
|
question: "Commit planning docs to git?",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "Yes (Recommended)", description: "Planning docs tracked in version control" },
|
||||||
|
{ label: "No", description: "Keep .planning/ local-only (add to .gitignore)" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
**Round 2 — Workflow agents (same as Step 5):**
|
||||||
|
|
||||||
|
```
|
||||||
|
AskUserQuestion([
|
||||||
|
{
|
||||||
|
header: "Research",
|
||||||
|
question: "Research before planning each phase? (adds tokens/time)",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "Yes (Recommended)", description: "Investigate domain, find patterns, surface gotchas" },
|
||||||
|
{ label: "No", description: "Plan directly from requirements" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Plan Check",
|
||||||
|
question: "Verify plans will achieve their goals? (adds tokens/time)",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "Yes (Recommended)", description: "Catch gaps before execution starts" },
|
||||||
|
{ label: "No", description: "Execute plans without verification" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Verifier",
|
||||||
|
question: "Verify work satisfies requirements after each phase? (adds tokens/time)",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "Yes (Recommended)", description: "Confirm deliverables match phase goals" },
|
||||||
|
{ label: "No", description: "Trust execution, skip verification" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Drift Guard",
|
||||||
|
question: "Enable the plan drift-guard? It verifies that symbols your plans cite (decorators, classes, functions, CLI flags) actually exist in your source at review time, catching hallucinated names before execution. [Y/n]",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "Yes (Recommended)", description: "Resolve symbol references against live source during plan review — catches hallucinated names before execution" },
|
||||||
|
{ label: "No", description: "Skip symbol grounding — plan review proceeds without source verification" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
// Model profile uses a two-question split because AskUserQuestion enforces a hard
|
||||||
|
// 4-option cap and there are 5 valid profiles (quality, balanced, budget, adaptive,
|
||||||
|
// inherit). Q1 routes between adaptive/standard-tier/inherit; Q2 (shown only when
|
||||||
|
// Q1 = "Standard tier…") picks among the three standard profiles. Mirrors the
|
||||||
|
// /gsd:settings split (#3784, #1516).
|
||||||
|
AskUserQuestion([
|
||||||
|
{
|
||||||
|
header: "AI Models",
|
||||||
|
question: "Which AI models for planning agents?",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "Adaptive (Recommended)", description: "Role-based cost optimization: heavy roles use the highest-tier model available on the active runtime, light roles use the cheapest. Best balance of quality and cost across all supported runtimes (Claude, Codex, Gemini, OpenRouter, local)." },
|
||||||
|
{ label: "Standard tier…", description: "Choose Quality, Balanced, or Budget — flat tier applied to all agents" },
|
||||||
|
{ label: "Inherit", description: "Use the current session model for all agents (required for non-Claude runtimes: Codex, Gemini CLI, OpenCode /model, OpenRouter, local models)" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
**Conditional visibility — model_profile (Q2):**
|
||||||
|
Only ask this question when Q1's answer is "Standard tier…".
|
||||||
|
If Q1 = "Adaptive (Recommended)" → write model_profile=adaptive and SKIP Q2.
|
||||||
|
If Q1 = "Inherit" → write model_profile=inherit and SKIP Q2.
|
||||||
|
If user cancels Q2 after picking "Standard tier…" → leave existing model_profile value unchanged.
|
||||||
|
|
||||||
|
AskUserQuestion([
|
||||||
|
{
|
||||||
|
question: "Which standard profile? (Quality / Balanced / Budget)",
|
||||||
|
header: "Model Tier",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "Quality", description: "Opus everywhere except verification (highest cost) — Claude only" },
|
||||||
|
{ label: "Balanced", description: "Opus for planning, Sonnet for research/execution/verification — Claude only" },
|
||||||
|
{ label: "Budget", description: "Sonnet for writing, Haiku for research/verification (lowest cost) — Claude only" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
// Map UI choices → config values:
|
||||||
|
// Q1 "Adaptive (Recommended)" → model_profile = "adaptive"
|
||||||
|
// Q1 "Inherit" → model_profile = "inherit"
|
||||||
|
// Q1 "Standard tier…" + Q2 "Quality" → model_profile = "quality"
|
||||||
|
// Q1 "Standard tier…" + Q2 "Balanced" → model_profile = "balanced"
|
||||||
|
// Q1 "Standard tier…" + Q2 "Budget" → model_profile = "budget"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Round 3 — PR body onboarding:**
|
||||||
|
|
||||||
|
Ask which optional PRD-style sections `/gsd:ship` should append to generated PR bodies. These map to `ship.pr_body_sections`; selected sections are written with `"enabled": true`, unselected seeded sections are written with `"enabled": false` so the project can enable them later without editing `ship.md`.
|
||||||
|
|
||||||
|
Prefer lean/agile PRD sections that make the delivered increment clear: user stories, acceptance criteria, Definition of Done or release criteria, risks, dependencies, and stakeholder review.
|
||||||
|
|
||||||
|
```
|
||||||
|
AskUserQuestion([
|
||||||
|
{
|
||||||
|
header: "PR Body",
|
||||||
|
question: "Which optional PRD-style sections should /gsd:ship include in PR bodies?",
|
||||||
|
multiSelect: true,
|
||||||
|
options: [
|
||||||
|
{ label: "User Stories & Acceptance Criteria", description: "Append user-facing stories and acceptance checks from REQUIREMENTS.md" },
|
||||||
|
{ label: "Risks & Dependencies", description: "Append rollout risks, dependencies, and rollback notes from PLAN.md" },
|
||||||
|
{ label: "Success Metrics & Release Criteria", description: "Append measurable Definition of Done and release checks for stakeholder review" },
|
||||||
|
{ label: "Stakeholder Review & Approval", description: "Append approval checklist for projects that need sign-off traceability" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
Build `ship.pr_body_sections` from those choices. For selected options, set `enabled: true`; for seeded but unselected options, set `enabled: false`. If the user selects none, use `"ship":{"pr_body_sections":[]}`.
|
||||||
|
|
||||||
|
Create `.planning/config.json` with all settings (CLI fills in remaining defaults automatically):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
||||||
|
mkdir -p .planning
|
||||||
|
gsd_run query config-new-project '{"mode":"yolo","granularity":"[selected]","parallelization":true|false,"commit_docs":true|false,"model_profile":"quality|balanced|budget|adaptive|inherit","workflow":{"research":true|false,"plan_check":true|false,"verifier":true|false,"nyquist_validation":true|false,"auto_advance":true},"plan_review":{"source_grounding":true|false},"ship":{"pr_body_sections":[{"heading":"User Stories & Acceptance Criteria","enabled":true|false,"source":"REQUIREMENTS.md ## User Stories || REQUIREMENTS.md ## Acceptance Criteria","fallback":"- Acceptance criteria are covered by the linked requirements and verification evidence."},{"heading":"Risks & Dependencies","enabled":true|false,"source":"PLAN.md ## Risks || PLAN.md ## Dependencies","fallback":"- No known high-risk rollout dependencies."},{"heading":"Success Metrics & Release Criteria","enabled":true|false,"source":"REQUIREMENTS.md ## Definition of Done || VERIFICATION.md ## Release Criteria","fallback":"- Release when automated verification and required manual checks pass."},{"heading":"Stakeholder Review & Approval","enabled":true|false,"template":"- Product owner approval pending for {phase_name}."}]}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
**If commit_docs = No:** Add `.planning/` to `.gitignore`.
|
||||||
|
|
||||||
|
**Commit config.json:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p .planning
|
||||||
|
gsd_run query commit "chore: add project config" --files .planning/config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Persist auto-advance chain flag to config (survives context compaction):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gsd_run query config-set workflow._auto_chain_active true
|
||||||
|
```
|
||||||
|
|
||||||
|
Proceed to Step 4 (skip Steps 3 and 5).
|
||||||
32
gsd-core/workflows/new-project/steps/auto-mode-detection.md
Normal file
32
gsd-core/workflows/new-project/steps/auto-mode-detection.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
## Auto Mode Detection
|
||||||
|
|
||||||
|
Check if `--auto` flag is present in $ARGUMENTS.
|
||||||
|
|
||||||
|
**If auto mode:**
|
||||||
|
|
||||||
|
- Skip brownfield mapping offer (assume greenfield)
|
||||||
|
- Skip deep questioning (extract context from provided document)
|
||||||
|
- Config: YOLO mode is implicit (skip that question), but ask granularity/git/agents FIRST (Step 2a)
|
||||||
|
- After config: run Steps 6-9 automatically with smart defaults:
|
||||||
|
- Research: Always yes
|
||||||
|
- Requirements: Include all table stakes + features from provided document
|
||||||
|
- Requirements approval: Auto-approve
|
||||||
|
- Roadmap approval: Auto-approve
|
||||||
|
|
||||||
|
**Document requirement:**
|
||||||
|
Auto mode requires an idea document — either:
|
||||||
|
|
||||||
|
- File reference: `/gsd:new-project --auto @prd.md`
|
||||||
|
- Pasted/written text in the prompt
|
||||||
|
|
||||||
|
If no document content provided, error:
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: --auto requires an idea document.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
/gsd:new-project --auto @your-idea.md
|
||||||
|
/gsd:new-project --auto [paste or write your idea here]
|
||||||
|
|
||||||
|
The document should describe what you want to build.
|
||||||
|
```
|
||||||
18
gsd-core/workflows/new-project/steps/codebase-map-offer.md
Normal file
18
gsd-core/workflows/new-project/steps/codebase-map-offer.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
**If `needs_codebase_map` is true** (from init — existing code detected but no codebase map):
|
||||||
|
|
||||||
|
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
|
||||||
|
Use AskUserQuestion:
|
||||||
|
|
||||||
|
- header: "Codebase"
|
||||||
|
- question: "I detected existing code in this directory. Would you like to map the codebase first?"
|
||||||
|
- options:
|
||||||
|
- "Map codebase first" — Run /gsd:map-codebase to understand existing architecture (Recommended)
|
||||||
|
- "Skip mapping" — Proceed with project initialization
|
||||||
|
|
||||||
|
**If "Map codebase first":**
|
||||||
|
|
||||||
|
```
|
||||||
|
Run `/gsd:map-codebase` first, then return to `/gsd:new-project`
|
||||||
|
```
|
||||||
|
|
||||||
|
Exit command.
|
||||||
@@ -13,11 +13,12 @@ Read all files referenced by the invoking prompt's execution_context before star
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
||||||
INIT=$(gsd_run query init.progress)
|
FORENSIC_PARAM=""; if [[ "$ARGUMENTS" =~ (^|[[:space:]])--forensic([[:space:]]|$) ]]; then FORENSIC_PARAM="--forensic"; fi
|
||||||
|
INIT=$(gsd_run query init.progress $FORENSIC_PARAM)
|
||||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||||
```
|
```
|
||||||
|
|
||||||
Extract from init JSON: `project_exists`, `roadmap_exists`, `state_exists`, `phases`, `current_phase`, `next_phase`, `milestone_version`, `completed_count`, `phase_count`, `paused_at`, `state_path`, `roadmap_path`, `project_path`, `config_path`.
|
Extract from init JSON: `project_exists`, `roadmap_exists`, `state_exists`, `phases`, `current_phase`, `next_phase`, `milestone_version`, `completed_count`, `phase_count`, `paused_at`, `state_path`, `roadmap_path`, `project_path`, `config_path`, `phase_mvp_mode`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
DISCUSS_MODE=$(gsd_run query config-get workflow.discuss_mode 2>/dev/null || echo "discuss")
|
DISCUSS_MODE=$(gsd_run query config-get workflow.discuss_mode 2>/dev/null || echo "discuss")
|
||||||
@@ -104,7 +105,7 @@ PROGRESS_BAR=$(gsd_run query progress.bar --raw)
|
|||||||
|
|
||||||
Present:
|
Present:
|
||||||
|
|
||||||
```
|
````
|
||||||
# [Project Name]
|
# [Project Name]
|
||||||
|
|
||||||
**Progress:** {PROGRESS_BAR}
|
**Progress:** {PROGRESS_BAR}
|
||||||
@@ -150,34 +151,13 @@ Render `Open Windows` only when `$WINDOWS_OPEN` is greater than `0` (or `$WINDOW
|
|||||||
|
|
||||||
## What's Next
|
## What's Next
|
||||||
[Next phase/plan objective from roadmap analyze]
|
[Next phase/plan objective from roadmap analyze]
|
||||||
```
|
````
|
||||||
|
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="mvp_display">
|
<!-- gsd:section id="mvp-display" when="state:phase-mvp-mode" -->
|
||||||
**MVP-mode display (when phase has `**Mode:** mvp` in ROADMAP.md).**
|
If `section_manifest` is `null` or `"mvp-display"` is in its `included` list: read and execute `gsd-core/workflows/progress/steps/mvp-display.md`. Otherwise skip — do not read the file.
|
||||||
|
<!-- /gsd:section -->
|
||||||
Resolve `MVP_MODE` per phase via the centralized resolver. progress has no `--mvp` CLI flag (mode is inherited from the planned phase), so we omit `--cli-flag`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
MVP_MODE=$(gsd_run query phase.mvp-mode "${PHASE_NUMBER}" --pick active)
|
|
||||||
```
|
|
||||||
|
|
||||||
When `MVP_MODE=true`, the per-phase progress block adds a **user-flow status** sub-block sourced from the phase's PLAN.md task names. Each task whose name reads like a user-visible capability (e.g., "Register flow", "Login flow", "Password reset") is rendered as a status line:
|
|
||||||
|
|
||||||
```
|
|
||||||
Phase 1 — User Auth MVP
|
|
||||||
✅ Walking Skeleton complete ← from SKELETON.md existence
|
|
||||||
✅ Register flow working ← from PLAN.md task with summary
|
|
||||||
✅ Login flow working ← from PLAN.md task with summary
|
|
||||||
🔄 Password reset (in progress) ← from PLAN.md task without summary
|
|
||||||
⬜ Email verification ← from PLAN.md task not yet started
|
|
||||||
```
|
|
||||||
|
|
||||||
**User-flow filter:** Tasks whose names are technical-sounding ("Wire DB schema", "Create migration", "Bump deps") are NOT rendered as user-flow status lines. Heuristic: a task name is user-flow-shaped if it ends in "flow", "page", "screen", or starts with a verb the user would recognize ("Register", "Login", "Upload", "View"). Tasks that fail the heuristic still count toward the standard task progress total but don't appear in the user-flow sub-block.
|
|
||||||
|
|
||||||
When `MVP_MODE=false` (mode is null, absent, or the phase has no `**Mode:**` line), fall back to the standard display path — no behavioral change.
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="route">
|
<step name="route">
|
||||||
**Determine next action based on verified counts.**
|
**Determine next action based on verified counts.**
|
||||||
@@ -678,131 +658,9 @@ Ready to plan the next milestone.
|
|||||||
- Handoff file exists → mention it, offer `/gsd:resume-work ${GSD_WS}`
|
- Handoff file exists → mention it, offer `/gsd:resume-work ${GSD_WS}`
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="forensic_audit">
|
<!-- gsd:section id="forensic-audit" when="flag:--forensic" -->
|
||||||
**Forensic Integrity Audit** — only runs when `--forensic` is present in ARGUMENTS.
|
If `section_manifest` is `null` or `"forensic-audit"` is in its `included` list: read and execute `gsd-core/workflows/progress/steps/forensic-audit.md`. Otherwise skip — do not read the file.
|
||||||
|
<!-- /gsd:section -->
|
||||||
If `--forensic` is NOT present in ARGUMENTS: skip this step entirely. Default progress behavior (standard report + routing) is unchanged.
|
|
||||||
|
|
||||||
If `--forensic` IS present: after the standard report and routing suggestion have been displayed, append the following audit section.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Forensic Integrity Audit
|
|
||||||
|
|
||||||
Running 7 deep checks against project state...
|
|
||||||
|
|
||||||
Run each check in order. For each check, emit ✓ (pass) or ⚠ (warning) with concrete evidence when a problem is found.
|
|
||||||
|
|
||||||
**Check 1 — STATE vs artifact consistency**
|
|
||||||
|
|
||||||
Read STATE.md `status` / `stopped_at` fields (from the STATE snapshot already loaded). Compare against the artifact count from the roadmap analysis. If STATE.md claims the current phase is pending/mid-flight but the artifact count shows it as complete (all PLAN.md files have matching SUMMARY.md files), flag inconsistency. Emit:
|
|
||||||
- ✓ `STATE.md consistent with artifact count` — if both agree
|
|
||||||
- ⚠ `STATE.md claims [status] but artifact count shows phase complete` — with the specific values
|
|
||||||
|
|
||||||
**Check 2 — Orphaned handoff files**
|
|
||||||
|
|
||||||
Check for existence of:
|
|
||||||
```bash
|
|
||||||
ls .planning/HANDOFF.json .planning/phases/*/.continue-here.md .planning/phases/*/*HANDOFF*.md 2>/dev/null || true
|
|
||||||
```
|
|
||||||
Also check `.planning/continue-here.md`.
|
|
||||||
|
|
||||||
Emit:
|
|
||||||
- ✓ `No orphaned handoff files` — if none found
|
|
||||||
- ⚠ `Orphaned handoff files found` — list each file path, add: `→ Work was paused mid-flight. Read the handoff before continuing.`
|
|
||||||
|
|
||||||
**Check 3 — Deferred scope drift**
|
|
||||||
|
|
||||||
Search phase artifacts (CONTEXT.md, DISCUSSION-LOG.md, BUG-BRIEF.md, VERIFICATION.md, SUMMARY.md, HANDOFF.md files under `.planning/phases/`) for patterns:
|
|
||||||
```bash
|
|
||||||
grep -rl "defer to Phase\|future phase\|out of scope Phase\|deferred to Phase" .planning/phases/ 2>/dev/null || true
|
|
||||||
```
|
|
||||||
|
|
||||||
For each match, extract the referenced phase number. Cross-reference against ROADMAP.md phase list. If the referenced phase number is NOT in ROADMAP.md, flag as deferred scope not captured.
|
|
||||||
|
|
||||||
Emit:
|
|
||||||
- ✓ `All deferred scope captured in ROADMAP` — if no mismatches
|
|
||||||
- ⚠ `Deferred scope references phase(s) not in ROADMAP` — list: file, reference text, missing phase number
|
|
||||||
|
|
||||||
**Check 4 — Memory-flagged pending work**
|
|
||||||
|
|
||||||
Check if `.planning/MEMORY.md` or `.planning/memory/` exists:
|
|
||||||
```bash
|
|
||||||
ls .planning/MEMORY.md .planning/memory/*.md 2>/dev/null || true
|
|
||||||
```
|
|
||||||
|
|
||||||
If found, grep for entries containing: `pending`, `status`, `deferred`, `not yet run`, `backfill`, `blocking`.
|
|
||||||
|
|
||||||
Emit:
|
|
||||||
- ✓ `No memory entries flagging pending work` — if none found or no MEMORY.md
|
|
||||||
- ⚠ `Memory entries flag pending/deferred work` — list the matching lines (max 5, truncated at 80 chars)
|
|
||||||
|
|
||||||
**Check 5 — Blocking operational todos**
|
|
||||||
|
|
||||||
Check for pending todos:
|
|
||||||
```bash
|
|
||||||
ls .planning/todos/pending/*.md 2>/dev/null || true
|
|
||||||
```
|
|
||||||
|
|
||||||
For files found, scan for keywords indicating operational blockers: `script`, `credential`, `API key`, `manual`, `verification`, `setup`, `configure`, `run `.
|
|
||||||
|
|
||||||
Emit:
|
|
||||||
- ✓ `No blocking operational todos` — if no pending todos or none match operational keywords
|
|
||||||
- ⚠ `Blocking operational todos found` — list the file names and matching keywords (max 5)
|
|
||||||
|
|
||||||
**Check 6 — Uncommitted code**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git status --porcelain 2>/dev/null | grep -v "^??" | grep -v "^.planning\/" | grep -v "^\.\." | head -10
|
|
||||||
```
|
|
||||||
|
|
||||||
If output is non-empty (modified/staged files outside `.planning/`), flag as uncommitted code.
|
|
||||||
|
|
||||||
Emit:
|
|
||||||
- ✓ `Working tree clean` — if no modified files outside `.planning/`
|
|
||||||
- ⚠ `Uncommitted changes in source files` — list up to 10 file paths
|
|
||||||
|
|
||||||
**Check 7 — Unresolved deferred items**
|
|
||||||
|
|
||||||
Glob every phase directory's SCOPE BOUNDARY log (executor writes out-of-scope discoveries here per `agents/gsd-executor.md`):
|
|
||||||
```bash
|
|
||||||
ls .planning/phases/*/deferred-items.md 2>/dev/null || true
|
|
||||||
```
|
|
||||||
|
|
||||||
For each `deferred-items.md` found, read its entries (bullet list, one entry per top-level `- ` line, continuation lines indented beneath it). An entry is RESOLVED only if it carries an explicit `status: resolved` field (case-insensitive) on one of its lines; every other entry — including one with no `status:` field at all — is UNRESOLVED and must be surfaced (fail-safe: never silently drop a possibly-open item).
|
|
||||||
|
|
||||||
Emit:
|
|
||||||
- ✓ `No unresolved deferred items` — if no `deferred-items.md` files exist, or every entry in every file is `status: resolved`
|
|
||||||
- ⚠ `Unresolved deferred items found` — list each file's phase directory and its unresolved entry text (max 5 per file, truncated at 80 chars)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
After all 7 checks, display the verdict:
|
|
||||||
|
|
||||||
**If all 7 checks passed:**
|
|
||||||
```
|
|
||||||
### Verdict: CLEAN
|
|
||||||
|
|
||||||
The standard progress report is trustworthy — proceed with the routing suggestion above.
|
|
||||||
```
|
|
||||||
|
|
||||||
**If 1 or more checks failed:**
|
|
||||||
```
|
|
||||||
### Verdict: N INTEGRITY ISSUE(S) FOUND
|
|
||||||
|
|
||||||
The standard progress report may not reflect true project state.
|
|
||||||
Review the flagged items above before acting on the routing suggestion.
|
|
||||||
```
|
|
||||||
|
|
||||||
Then for each failed check, add a concrete next action:
|
|
||||||
- Check 2 (orphaned handoff): `Read the handoff file(s) and resume from where work was paused: /gsd:resume-work ${GSD_WS}`
|
|
||||||
- Check 3 (deferred scope): `Add the missing phases to ROADMAP.md or update the deferred references`
|
|
||||||
- Check 4 (memory pending): `Review the flagged memory entries and resolve or clear them`
|
|
||||||
- Check 5 (blocking todos): `Complete the operational steps in .planning/todos/pending/ before continuing`
|
|
||||||
- Check 6 (uncommitted code): `Commit or stash the uncommitted changes before advancing`
|
|
||||||
- Check 7 (unresolved deferred items): `Address each deferred item and mark it status: resolved in its deferred-items.md, or fold it into the roadmap`
|
|
||||||
- Check 1 (STATE inconsistency): `Run /gsd:verify-work ${PHASE} ${GSD_WS} to reconcile state`
|
|
||||||
</step>
|
|
||||||
|
|
||||||
</process>
|
</process>
|
||||||
|
|
||||||
|
|||||||
125
gsd-core/workflows/progress/steps/forensic-audit.md
Normal file
125
gsd-core/workflows/progress/steps/forensic-audit.md
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
<step name="forensic_audit">
|
||||||
|
**Forensic Integrity Audit** — only runs when `--forensic` is present in ARGUMENTS.
|
||||||
|
|
||||||
|
If `--forensic` is NOT present in ARGUMENTS: skip this step entirely. Default progress behavior (standard report + routing) is unchanged.
|
||||||
|
|
||||||
|
If `--forensic` IS present: after the standard report and routing suggestion have been displayed, append the following audit section.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Forensic Integrity Audit
|
||||||
|
|
||||||
|
Running 7 deep checks against project state...
|
||||||
|
|
||||||
|
Run each check in order. For each check, emit ✓ (pass) or ⚠ (warning) with concrete evidence when a problem is found.
|
||||||
|
|
||||||
|
**Check 1 — STATE vs artifact consistency**
|
||||||
|
|
||||||
|
Read STATE.md `status` / `stopped_at` fields (from the STATE snapshot already loaded). Compare against the artifact count from the roadmap analysis. If STATE.md claims the current phase is pending/mid-flight but the artifact count shows it as complete (all PLAN.md files have matching SUMMARY.md files), flag inconsistency. Emit:
|
||||||
|
- ✓ `STATE.md consistent with artifact count` — if both agree
|
||||||
|
- ⚠ `STATE.md claims [status] but artifact count shows phase complete` — with the specific values
|
||||||
|
|
||||||
|
**Check 2 — Orphaned handoff files**
|
||||||
|
|
||||||
|
Check for existence of:
|
||||||
|
```bash
|
||||||
|
ls .planning/HANDOFF.json .planning/phases/*/.continue-here.md .planning/phases/*/*HANDOFF*.md 2>/dev/null || true
|
||||||
|
```
|
||||||
|
Also check `.planning/continue-here.md`.
|
||||||
|
|
||||||
|
Emit:
|
||||||
|
- ✓ `No orphaned handoff files` — if none found
|
||||||
|
- ⚠ `Orphaned handoff files found` — list each file path, add: `→ Work was paused mid-flight. Read the handoff before continuing.`
|
||||||
|
|
||||||
|
**Check 3 — Deferred scope drift**
|
||||||
|
|
||||||
|
Search phase artifacts (CONTEXT.md, DISCUSSION-LOG.md, BUG-BRIEF.md, VERIFICATION.md, SUMMARY.md, HANDOFF.md files under `.planning/phases/`) for patterns:
|
||||||
|
```bash
|
||||||
|
grep -rl "defer to Phase\|future phase\|out of scope Phase\|deferred to Phase" .planning/phases/ 2>/dev/null || true
|
||||||
|
```
|
||||||
|
|
||||||
|
For each match, extract the referenced phase number. Cross-reference against ROADMAP.md phase list. If the referenced phase number is NOT in ROADMAP.md, flag as deferred scope not captured.
|
||||||
|
|
||||||
|
Emit:
|
||||||
|
- ✓ `All deferred scope captured in ROADMAP` — if no mismatches
|
||||||
|
- ⚠ `Deferred scope references phase(s) not in ROADMAP` — list: file, reference text, missing phase number
|
||||||
|
|
||||||
|
**Check 4 — Memory-flagged pending work**
|
||||||
|
|
||||||
|
Check if `.planning/MEMORY.md` or `.planning/memory/` exists:
|
||||||
|
```bash
|
||||||
|
ls .planning/MEMORY.md .planning/memory/*.md 2>/dev/null || true
|
||||||
|
```
|
||||||
|
|
||||||
|
If found, grep for entries containing: `pending`, `status`, `deferred`, `not yet run`, `backfill`, `blocking`.
|
||||||
|
|
||||||
|
Emit:
|
||||||
|
- ✓ `No memory entries flagging pending work` — if none found or no MEMORY.md
|
||||||
|
- ⚠ `Memory entries flag pending/deferred work` — list the matching lines (max 5, truncated at 80 chars)
|
||||||
|
|
||||||
|
**Check 5 — Blocking operational todos**
|
||||||
|
|
||||||
|
Check for pending todos:
|
||||||
|
```bash
|
||||||
|
ls .planning/todos/pending/*.md 2>/dev/null || true
|
||||||
|
```
|
||||||
|
|
||||||
|
For files found, scan for keywords indicating operational blockers: `script`, `credential`, `API key`, `manual`, `verification`, `setup`, `configure`, `run `.
|
||||||
|
|
||||||
|
Emit:
|
||||||
|
- ✓ `No blocking operational todos` — if no pending todos or none match operational keywords
|
||||||
|
- ⚠ `Blocking operational todos found` — list the file names and matching keywords (max 5)
|
||||||
|
|
||||||
|
**Check 6 — Uncommitted code**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git status --porcelain 2>/dev/null | grep -v "^??" | grep -v "^.planning\/" | grep -v "^\.\." | head -10
|
||||||
|
```
|
||||||
|
|
||||||
|
If output is non-empty (modified/staged files outside `.planning/`), flag as uncommitted code.
|
||||||
|
|
||||||
|
Emit:
|
||||||
|
- ✓ `Working tree clean` — if no modified files outside `.planning/`
|
||||||
|
- ⚠ `Uncommitted changes in source files` — list up to 10 file paths
|
||||||
|
|
||||||
|
**Check 7 — Unresolved deferred items**
|
||||||
|
|
||||||
|
Glob every phase directory's SCOPE BOUNDARY log (executor writes out-of-scope discoveries here per `agents/gsd-executor.md`):
|
||||||
|
```bash
|
||||||
|
ls .planning/phases/*/deferred-items.md 2>/dev/null || true
|
||||||
|
```
|
||||||
|
|
||||||
|
For each `deferred-items.md` found, read its entries (bullet list, one entry per top-level `- ` line, continuation lines indented beneath it). An entry is RESOLVED only if it carries an explicit `status: resolved` field (case-insensitive) on one of its lines; every other entry — including one with no `status:` field at all — is UNRESOLVED and must be surfaced (fail-safe: never silently drop a possibly-open item).
|
||||||
|
|
||||||
|
Emit:
|
||||||
|
- ✓ `No unresolved deferred items` — if no `deferred-items.md` files exist, or every entry in every file is `status: resolved`
|
||||||
|
- ⚠ `Unresolved deferred items found` — list each file's phase directory and its unresolved entry text (max 5 per file, truncated at 80 chars)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
After all 7 checks, display the verdict:
|
||||||
|
|
||||||
|
**If all 7 checks passed:**
|
||||||
|
```
|
||||||
|
### Verdict: CLEAN
|
||||||
|
|
||||||
|
The standard progress report is trustworthy — proceed with the routing suggestion above.
|
||||||
|
```
|
||||||
|
|
||||||
|
**If 1 or more checks failed:**
|
||||||
|
```
|
||||||
|
### Verdict: N INTEGRITY ISSUE(S) FOUND
|
||||||
|
|
||||||
|
The standard progress report may not reflect true project state.
|
||||||
|
Review the flagged items above before acting on the routing suggestion.
|
||||||
|
```
|
||||||
|
|
||||||
|
Then for each failed check, add a concrete next action:
|
||||||
|
- Check 2 (orphaned handoff): `Read the handoff file(s) and resume from where work was paused: /gsd:resume-work ${GSD_WS}`
|
||||||
|
- Check 3 (deferred scope): `Add the missing phases to ROADMAP.md or update the deferred references`
|
||||||
|
- Check 4 (memory pending): `Review the flagged memory entries and resolve or clear them`
|
||||||
|
- Check 5 (blocking todos): `Complete the operational steps in .planning/todos/pending/ before continuing`
|
||||||
|
- Check 6 (uncommitted code): `Commit or stash the uncommitted changes before advancing`
|
||||||
|
- Check 7 (unresolved deferred items): `Address each deferred item and mark it status: resolved in its deferred-items.md, or fold it into the roadmap`
|
||||||
|
- Check 1 (STATE inconsistency): `Run /gsd:verify-work ${PHASE} ${GSD_WS} to reconcile state`
|
||||||
|
</step>
|
||||||
18
gsd-core/workflows/progress/steps/mvp-display.md
Normal file
18
gsd-core/workflows/progress/steps/mvp-display.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<step name="mvp_display">
|
||||||
|
**MVP-mode display (when phase has `**Mode:** mvp` in ROADMAP.md).**
|
||||||
|
|
||||||
|
`init.progress` already resolved `phase_mvp_mode` for the current phase (progress has no `--mvp` CLI flag — mode is inherited from the planned phase), and this step is only read at all when that same fact is `true` (`section_manifest`'s `mvp-display` inclusion gates on it) — so there is no need to re-resolve `MVP_MODE` here via a fresh `gsd_run query phase.mvp-mode` call; that would gate this section on a fact its own body recomputes, which is circular and self-disabling. Simply consume `$phase_mvp_mode` (already extracted from init JSON in `init_context`) as `true` for the remainder of this step.
|
||||||
|
|
||||||
|
The per-phase progress block adds a **user-flow status** sub-block sourced from the phase's PLAN.md task names. Each task whose name reads like a user-visible capability (e.g., "Register flow", "Login flow", "Password reset") is rendered as a status line:
|
||||||
|
|
||||||
|
```
|
||||||
|
Phase 1 — User Auth MVP
|
||||||
|
✅ Walking Skeleton complete ← from SKELETON.md existence
|
||||||
|
✅ Register flow working ← from PLAN.md task with summary
|
||||||
|
✅ Login flow working ← from PLAN.md task with summary
|
||||||
|
🔄 Password reset (in progress) ← from PLAN.md task without summary
|
||||||
|
⬜ Email verification ← from PLAN.md task not yet started
|
||||||
|
```
|
||||||
|
|
||||||
|
**User-flow filter:** Tasks whose names are technical-sounding ("Wire DB schema", "Create migration", "Bump deps") are NOT rendered as user-flow status lines. Heuristic: a task name is user-flow-shaped if it ends in "flow", "page", "screen", or starts with a verb the user would recognize ("Register", "Login", "Upload", "View"). Tasks that fail the heuristic still count toward the standard task progress total but don't appear in the user-flow sub-block.
|
||||||
|
</step>
|
||||||
@@ -131,7 +131,11 @@ If `$VALIDATE_MODE` only:
|
|||||||
**Step 2: Initialize**
|
**Step 2: Initialize**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
INIT=$(gsd_run query init.quick "$DESCRIPTION")
|
DISCUSS_PARAM=""; if [[ "$ARGUMENTS" =~ (^|[[:space:]])--discuss([[:space:]]|$) ]]; then DISCUSS_PARAM="--discuss"; fi
|
||||||
|
RESEARCH_PARAM=""; if [[ "$ARGUMENTS" =~ (^|[[:space:]])--research([[:space:]]|$) ]]; then RESEARCH_PARAM="--research"; fi
|
||||||
|
VALIDATE_PARAM=""; if [[ "$ARGUMENTS" =~ (^|[[:space:]])--validate([[:space:]]|$) ]]; then VALIDATE_PARAM="--validate"; fi
|
||||||
|
FULL_PARAM=""; if [[ "$ARGUMENTS" =~ (^|[[:space:]])--full([[:space:]]|$) ]]; then FULL_PARAM="--full"; fi
|
||||||
|
INIT=$(gsd_run query init.quick "$DESCRIPTION" $DISCUSS_PARAM $RESEARCH_PARAM $VALIDATE_PARAM $FULL_PARAM)
|
||||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||||
AGENT_SKILLS_PLANNER=$(gsd_run query agent-skills gsd-planner)
|
AGENT_SKILLS_PLANNER=$(gsd_run query agent-skills gsd-planner)
|
||||||
AGENT_SKILLS_EXECUTOR=$(gsd_run query agent-skills gsd-executor)
|
AGENT_SKILLS_EXECUTOR=$(gsd_run query agent-skills gsd-executor)
|
||||||
@@ -276,205 +280,15 @@ Store `$QUICK_DIR` for use in orchestration.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Step 4.5: Discussion phase (only when `$DISCUSS_MODE`)**
|
<!-- gsd:section id="discussion-phase" when="flag:--discuss" -->
|
||||||
|
If `section_manifest` is `null` or `"discussion-phase"` is in its `included` list: read and execute `gsd-core/workflows/quick/steps/discussion-phase.md`. Otherwise skip — do not read the file.
|
||||||
Skip this step entirely if NOT `$DISCUSS_MODE`.
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
Display banner:
|
|
||||||
```
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
GSD ► DISCUSSING QUICK TASK
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
|
|
||||||
◆ Surfacing gray areas for: ${DESCRIPTION}
|
|
||||||
```
|
|
||||||
|
|
||||||
**4.5a. Identify gray areas**
|
|
||||||
|
|
||||||
Analyze `$DESCRIPTION` to identify 2-4 gray areas — implementation decisions that would change the outcome and that the user should weigh in on.
|
|
||||||
|
|
||||||
Use the domain-aware heuristic to generate phase-specific (not generic) gray areas:
|
|
||||||
- Something users **SEE** → layout, density, interactions, states
|
|
||||||
- Something users **CALL** → responses, errors, auth, versioning
|
|
||||||
- Something users **RUN** → output format, flags, modes, error handling
|
|
||||||
- Something users **READ** → structure, tone, depth, flow
|
|
||||||
- Something being **ORGANIZED** → criteria, grouping, naming, exceptions
|
|
||||||
|
|
||||||
Each gray area should be a concrete decision point, not a vague category. Example: "Loading behavior" not "UX".
|
|
||||||
|
|
||||||
**4.5b. Present gray areas**
|
|
||||||
|
|
||||||
```
|
|
||||||
AskUserQuestion(
|
|
||||||
header: "Gray Areas",
|
|
||||||
question: "Which areas need clarification before planning?",
|
|
||||||
options: [
|
|
||||||
{ label: "${area_1}", description: "${why_it_matters_1}" },
|
|
||||||
{ label: "${area_2}", description: "${why_it_matters_2}" },
|
|
||||||
{ label: "${area_3}", description: "${why_it_matters_3}" },
|
|
||||||
{ label: "All clear", description: "Skip discussion — I know what I want" }
|
|
||||||
],
|
|
||||||
multiSelect: true
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
If user selects "All clear" → skip to Step 5 (no CONTEXT.md written).
|
|
||||||
|
|
||||||
**4.5c. Discuss selected areas**
|
|
||||||
|
|
||||||
For each selected area, ask 1-2 focused questions via AskUserQuestion:
|
|
||||||
|
|
||||||
```
|
|
||||||
AskUserQuestion(
|
|
||||||
header: "${area_name}",
|
|
||||||
question: "${specific_question_about_this_area}",
|
|
||||||
options: [
|
|
||||||
{ label: "${concrete_choice_1}", description: "${what_this_means}" },
|
|
||||||
{ label: "${concrete_choice_2}", description: "${what_this_means}" },
|
|
||||||
{ label: "${concrete_choice_3}", description: "${what_this_means}" },
|
|
||||||
{ label: "You decide", description: "Claude's discretion" }
|
|
||||||
],
|
|
||||||
multiSelect: false
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
Rules:
|
|
||||||
- Options must be concrete choices, not abstract categories
|
|
||||||
- Highlight recommended choice where you have a clear opinion
|
|
||||||
- If user selects "Other" with freeform text, switch to plain text follow-up (per questioning.md freeform rule)
|
|
||||||
- If user selects "You decide", capture as Claude's Discretion in CONTEXT.md
|
|
||||||
- Max 2 questions per area — this is lightweight, not a deep dive
|
|
||||||
|
|
||||||
Collect all decisions into `$DECISIONS`.
|
|
||||||
|
|
||||||
**4.5d. Write CONTEXT.md**
|
|
||||||
|
|
||||||
Write `${QUICK_DIR}/${quick_id}-CONTEXT.md` using the standard context template structure:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# Quick Task ${quick_id}: ${DESCRIPTION} - Context
|
|
||||||
|
|
||||||
**Gathered:** ${date}
|
|
||||||
**Status:** Ready for planning
|
|
||||||
|
|
||||||
<domain>
|
|
||||||
## Task Boundary
|
|
||||||
|
|
||||||
${DESCRIPTION}
|
|
||||||
|
|
||||||
</domain>
|
|
||||||
|
|
||||||
<decisions>
|
|
||||||
## Implementation Decisions
|
|
||||||
|
|
||||||
### ${area_1_name}
|
|
||||||
- ${decision_from_discussion}
|
|
||||||
|
|
||||||
### ${area_2_name}
|
|
||||||
- ${decision_from_discussion}
|
|
||||||
|
|
||||||
### Claude's Discretion
|
|
||||||
${areas_where_user_said_you_decide_or_areas_not_discussed}
|
|
||||||
|
|
||||||
</decisions>
|
|
||||||
|
|
||||||
<specifics>
|
|
||||||
## Specific Ideas
|
|
||||||
|
|
||||||
${any_specific_references_or_examples_from_discussion}
|
|
||||||
|
|
||||||
[If none: "No specific requirements — open to standard approaches"]
|
|
||||||
|
|
||||||
</specifics>
|
|
||||||
|
|
||||||
<canonical_refs>
|
|
||||||
## Canonical References
|
|
||||||
|
|
||||||
${any_specs_adrs_or_docs_referenced_during_discussion}
|
|
||||||
|
|
||||||
[If none: "No external specs — requirements fully captured in decisions above"]
|
|
||||||
|
|
||||||
</canonical_refs>
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: Quick task CONTEXT.md omits `<code_context>` and `<deferred>` sections (no codebase scouting, no phase scope to defer to). Keep it lean. The `<canonical_refs>` section is included when external docs were referenced — omit it only if no external docs apply.
|
|
||||||
|
|
||||||
Report: `Context captured: ${QUICK_DIR}/${quick_id}-CONTEXT.md`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Step 4.75: Research phase (only when `$RESEARCH_MODE`)**
|
<!-- gsd:section id="research-phase" when="flag:--research" -->
|
||||||
|
If `section_manifest` is `null` or `"research-phase"` is in its `included` list: read and execute `gsd-core/workflows/quick/steps/research-phase.md`. Otherwise skip — do not read the file.
|
||||||
Skip this step entirely if NOT `$RESEARCH_MODE`.
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
Display banner:
|
|
||||||
```
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
GSD ► RESEARCHING QUICK TASK
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
|
|
||||||
◆ Investigating approaches for: ${DESCRIPTION} (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
|
||||||
```
|
|
||||||
|
|
||||||
Spawn a single focused researcher (not 4 parallel researchers like full phases — quick tasks need targeted research, not broad domain surveys):
|
|
||||||
|
|
||||||
<!-- #2517 model-omit-on-inherit -->
|
|
||||||
|
|
||||||
> **Model omission (#2517).** Omit the `model` parameter entirely when the value it would carry (`planner_model`, `checker_model`, `executor_model`, `reviewer_model`, `verifier_model`) is `"inherit"` or empty. An empty value 404s on runtimes without native tier aliases — the default on non-Claude runtimes. Omitting it inherits the orchestrator's model. See @gsd-core/references/model-profile-resolution.md.
|
|
||||||
|
|
||||||
```
|
|
||||||
Agent(
|
|
||||||
prompt="
|
|
||||||
<research_context>
|
|
||||||
|
|
||||||
**Mode:** quick-task
|
|
||||||
**Task:** ${DESCRIPTION}
|
|
||||||
**Output:** ${QUICK_DIR}/${quick_id}-RESEARCH.md
|
|
||||||
|
|
||||||
<files_to_read>
|
|
||||||
- ${STATE_PATH} (Project state — what's already built)
|
|
||||||
- ${PROJECT_PATH} (Project context)
|
|
||||||
- ./CLAUDE.md or ./.claude/CLAUDE.md (if exists — project-specific guidelines)
|
|
||||||
${DISCUSS_MODE ? '- ' + QUICK_DIR + '/' + quick_id + '-CONTEXT.md (User decisions — research should align with these)' : ''}
|
|
||||||
</files_to_read>
|
|
||||||
|
|
||||||
${AGENT_SKILLS_PLANNER}
|
|
||||||
|
|
||||||
</research_context>
|
|
||||||
|
|
||||||
<focus>
|
|
||||||
This is a quick task, not a full phase. Research should be concise and targeted:
|
|
||||||
1. Best libraries/patterns for this specific task
|
|
||||||
2. Common pitfalls and how to avoid them
|
|
||||||
3. Integration points with existing codebase
|
|
||||||
4. Any constraints or gotchas worth knowing before planning
|
|
||||||
|
|
||||||
Do NOT produce a full domain survey. Target 1-2 pages of actionable findings.
|
|
||||||
</focus>
|
|
||||||
|
|
||||||
<!-- #2508 runtime-aware-dispatch -->
|
|
||||||
|
|
||||||
> **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested <role> --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_<ROLE>}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md.
|
|
||||||
|
|
||||||
<output>
|
|
||||||
Write research to: ${QUICK_DIR}/${quick_id}-RESEARCH.md
|
|
||||||
Use standard research format but keep it lean — skip sections that don't apply.
|
|
||||||
Return: ## RESEARCH COMPLETE with file path
|
|
||||||
</output>
|
|
||||||
",
|
|
||||||
subagent_type="gsd-phase-researcher",
|
|
||||||
model="{planner_model}",
|
|
||||||
description="Research: ${DESCRIPTION}"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
> **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.
|
|
||||||
|
|
||||||
After researcher returns:
|
|
||||||
1. Verify research exists at `${QUICK_DIR}/${quick_id}-RESEARCH.md`
|
|
||||||
2. Report: "Research complete: ${QUICK_DIR}/${quick_id}-RESEARCH.md"
|
|
||||||
|
|
||||||
If research file not found, warn but continue: "Research agent did not produce output — proceeding to planning without research."
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -539,156 +353,15 @@ If plan not found, error: "Planner failed to create ${quick_id}-PLAN.md"
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Step 5.5: Plan-checker loop (only when `$VALIDATE_MODE`)**
|
<!-- gsd:section id="plan-checker-loop" when="flag:--validate" -->
|
||||||
|
If `section_manifest` is `null` or `"plan-checker-loop"` is in its `included` list: read and execute `gsd-core/workflows/quick/steps/plan-checker-loop.md`. Otherwise skip — do not read the file.
|
||||||
Skip this step entirely if NOT `$VALIDATE_MODE`.
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
Display banner:
|
|
||||||
```
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
GSD ► CHECKING PLAN
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
|
|
||||||
◆ Spawning plan checker... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
|
||||||
```
|
|
||||||
|
|
||||||
Checker prompt:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
<verification_context>
|
|
||||||
**Mode:** quick-full
|
|
||||||
**Task Description:** ${DESCRIPTION}
|
|
||||||
|
|
||||||
<files_to_read>
|
|
||||||
- ${QUICK_DIR}/${quick_id}-PLAN.md (Plan to verify)
|
|
||||||
</files_to_read>
|
|
||||||
|
|
||||||
${AGENT_SKILLS_CHECKER}
|
|
||||||
|
|
||||||
**Scope:** This is a quick task, not a full phase. Skip checks that require a ROADMAP phase goal.
|
|
||||||
</verification_context>
|
|
||||||
|
|
||||||
<check_dimensions>
|
|
||||||
- Requirement coverage: Does the plan address the task description?
|
|
||||||
- Task completeness: Do tasks have files, action, verify, done fields?
|
|
||||||
- Key links: Are referenced files real?
|
|
||||||
- Scope sanity: Is this appropriately sized for a quick task (1-3 tasks)?
|
|
||||||
- must_haves derivation: Are must_haves traceable to the task description?
|
|
||||||
|
|
||||||
Skip: cross-plan deps (single plan), ROADMAP alignment
|
|
||||||
${DISCUSS_MODE ? '- Context compliance: Does the plan honor locked decisions from CONTEXT.md?' : '- Skip: context compliance (no CONTEXT.md)'}
|
|
||||||
</check_dimensions>
|
|
||||||
|
|
||||||
<expected_output>
|
|
||||||
- ## VERIFICATION PASSED — all checks pass
|
|
||||||
- ## ISSUES FOUND — structured issue list
|
|
||||||
</expected_output>
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
Agent(
|
|
||||||
prompt=checker_prompt,
|
|
||||||
subagent_type="gsd-plan-checker",
|
|
||||||
model="{checker_model}",
|
|
||||||
description="Check quick plan: ${DESCRIPTION}"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
> **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.
|
|
||||||
|
|
||||||
**Handle checker return:**
|
|
||||||
|
|
||||||
- **`## VERIFICATION PASSED`:** Display confirmation, proceed to step 6.
|
|
||||||
- **`## ISSUES FOUND`:** Display issues, check iteration count, enter revision loop.
|
|
||||||
|
|
||||||
**Revision loop (max 2 iterations):**
|
|
||||||
|
|
||||||
Track `iteration_count` (starts at 1 after initial plan + check).
|
|
||||||
|
|
||||||
**If iteration_count < 2:**
|
|
||||||
|
|
||||||
Display: `Sending back to planner for revision... (iteration ${N}/2)`
|
|
||||||
|
|
||||||
Revision prompt:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
<revision_context>
|
|
||||||
**Mode:** quick-full (revision)
|
|
||||||
|
|
||||||
<files_to_read>
|
|
||||||
- ${QUICK_DIR}/${quick_id}-PLAN.md (Existing plan)
|
|
||||||
</files_to_read>
|
|
||||||
|
|
||||||
${AGENT_SKILLS_PLANNER}
|
|
||||||
|
|
||||||
**Checker issues:** ${structured_issues_from_checker}
|
|
||||||
|
|
||||||
</revision_context>
|
|
||||||
|
|
||||||
<instructions>
|
|
||||||
Make targeted updates to address checker issues.
|
|
||||||
Do NOT replan from scratch unless issues are fundamental.
|
|
||||||
Return what changed.
|
|
||||||
</instructions>
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
Agent(
|
|
||||||
prompt=revision_prompt,
|
|
||||||
subagent_type="gsd-planner",
|
|
||||||
model="{planner_model}",
|
|
||||||
description="Revise quick plan: ${DESCRIPTION}"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
> **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.
|
|
||||||
|
|
||||||
After planner returns → spawn checker again, increment iteration_count.
|
|
||||||
|
|
||||||
**If iteration_count >= 2:**
|
|
||||||
|
|
||||||
Display: `Max iterations reached. ${N} issues remain:` + issue list
|
|
||||||
|
|
||||||
Offer: 1) Force proceed, 2) Abort
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Step 5.6: Pre-dispatch plan commit (worktree mode only)**
|
<!-- gsd:section id="worktree-pre-dispatch-commit" when="state:worktrees-enabled" -->
|
||||||
|
If `section_manifest` is `null` or `"worktree-pre-dispatch-commit"` is in its `included` list: read and execute `gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md`. Otherwise skip — do not read the file.
|
||||||
When `USE_WORKTREES !== "false"`, commit PLAN.md to the current branch **before** spawning the executor. This ensures the worktree inherits PLAN.md at its branch HEAD so the executor can read it via a worktree-rooted path — avoiding the main-repo path priming that triggers CC #36182 path-resolution drift.
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
Skip this step entirely if `USE_WORKTREES === "false"` (non-worktree mode: PLAN.md is committed in Step 8 as usual).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
QUICK_PLAN_PARENT=""
|
|
||||||
QUICK_PLAN_COMMIT=""
|
|
||||||
if [ "${USE_WORKTREES}" != "false" ]; then
|
|
||||||
QUICK_PLAN_PARENT=$(git rev-parse HEAD)
|
|
||||||
COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
|
|
||||||
if [ "$COMMIT_DOCS" != "false" ]; then
|
|
||||||
git add "${QUICK_DIR}/${quick_id}-PLAN.md"
|
|
||||||
# No-op skip if nothing actually staged (idempotent re-runs).
|
|
||||||
if git diff --cached --quiet -- "${QUICK_DIR}/${quick_id}-PLAN.md"; then
|
|
||||||
echo "ℹ Pre-dispatch PLAN.md commit skipped (no staged changes)"
|
|
||||||
else
|
|
||||||
# Run hooks normally (#2924). If a project opts out via
|
|
||||||
# workflow.worktree_skip_hooks=true, honor that opt-in only.
|
|
||||||
SKIP_HOOKS=$(gsd_run query config-get workflow.worktree_skip_hooks 2>/dev/null || echo "false")
|
|
||||||
if [ "$SKIP_HOOKS" = "true" ]; then
|
|
||||||
git commit --no-verify -m "docs(${quick_id}): pre-dispatch plan for ${DESCRIPTION}" -- "${QUICK_DIR}/${quick_id}-PLAN.md" \
|
|
||||||
|| { echo "ERROR: pre-dispatch PLAN.md commit failed (--no-verify path). Aborting before executor dispatch." >&2; exit 1; }
|
|
||||||
else
|
|
||||||
git commit -m "docs(${quick_id}): pre-dispatch plan for ${DESCRIPTION}" -- "${QUICK_DIR}/${quick_id}-PLAN.md" \
|
|
||||||
|| { echo "ERROR: pre-dispatch PLAN.md commit failed — likely a pre-commit hook failure. Fix the hook output above (or set workflow.worktree_skip_hooks=true to bypass) and re-run." >&2; exit 1; }
|
|
||||||
fi
|
|
||||||
QUICK_PLAN_COMMIT=$(git rev-parse HEAD)
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ -z "$QUICK_PLAN_COMMIT" ]; then
|
|
||||||
QUICK_PLAN_COMMIT=$(git rev-parse HEAD)
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -901,52 +574,9 @@ If review produces findings, display advisory message. **Error handling:** Failu
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Step 6.5: Verification (only when `$VALIDATE_MODE`)**
|
<!-- gsd:section id="quick-verification" when="flag:--validate" -->
|
||||||
|
If `section_manifest` is `null` or `"quick-verification"` is in its `included` list: read and execute `gsd-core/workflows/quick/steps/quick-verification.md`. Otherwise skip — do not read the file.
|
||||||
Skip this step entirely if NOT `$VALIDATE_MODE`.
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
Display banner:
|
|
||||||
```
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
GSD ► VERIFYING RESULTS
|
|
||||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
||||||
|
|
||||||
◆ Spawning verifier... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
Agent(
|
|
||||||
prompt="Verify quick task goal achievement.
|
|
||||||
Task directory: ${QUICK_DIR}
|
|
||||||
Task goal: ${DESCRIPTION}
|
|
||||||
|
|
||||||
<files_to_read>
|
|
||||||
- ${QUICK_DIR}/${quick_id}-PLAN.md (Plan)
|
|
||||||
</files_to_read>
|
|
||||||
|
|
||||||
${AGENT_SKILLS_VERIFIER}
|
|
||||||
|
|
||||||
Check must_haves against actual codebase. Create VERIFICATION.md at ${QUICK_DIR}/${quick_id}-VERIFICATION.md.",
|
|
||||||
subagent_type="gsd-verifier",
|
|
||||||
model="{verifier_model}",
|
|
||||||
description="Verify: ${DESCRIPTION}"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
> **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.
|
|
||||||
|
|
||||||
Read verification status:
|
|
||||||
```bash
|
|
||||||
grep "^status:" "${QUICK_DIR}/${quick_id}-VERIFICATION.md" | cut -d: -f2 | tr -d ' '
|
|
||||||
```
|
|
||||||
|
|
||||||
Store as `$VERIFICATION_STATUS`.
|
|
||||||
|
|
||||||
| Status | Action |
|
|
||||||
|--------|--------|
|
|
||||||
| `passed` | Store `$VERIFICATION_STATUS = "Verified"`, continue to step 7 |
|
|
||||||
| `human_needed` | Display items needing manual check, store `$VERIFICATION_STATUS = "Needs Review"`, continue |
|
|
||||||
| `gaps_found` | Display gap summary, offer: 1) Re-run executor to fix gaps, 2) Accept as-is. Store `$VERIFICATION_STATUS = "Gaps"` |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
124
gsd-core/workflows/quick/steps/discussion-phase.md
Normal file
124
gsd-core/workflows/quick/steps/discussion-phase.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
**Step 4.5: Discussion phase (only when `$DISCUSS_MODE`)**
|
||||||
|
|
||||||
|
Skip this step entirely if NOT `$DISCUSS_MODE`.
|
||||||
|
|
||||||
|
Display banner:
|
||||||
|
```
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
GSD ► DISCUSSING QUICK TASK
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
◆ Surfacing gray areas for: ${DESCRIPTION}
|
||||||
|
```
|
||||||
|
|
||||||
|
**4.5a. Identify gray areas**
|
||||||
|
|
||||||
|
Analyze `$DESCRIPTION` to identify 2-4 gray areas — implementation decisions that would change the outcome and that the user should weigh in on.
|
||||||
|
|
||||||
|
Use the domain-aware heuristic to generate phase-specific (not generic) gray areas:
|
||||||
|
- Something users **SEE** → layout, density, interactions, states
|
||||||
|
- Something users **CALL** → responses, errors, auth, versioning
|
||||||
|
- Something users **RUN** → output format, flags, modes, error handling
|
||||||
|
- Something users **READ** → structure, tone, depth, flow
|
||||||
|
- Something being **ORGANIZED** → criteria, grouping, naming, exceptions
|
||||||
|
|
||||||
|
Each gray area should be a concrete decision point, not a vague category. Example: "Loading behavior" not "UX".
|
||||||
|
|
||||||
|
**4.5b. Present gray areas**
|
||||||
|
|
||||||
|
```
|
||||||
|
AskUserQuestion(
|
||||||
|
header: "Gray Areas",
|
||||||
|
question: "Which areas need clarification before planning?",
|
||||||
|
options: [
|
||||||
|
{ label: "${area_1}", description: "${why_it_matters_1}" },
|
||||||
|
{ label: "${area_2}", description: "${why_it_matters_2}" },
|
||||||
|
{ label: "${area_3}", description: "${why_it_matters_3}" },
|
||||||
|
{ label: "All clear", description: "Skip discussion — I know what I want" }
|
||||||
|
],
|
||||||
|
multiSelect: true
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
If user selects "All clear" → skip to Step 5 (no CONTEXT.md written).
|
||||||
|
|
||||||
|
**4.5c. Discuss selected areas**
|
||||||
|
|
||||||
|
For each selected area, ask 1-2 focused questions via AskUserQuestion:
|
||||||
|
|
||||||
|
```
|
||||||
|
AskUserQuestion(
|
||||||
|
header: "${area_name}",
|
||||||
|
question: "${specific_question_about_this_area}",
|
||||||
|
options: [
|
||||||
|
{ label: "${concrete_choice_1}", description: "${what_this_means}" },
|
||||||
|
{ label: "${concrete_choice_2}", description: "${what_this_means}" },
|
||||||
|
{ label: "${concrete_choice_3}", description: "${what_this_means}" },
|
||||||
|
{ label: "You decide", description: "Claude's discretion" }
|
||||||
|
],
|
||||||
|
multiSelect: false
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- Options must be concrete choices, not abstract categories
|
||||||
|
- Highlight recommended choice where you have a clear opinion
|
||||||
|
- If user selects "Other" with freeform text, switch to plain text follow-up (per questioning.md freeform rule)
|
||||||
|
- If user selects "You decide", capture as Claude's Discretion in CONTEXT.md
|
||||||
|
- Max 2 questions per area — this is lightweight, not a deep dive
|
||||||
|
|
||||||
|
Collect all decisions into `$DECISIONS`.
|
||||||
|
|
||||||
|
**4.5d. Write CONTEXT.md**
|
||||||
|
|
||||||
|
Write `${QUICK_DIR}/${quick_id}-CONTEXT.md` using the standard context template structure:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Quick Task ${quick_id}: ${DESCRIPTION} - Context
|
||||||
|
|
||||||
|
**Gathered:** ${date}
|
||||||
|
**Status:** Ready for planning
|
||||||
|
|
||||||
|
<domain>
|
||||||
|
## Task Boundary
|
||||||
|
|
||||||
|
${DESCRIPTION}
|
||||||
|
|
||||||
|
</domain>
|
||||||
|
|
||||||
|
<decisions>
|
||||||
|
## Implementation Decisions
|
||||||
|
|
||||||
|
### ${area_1_name}
|
||||||
|
- ${decision_from_discussion}
|
||||||
|
|
||||||
|
### ${area_2_name}
|
||||||
|
- ${decision_from_discussion}
|
||||||
|
|
||||||
|
### Claude's Discretion
|
||||||
|
${areas_where_user_said_you_decide_or_areas_not_discussed}
|
||||||
|
|
||||||
|
</decisions>
|
||||||
|
|
||||||
|
<specifics>
|
||||||
|
## Specific Ideas
|
||||||
|
|
||||||
|
${any_specific_references_or_examples_from_discussion}
|
||||||
|
|
||||||
|
[If none: "No specific requirements — open to standard approaches"]
|
||||||
|
|
||||||
|
</specifics>
|
||||||
|
|
||||||
|
<canonical_refs>
|
||||||
|
## Canonical References
|
||||||
|
|
||||||
|
${any_specs_adrs_or_docs_referenced_during_discussion}
|
||||||
|
|
||||||
|
[If none: "No external specs — requirements fully captured in decisions above"]
|
||||||
|
|
||||||
|
</canonical_refs>
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: Quick task CONTEXT.md omits `<code_context>` and `<deferred>` sections (no codebase scouting, no phase scope to defer to). Keep it lean. The `<canonical_refs>` section is included when external docs were referenced — omit it only if no external docs apply.
|
||||||
|
|
||||||
|
Report: `Context captured: ${QUICK_DIR}/${quick_id}-CONTEXT.md`
|
||||||
111
gsd-core/workflows/quick/steps/plan-checker-loop.md
Normal file
111
gsd-core/workflows/quick/steps/plan-checker-loop.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
**Step 5.5: Plan-checker loop (only when `$VALIDATE_MODE`)**
|
||||||
|
|
||||||
|
Skip this step entirely if NOT `$VALIDATE_MODE`.
|
||||||
|
|
||||||
|
Display banner:
|
||||||
|
```
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
GSD ► CHECKING PLAN
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
◆ Spawning plan checker... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||||
|
```
|
||||||
|
|
||||||
|
Checker prompt:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
<verification_context>
|
||||||
|
**Mode:** quick-full
|
||||||
|
**Task Description:** ${DESCRIPTION}
|
||||||
|
|
||||||
|
<files_to_read>
|
||||||
|
- ${QUICK_DIR}/${quick_id}-PLAN.md (Plan to verify)
|
||||||
|
</files_to_read>
|
||||||
|
|
||||||
|
${AGENT_SKILLS_CHECKER}
|
||||||
|
|
||||||
|
**Scope:** This is a quick task, not a full phase. Skip checks that require a ROADMAP phase goal.
|
||||||
|
</verification_context>
|
||||||
|
|
||||||
|
<check_dimensions>
|
||||||
|
- Requirement coverage: Does the plan address the task description?
|
||||||
|
- Task completeness: Do tasks have files, action, verify, done fields?
|
||||||
|
- Key links: Are referenced files real?
|
||||||
|
- Scope sanity: Is this appropriately sized for a quick task (1-3 tasks)?
|
||||||
|
- must_haves derivation: Are must_haves traceable to the task description?
|
||||||
|
|
||||||
|
Skip: cross-plan deps (single plan), ROADMAP alignment
|
||||||
|
${DISCUSS_MODE ? '- Context compliance: Does the plan honor locked decisions from CONTEXT.md?' : '- Skip: context compliance (no CONTEXT.md)'}
|
||||||
|
</check_dimensions>
|
||||||
|
|
||||||
|
<expected_output>
|
||||||
|
- ## VERIFICATION PASSED — all checks pass
|
||||||
|
- ## ISSUES FOUND — structured issue list
|
||||||
|
</expected_output>
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
Agent(
|
||||||
|
prompt=checker_prompt,
|
||||||
|
subagent_type="gsd-plan-checker",
|
||||||
|
model="{checker_model}",
|
||||||
|
description="Check quick plan: ${DESCRIPTION}"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
> **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.
|
||||||
|
|
||||||
|
**Handle checker return:**
|
||||||
|
|
||||||
|
- **`## VERIFICATION PASSED`:** Display confirmation, proceed to step 6.
|
||||||
|
- **`## ISSUES FOUND`:** Display issues, check iteration count, enter revision loop.
|
||||||
|
|
||||||
|
**Revision loop (max 2 iterations):**
|
||||||
|
|
||||||
|
Track `iteration_count` (starts at 1 after initial plan + check).
|
||||||
|
|
||||||
|
**If iteration_count < 2:**
|
||||||
|
|
||||||
|
Display: `Sending back to planner for revision... (iteration ${N}/2)`
|
||||||
|
|
||||||
|
Revision prompt:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
<revision_context>
|
||||||
|
**Mode:** quick-full (revision)
|
||||||
|
|
||||||
|
<files_to_read>
|
||||||
|
- ${QUICK_DIR}/${quick_id}-PLAN.md (Existing plan)
|
||||||
|
</files_to_read>
|
||||||
|
|
||||||
|
${AGENT_SKILLS_PLANNER}
|
||||||
|
|
||||||
|
**Checker issues:** ${structured_issues_from_checker}
|
||||||
|
|
||||||
|
</revision_context>
|
||||||
|
|
||||||
|
<instructions>
|
||||||
|
Make targeted updates to address checker issues.
|
||||||
|
Do NOT replan from scratch unless issues are fundamental.
|
||||||
|
Return what changed.
|
||||||
|
</instructions>
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
Agent(
|
||||||
|
prompt=revision_prompt,
|
||||||
|
subagent_type="gsd-planner",
|
||||||
|
model="{planner_model}",
|
||||||
|
description="Revise quick plan: ${DESCRIPTION}"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
> **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.
|
||||||
|
|
||||||
|
After planner returns → spawn checker again, increment iteration_count.
|
||||||
|
|
||||||
|
**If iteration_count >= 2:**
|
||||||
|
|
||||||
|
Display: `Max iterations reached. ${N} issues remain:` + issue list
|
||||||
|
|
||||||
|
Offer: 1) Force proceed, 2) Abort
|
||||||
46
gsd-core/workflows/quick/steps/quick-verification.md
Normal file
46
gsd-core/workflows/quick/steps/quick-verification.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
**Step 6.5: Verification (only when `$VALIDATE_MODE`)**
|
||||||
|
|
||||||
|
Skip this step entirely if NOT `$VALIDATE_MODE`.
|
||||||
|
|
||||||
|
Display banner:
|
||||||
|
```
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
GSD ► VERIFYING RESULTS
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
◆ Spawning verifier... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
Agent(
|
||||||
|
prompt="Verify quick task goal achievement.
|
||||||
|
Task directory: ${QUICK_DIR}
|
||||||
|
Task goal: ${DESCRIPTION}
|
||||||
|
|
||||||
|
<files_to_read>
|
||||||
|
- ${QUICK_DIR}/${quick_id}-PLAN.md (Plan)
|
||||||
|
</files_to_read>
|
||||||
|
|
||||||
|
${AGENT_SKILLS_VERIFIER}
|
||||||
|
|
||||||
|
Check must_haves against actual codebase. Create VERIFICATION.md at ${QUICK_DIR}/${quick_id}-VERIFICATION.md.",
|
||||||
|
subagent_type="gsd-verifier",
|
||||||
|
model="{verifier_model}",
|
||||||
|
description="Verify: ${DESCRIPTION}"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
> **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.
|
||||||
|
|
||||||
|
Read verification status:
|
||||||
|
```bash
|
||||||
|
grep "^status:" "${QUICK_DIR}/${quick_id}-VERIFICATION.md" | cut -d: -f2 | tr -d ' '
|
||||||
|
```
|
||||||
|
|
||||||
|
Store as `$VERIFICATION_STATUS`.
|
||||||
|
|
||||||
|
| Status | Action |
|
||||||
|
|--------|--------|
|
||||||
|
| `passed` | Store `$VERIFICATION_STATUS = "Verified"`, continue to step 7 |
|
||||||
|
| `human_needed` | Display items needing manual check, store `$VERIFICATION_STATUS = "Needs Review"`, continue |
|
||||||
|
| `gaps_found` | Display gap summary, offer: 1) Re-run executor to fix gaps, 2) Accept as-is. Store `$VERIFICATION_STATUS = "Gaps"` |
|
||||||
72
gsd-core/workflows/quick/steps/research-phase.md
Normal file
72
gsd-core/workflows/quick/steps/research-phase.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
**Step 4.75: Research phase (only when `$RESEARCH_MODE`)**
|
||||||
|
|
||||||
|
Skip this step entirely if NOT `$RESEARCH_MODE`.
|
||||||
|
|
||||||
|
Display banner:
|
||||||
|
```
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
GSD ► RESEARCHING QUICK TASK
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
◆ Investigating approaches for: ${DESCRIPTION} (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||||
|
```
|
||||||
|
|
||||||
|
Spawn a single focused researcher (not 4 parallel researchers like full phases — quick tasks need targeted research, not broad domain surveys):
|
||||||
|
|
||||||
|
<!-- #2517 model-omit-on-inherit -->
|
||||||
|
|
||||||
|
> **Model omission (#2517).** Omit the `model` parameter entirely when the value it would carry (`planner_model`, `checker_model`, `executor_model`, `reviewer_model`, `verifier_model`) is `"inherit"` or empty. An empty value 404s on runtimes without native tier aliases — the default on non-Claude runtimes. Omitting it inherits the orchestrator's model. See @gsd-core/references/model-profile-resolution.md.
|
||||||
|
|
||||||
|
<!-- #2508 runtime-aware-dispatch -->
|
||||||
|
|
||||||
|
> **Runtime-aware dispatch (#2508 Phase 4).** GSD workflows dispatch specialized subagents by role. Before dispatching on a built-in-only runtime (kimi-code — three built-ins only), resolve the role to a built-in via `gsd_run query resolve-dispatch-type --requested <role> --raw`. On named-dispatch runtimes (Claude/OpenCode/…) the role is returned unchanged; on kimi-code it maps to `coder`/`explore`/`plan` by role-suffix. The persona rides `${AGENT_SKILLS_<ROLE>}` (Phase 3) regardless. See @gsd-core/references/runtime-aware-dispatch.md.
|
||||||
|
|
||||||
|
```
|
||||||
|
Agent(
|
||||||
|
prompt="
|
||||||
|
<research_context>
|
||||||
|
|
||||||
|
**Mode:** quick-task
|
||||||
|
**Task:** ${DESCRIPTION}
|
||||||
|
**Output:** ${QUICK_DIR}/${quick_id}-RESEARCH.md
|
||||||
|
|
||||||
|
<files_to_read>
|
||||||
|
- ${STATE_PATH} (Project state — what's already built)
|
||||||
|
- ${PROJECT_PATH} (Project context)
|
||||||
|
- ./CLAUDE.md or ./.claude/CLAUDE.md (if exists — project-specific guidelines)
|
||||||
|
${DISCUSS_MODE ? '- ' + QUICK_DIR + '/' + quick_id + '-CONTEXT.md (User decisions — research should align with these)' : ''}
|
||||||
|
</files_to_read>
|
||||||
|
|
||||||
|
${AGENT_SKILLS_PLANNER}
|
||||||
|
|
||||||
|
</research_context>
|
||||||
|
|
||||||
|
<focus>
|
||||||
|
This is a quick task, not a full phase. Research should be concise and targeted:
|
||||||
|
1. Best libraries/patterns for this specific task
|
||||||
|
2. Common pitfalls and how to avoid them
|
||||||
|
3. Integration points with existing codebase
|
||||||
|
4. Any constraints or gotchas worth knowing before planning
|
||||||
|
|
||||||
|
Do NOT produce a full domain survey. Target 1-2 pages of actionable findings.
|
||||||
|
</focus>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
Write research to: ${QUICK_DIR}/${quick_id}-RESEARCH.md
|
||||||
|
Use standard research format but keep it lean — skip sections that don't apply.
|
||||||
|
Return: ## RESEARCH COMPLETE with file path
|
||||||
|
</output>
|
||||||
|
",
|
||||||
|
subagent_type="gsd-phase-researcher",
|
||||||
|
model="{planner_model}",
|
||||||
|
description="Research: ${DESCRIPTION}"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
> **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.
|
||||||
|
|
||||||
|
After researcher returns:
|
||||||
|
1. Verify research exists at `${QUICK_DIR}/${quick_id}-RESEARCH.md`
|
||||||
|
2. Report: "Research complete: ${QUICK_DIR}/${quick_id}-RESEARCH.md"
|
||||||
|
|
||||||
|
If research file not found, warn but continue: "Research agent did not produce output — proceeding to planning without research."
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
**Step 5.6: Pre-dispatch plan commit (worktree mode only)**
|
||||||
|
|
||||||
|
When `USE_WORKTREES !== "false"`, commit PLAN.md to the current branch **before** spawning the executor. This ensures the worktree inherits PLAN.md at its branch HEAD so the executor can read it via a worktree-rooted path — avoiding the main-repo path priming that triggers CC #36182 path-resolution drift.
|
||||||
|
|
||||||
|
Skip this step entirely if `USE_WORKTREES === "false"` (non-worktree mode: PLAN.md is committed in Step 8 as usual).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
||||||
|
QUICK_PLAN_PARENT=""
|
||||||
|
QUICK_PLAN_COMMIT=""
|
||||||
|
if [ "${USE_WORKTREES}" != "false" ]; then
|
||||||
|
QUICK_PLAN_PARENT=$(git rev-parse HEAD)
|
||||||
|
COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
|
||||||
|
if [ "$COMMIT_DOCS" != "false" ]; then
|
||||||
|
git add "${QUICK_DIR}/${quick_id}-PLAN.md"
|
||||||
|
# No-op skip if nothing actually staged (idempotent re-runs).
|
||||||
|
if git diff --cached --quiet -- "${QUICK_DIR}/${quick_id}-PLAN.md"; then
|
||||||
|
echo "ℹ Pre-dispatch PLAN.md commit skipped (no staged changes)"
|
||||||
|
else
|
||||||
|
# Run hooks normally (#2924). If a project opts out via
|
||||||
|
# workflow.worktree_skip_hooks=true, honor that opt-in only.
|
||||||
|
SKIP_HOOKS=$(gsd_run query config-get workflow.worktree_skip_hooks 2>/dev/null || echo "false")
|
||||||
|
if [ "$SKIP_HOOKS" = "true" ]; then
|
||||||
|
git commit --no-verify -m "docs(${quick_id}): pre-dispatch plan for ${DESCRIPTION}" -- "${QUICK_DIR}/${quick_id}-PLAN.md" \
|
||||||
|
|| { echo "ERROR: pre-dispatch PLAN.md commit failed (--no-verify path). Aborting before executor dispatch." >&2; exit 1; }
|
||||||
|
else
|
||||||
|
git commit -m "docs(${quick_id}): pre-dispatch plan for ${DESCRIPTION}" -- "${QUICK_DIR}/${quick_id}-PLAN.md" \
|
||||||
|
|| { echo "ERROR: pre-dispatch PLAN.md commit failed — likely a pre-commit hook failure. Fix the hook output above (or set workflow.worktree_skip_hooks=true to bypass) and re-run." >&2; exit 1; }
|
||||||
|
fi
|
||||||
|
QUICK_PLAN_COMMIT=$(git rev-parse HEAD)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ -z "$QUICK_PLAN_COMMIT" ]; then
|
||||||
|
QUICK_PLAN_COMMIT=$(git rev-parse HEAD)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
```
|
||||||
@@ -105,10 +105,9 @@ user who wants a preferred set has `review.default_reviewers`. Both stay lenient
|
|||||||
preference evaluated across many hosts, so a subset being present is expected, not an error
|
preference evaluated across many hosts, so a subset being present is expected, not an error
|
||||||
- If all configured reviewers are unavailable, fail with an actionable message
|
- If all configured reviewers are unavailable, fail with an actionable message
|
||||||
|
|
||||||
**Reviewer instances (#1517, optional):** if `review.reviewer_instances` is configured,
|
<!-- gsd:section id="reviewer-instances-note-1" when="state:reviewer-instances-configured" -->
|
||||||
instance names in `review.default_reviewers` run as independent identities. Resolution rules
|
If `section_manifest` is `null` or `"reviewer-instances-note-1"` is in its `included` list: read and execute `gsd-core/workflows/review/steps/reviewer-instances-note-1.md`. Otherwise skip — do not read the file.
|
||||||
are in `gsd-core/references/reviewer-instances.md` — load it lazily only when instances are
|
<!-- /gsd:section -->
|
||||||
configured. Unconfigured → default path unchanged.
|
|
||||||
|
|
||||||
If no CLIs are available:
|
If no CLIs are available:
|
||||||
```
|
```
|
||||||
@@ -156,7 +155,7 @@ Rules:
|
|||||||
Collect phase artifacts for the review prompt:
|
Collect phase artifacts for the review prompt:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
INIT=$(gsd_run query init.phase-op "${PHASE_ARG}")
|
INIT=$(gsd_run query init.review "${PHASE_ARG}")
|
||||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||||
|
|
||||||
# #2358: ONE run-scoped temp dir (portable via ${TMPDIR:-/tmp}) so overlapping
|
# #2358: ONE run-scoped temp dir (portable via ${TMPDIR:-/tmp}) so overlapping
|
||||||
@@ -306,9 +305,9 @@ An environment that genuinely hits an untrusted-hook prompt surfaces through the
|
|||||||
the empty-output stub as a dropped lane with diagnosable stderr, not silent attrition. Do not
|
the empty-output stub as a dropped lane with diagnosable stderr, not silent attrition. Do not
|
||||||
reintroduce the flag (even spelled out in prose — a regression test bans the literal file-wide).
|
reintroduce the flag (even spelled out in prose — a regression test bans the literal file-wide).
|
||||||
|
|
||||||
**Reviewer instances (#1517, optional):** instances resolve *through* a lane and are not lanes
|
<!-- gsd:section id="reviewer-instances-note-2" when="state:reviewer-instances-configured" -->
|
||||||
themselves (ADR-2782 D8). Each selected instance invokes its base `cli` with its own `model`/`agent`
|
If `section_manifest` is `null` or `"reviewer-instances-note-2"` is in its `included` list: read and execute `gsd-core/workflows/review/steps/reviewer-instances-note-2.md`. Otherwise skip — do not read the file.
|
||||||
as opaque argv. Exact invocation in `gsd-core/references/reviewer-instances.md`.
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
Lanes run **sequentially, not in parallel** — concurrent invocation trips provider rate limits.
|
Lanes run **sequentially, not in parallel** — concurrent invocation trips provider rate limits.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
**Reviewer instances (#1517, optional):** if `review.reviewer_instances` is configured,
|
||||||
|
instance names in `review.default_reviewers` run as independent identities. Resolution rules
|
||||||
|
are in `gsd-core/references/reviewer-instances.md` — load it lazily only when instances are
|
||||||
|
configured. Unconfigured → default path unchanged.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
**Reviewer instances (#1517, optional):** instances resolve *through* a lane and are not lanes
|
||||||
|
themselves (ADR-2782 D8). Each selected instance invokes its base `cli` with its own `model`/`agent`
|
||||||
|
as opaque argv. Exact invocation in `gsd-core/references/reviewer-instances.md`.
|
||||||
@@ -1,5 +1,65 @@
|
|||||||
{
|
{
|
||||||
"workflows": {
|
"workflows": {
|
||||||
|
"autonomous": [
|
||||||
|
{
|
||||||
|
"id": "converge-fail-fast",
|
||||||
|
"when": "state:plan-strategy-converge",
|
||||||
|
"read": "gsd-core/workflows/autonomous/steps/converge-fail-fast.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "converge-banner",
|
||||||
|
"when": "state:plan-strategy-converge",
|
||||||
|
"read": "gsd-core/workflows/autonomous/steps/converge-banner.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "converge-dispatch-bg",
|
||||||
|
"when": "state:plan-strategy-converge",
|
||||||
|
"read": "gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "converge-dispatch-inline",
|
||||||
|
"when": "state:plan-strategy-converge",
|
||||||
|
"read": "gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "converge-loop",
|
||||||
|
"when": "state:plan-strategy-converge",
|
||||||
|
"read": "gsd-core/workflows/autonomous/steps/converge-loop.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"code-review": [
|
||||||
|
{
|
||||||
|
"id": "structural-pre-pass",
|
||||||
|
"when": "state:fallow-enabled",
|
||||||
|
"read": "gsd-core/workflows/code-review/steps/structural-pre-pass.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dispatch-fix",
|
||||||
|
"when": "flag:--fix",
|
||||||
|
"read": "gsd-core/workflows/code-review/steps/dispatch-fix.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"complete-milestone": [
|
||||||
|
{
|
||||||
|
"id": "git-tag",
|
||||||
|
"when": "state:git-create-tag",
|
||||||
|
"read": "gsd-core/workflows/complete-milestone/steps/git-tag.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"discuss-phase-assumptions": [
|
||||||
|
{
|
||||||
|
"id": "auto-advance-dispatch",
|
||||||
|
"when": "state:auto-advance-active",
|
||||||
|
"read": "gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"docs-update": [
|
||||||
|
{
|
||||||
|
"id": "dispatch-monorepo-packages",
|
||||||
|
"when": "state:is-monorepo",
|
||||||
|
"read": "gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
"execute-phase": [
|
"execute-phase": [
|
||||||
{
|
{
|
||||||
"id": "partial-wave",
|
"id": "partial-wave",
|
||||||
@@ -17,6 +77,35 @@
|
|||||||
"read": "gsd-core/workflows/execute-phase/steps/regression-gate.md"
|
"read": "gsd-core/workflows/execute-phase/steps/regression-gate.md"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"new-milestone": [
|
||||||
|
{
|
||||||
|
"id": "project-md-milestone-write",
|
||||||
|
"when": "state:flat-mode",
|
||||||
|
"read": "gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "reset-phase-safety",
|
||||||
|
"when": "flag:--reset-phase-numbers",
|
||||||
|
"read": "gsd-core/workflows/new-milestone/steps/reset-phase-safety.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"new-project": [
|
||||||
|
{
|
||||||
|
"id": "auto-mode-detection",
|
||||||
|
"when": "flag:--auto",
|
||||||
|
"read": "gsd-core/workflows/new-project/steps/auto-mode-detection.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "codebase-map-offer",
|
||||||
|
"when": "state:needs-codebase-map",
|
||||||
|
"read": "gsd-core/workflows/new-project/steps/codebase-map-offer.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "auto-mode-config",
|
||||||
|
"when": "flag:--auto",
|
||||||
|
"read": "gsd-core/workflows/new-project/steps/auto-mode-config.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
"plan-phase": [
|
"plan-phase": [
|
||||||
{
|
{
|
||||||
"id": "reviews-prerequisite",
|
"id": "reviews-prerequisite",
|
||||||
@@ -48,6 +137,83 @@
|
|||||||
"when": "state:chunked-mode",
|
"when": "state:chunked-mode",
|
||||||
"read": "gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md"
|
"read": "gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"progress": [
|
||||||
|
{
|
||||||
|
"id": "mvp-display",
|
||||||
|
"when": "state:phase-mvp-mode",
|
||||||
|
"read": "gsd-core/workflows/progress/steps/mvp-display.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "forensic-audit",
|
||||||
|
"when": "flag:--forensic",
|
||||||
|
"read": "gsd-core/workflows/progress/steps/forensic-audit.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"quick": [
|
||||||
|
{
|
||||||
|
"id": "discussion-phase",
|
||||||
|
"when": "flag:--discuss",
|
||||||
|
"read": "gsd-core/workflows/quick/steps/discussion-phase.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "research-phase",
|
||||||
|
"when": "flag:--research",
|
||||||
|
"read": "gsd-core/workflows/quick/steps/research-phase.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "plan-checker-loop",
|
||||||
|
"when": "flag:--validate",
|
||||||
|
"read": "gsd-core/workflows/quick/steps/plan-checker-loop.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "worktree-pre-dispatch-commit",
|
||||||
|
"when": "state:worktrees-enabled",
|
||||||
|
"read": "gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "quick-verification",
|
||||||
|
"when": "flag:--validate",
|
||||||
|
"read": "gsd-core/workflows/quick/steps/quick-verification.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"review": [
|
||||||
|
{
|
||||||
|
"id": "reviewer-instances-note-1",
|
||||||
|
"when": "state:reviewer-instances-configured",
|
||||||
|
"read": "gsd-core/workflows/review/steps/reviewer-instances-note-1.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "reviewer-instances-note-2",
|
||||||
|
"when": "state:reviewer-instances-configured",
|
||||||
|
"read": "gsd-core/workflows/review/steps/reviewer-instances-note-2.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transition": [
|
||||||
|
{
|
||||||
|
"id": "workstream-collision-check",
|
||||||
|
"when": "state:workstream-active",
|
||||||
|
"read": "gsd-core/workflows/transition/steps/workstream-collision-check.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"update": [
|
||||||
|
{
|
||||||
|
"id": "channel-banner",
|
||||||
|
"when": "state:next-channel",
|
||||||
|
"read": "gsd-core/workflows/update/steps/channel-banner.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"verify-work": [
|
||||||
|
{
|
||||||
|
"id": "automated-ui-verification",
|
||||||
|
"when": "state:ui-phase-active",
|
||||||
|
"read": "gsd-core/workflows/verify-work/steps/automated-ui-verification.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "mvp-uat-framing",
|
||||||
|
"when": "state:phase-mvp-mode",
|
||||||
|
"read": "gsd-core/workflows/verify-work/steps/mvp-uat-framing.md"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -417,33 +417,20 @@ ROADMAP=$(gsd_run query roadmap.analyze)
|
|||||||
|
|
||||||
This returns all phases with goals, disk status, and completion info.
|
This returns all phases with goals, disk status, and completion info.
|
||||||
|
|
||||||
---
|
**Section-manifest gate (#2994):** `gsd_run` is already established above (`update_roadmap_and_state` step) — fetch the dedicated `init.transition` bundle for the workstream-collision-check gate below:
|
||||||
|
|
||||||
**Workstream collision check (when `is_last_phase: true`):**
|
|
||||||
|
|
||||||
Before routing to Route B, check whether other workstreams are still active.
|
|
||||||
This prevents one workstream from advancing or completing the milestone while
|
|
||||||
other workstreams are still working on their phases.
|
|
||||||
|
|
||||||
**Skip this check if NOT in workstream mode** (i.e., `GSD_WORKSTREAM` is not set / flat mode).
|
|
||||||
In flat mode, go directly to **Route B**.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Only check if we're in workstream mode
|
INIT_TRANSITION=$(gsd_run query init.transition)
|
||||||
if [ -n "$GSD_WORKSTREAM" ]; then
|
if [[ "$INIT_TRANSITION" == @file:* ]]; then INIT_TRANSITION=$(cat "${INIT_TRANSITION#@file:}"); fi
|
||||||
WS_LIST=$(gsd_run query workstream.list --raw)
|
|
||||||
fi
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Parse the JSON result. The output has `{ mode, workstreams: [...] }`.
|
Extract from `INIT_TRANSITION`: `other_active_workstreams`, `section_manifest`.
|
||||||
Each workstream entry has: `name`, `status`, `current_phase`, `phase_count`, `completed_phases`.
|
|
||||||
|
|
||||||
Filter out the current workstream (`$GSD_WORKSTREAM`) and any workstreams with
|
---
|
||||||
status containing "milestone complete" or "archived" (case-insensitive).
|
|
||||||
The remaining entries are **other active workstreams**.
|
|
||||||
|
|
||||||
- **If other active workstreams exist** → Go to **Route B1**
|
<!-- gsd:section id="workstream-collision-check" when="state:workstream-active" -->
|
||||||
- **If NO other active workstreams** (or flat mode) → Go to **Route B**
|
If `section_manifest` (from `INIT_TRANSITION`) is `null` or `"workstream-collision-check"` is in its `included` list: read and execute `gsd-core/workflows/transition/steps/workstream-collision-check.md`. Otherwise (flat mode) skip — do not read the file; go directly to **Route B**.
|
||||||
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
**Workstream collision check (when `is_last_phase: true`):**
|
||||||
|
|
||||||
|
Before routing to Route B, check whether other workstreams are still active.
|
||||||
|
This prevents one workstream from advancing or completing the milestone while
|
||||||
|
other workstreams are still working on their phases.
|
||||||
|
|
||||||
|
**Skip this check if NOT in workstream mode** (i.e., `GSD_WORKSTREAM` is not set / flat mode).
|
||||||
|
In flat mode, go directly to **Route B**.
|
||||||
|
|
||||||
|
Parse `other_active_workstreams` from `INIT_TRANSITION` (already fetched above — no
|
||||||
|
`gsd_run` call needed here). `init.transition` pre-filters this list exactly as this
|
||||||
|
check requires: it excludes the current workstream (`$GSD_WORKSTREAM`) and any
|
||||||
|
workstream whose status contains "milestone complete" or "archived"
|
||||||
|
(case-insensitive). Each remaining entry has `name` and `status`.
|
||||||
|
|
||||||
|
- **If `other_active_workstreams` is non-empty** → Go to **Route B1**
|
||||||
|
- **If `other_active_workstreams` is empty** (or flat mode) → Go to **Route B**
|
||||||
@@ -102,6 +102,21 @@ esac
|
|||||||
```
|
```
|
||||||
|
|
||||||
`TAG` is restricted to `latest`/`next` by `check-latest-version.cjs` (it rejects any other value with exit 2), so no arbitrary dist-tag can leak through. Omitting `--next`/`--rc` reproduces the prior behavior exactly: `TAG=latest`.
|
`TAG` is restricted to `latest`/`next` by `check-latest-version.cjs` (it rejects any other value with exit 2), so no arbitrary dist-tag can leak through. Omitting `--next`/`--rc` reproduces the prior behavior exactly: `TAG=latest`.
|
||||||
|
|
||||||
|
**Section-manifest gate (#2994):** reuse the `$GSD_TOOLS` already resolved by `get_installed_version` above — do NOT copy the canonical launcher preamble here, it assigns the SAME `$GSD_TOOLS` variable via a different (fixed-candidate) resolution and would silently override the value `backup_custom_files`/`restore_custom_files` (later steps) still depend on. Forward `--next`/`--rc` from `$ARGUMENTS` so `init.update`'s `state:next-channel` fact matches this step's own case-statement:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
INIT_UPDATE=""
|
||||||
|
if [ -n "$GSD_TOOLS" ]; then
|
||||||
|
case "$GSD_TOOLS" in
|
||||||
|
*.cjs) INIT_UPDATE="$(node "$GSD_TOOLS" query init.update $([ "$TAG" = "next" ] && echo --next) 2>/dev/null)" ;;
|
||||||
|
*) INIT_UPDATE="$("$GSD_TOOLS" query init.update $([ "$TAG" = "next" ] && echo --next) 2>/dev/null)" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
if [[ "$INIT_UPDATE" == @file:* ]]; then INIT_UPDATE=$(cat "${INIT_UPDATE#@file:}"); fi
|
||||||
|
```
|
||||||
|
|
||||||
|
Extract `section_manifest` from `INIT_UPDATE` — gates the `channel-banner` section in `compare_versions` below.
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="check_latest_version">
|
<step name="check_latest_version">
|
||||||
@@ -152,13 +167,9 @@ Exit.
|
|||||||
<step name="compare_versions">
|
<step name="compare_versions">
|
||||||
Compare installed vs latest:
|
Compare installed vs latest:
|
||||||
|
|
||||||
**Only when `TAG=next`** (the user passed `--next`/`--rc`), prepend a channel banner so they know they are leaving the stable line — add this line immediately after the `**Latest:**` line in whichever output block renders:
|
<!-- gsd:section id="channel-banner" when="state:next-channel" -->
|
||||||
|
If `section_manifest` (from `INIT_UPDATE`) is `null` or `"channel-banner"` is in its `included` list: read and execute `gsd-core/workflows/update/steps/channel-banner.md`. Otherwise (default stable channel) skip — do not read the file; the output must match the prior stable behavior exactly, with no channel line.
|
||||||
**Channel:** {CHANNEL_LABEL}
|
<!-- /gsd:section -->
|
||||||
|
|
||||||
On the default stable channel (`TAG=latest`), do NOT add a channel line — the output must match the prior stable behavior exactly.
|
|
||||||
|
|
||||||
When `TAG=next`, the "latest" value is the release candidate published under `@next` (e.g. `1.4.0-rc.1`). Apply standard semver precedence for prereleases (`1.4.0-rc.1` is newer than `1.3.1` but older than the final `1.4.0`). Do NOT treat an `-rc.N` suffix as a dev install or as "behind" — offer it as an available update.
|
|
||||||
|
|
||||||
**If installed == latest:**
|
**If installed == latest:**
|
||||||
```
|
```
|
||||||
|
|||||||
7
gsd-core/workflows/update/steps/channel-banner.md
Normal file
7
gsd-core/workflows/update/steps/channel-banner.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
**Only when `TAG=next`** (the user passed `--next`/`--rc`), prepend a channel banner so they know they are leaving the stable line — add this line immediately after the `**Latest:**` line in whichever output block renders:
|
||||||
|
|
||||||
|
**Channel:** {CHANNEL_LABEL}
|
||||||
|
|
||||||
|
On the default stable channel (`TAG=latest`), do NOT add a channel line — the output must match the prior stable behavior exactly.
|
||||||
|
|
||||||
|
When `TAG=next`, the "latest" value is the release candidate published under `@next` (e.g. `1.4.0-rc.1`). Apply standard semver precedence for prereleases (`1.4.0-rc.1` is newer than `1.3.1` but older than the final `1.4.0`). Do NOT treat an `-rc.N` suffix as a dev install or as "behind" — offer it as an available update.
|
||||||
@@ -39,8 +39,8 @@ If $ARGUMENTS contains a phase number, load context:
|
|||||||
```bash
|
```bash
|
||||||
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
||||||
GSD_WS=""
|
GSD_WS=""
|
||||||
echo "$ARGUMENTS" | grep -qE -- '--ws[[:space:]]+[^[:space:]]+' && GSD_WS=$(echo "$ARGUMENTS" | grep -oE -- '--ws[[:space:]]+[^[:space:]]+')
|
echo "$ARGUMENTS" | grep -qE -- '--ws[[:space:]]+[A-Za-z0-9._-]+' && GSD_WS=$(echo "$ARGUMENTS" | grep -oE -- '--ws[[:space:]]+[A-Za-z0-9._-]+')
|
||||||
PHASE_ARG=$(echo "$ARGUMENTS" | sed -E 's/--ws[[:space:]]+[^[:space:]]+//g' | xargs)
|
PHASE_ARG=$(echo "$ARGUMENTS" | sed -E 's/--ws[[:space:]]+[A-Za-z0-9._-]+//g' | xargs)
|
||||||
|
|
||||||
INIT=$(gsd_run query init.verify-work "${PHASE_ARG}" ${GSD_WS})
|
INIT=$(gsd_run query init.verify-work "${PHASE_ARG}" ${GSD_WS})
|
||||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||||
@@ -145,42 +145,9 @@ Provide a phase number to start testing (e.g., /gsd:verify-work 4)
|
|||||||
Continue to `create_uat_file`.
|
Continue to `create_uat_file`.
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="automated_ui_verification">
|
<!-- gsd:section id="automated-ui-verification" when="state:ui-phase-active" -->
|
||||||
**Automated UI Verification (when Playwright-MCP is available)**
|
If `section_manifest` is `null` or `"automated-ui-verification"` is in its `included` list: read and execute `gsd-core/workflows/verify-work/steps/automated-ui-verification.md`. Otherwise skip — do not read the file.
|
||||||
|
<!-- /gsd:section -->
|
||||||
Before UAT, check UI capability activation and whether Playwright/Puppeteer MCP tools are available.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
PLAN_HOOKS_JSON=$(gsd_run loop render-hooks plan:pre --raw)
|
|
||||||
UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
|
||||||
```
|
|
||||||
|
|
||||||
Set `UI_PHASE_ACTIVE=true` when `PLAN_HOOKS_JSON.activeHooks` contains an active `ui` step hook.
|
|
||||||
|
|
||||||
**If Playwright-MCP tools are available in this session (`mcp__playwright__*` tools
|
|
||||||
respond to tool calls) AND (`UI_PHASE_ACTIVE` is `true` OR `UI_SPEC_FILE` is non-empty):**
|
|
||||||
|
|
||||||
For each UI checkpoint listed in the phase's UI-SPEC.md (or inferred from SUMMARY.md):
|
|
||||||
|
|
||||||
1. Use `mcp__playwright__navigate` (or equivalent) to open the component's URL.
|
|
||||||
2. Use `mcp__playwright__screenshot` to capture a screenshot.
|
|
||||||
3. Compare the screenshot visually against the spec's stated requirements
|
|
||||||
(dimensions, color, layout, spacing).
|
|
||||||
4. Automatically mark checkpoints as **passed** or **needs review** based on the
|
|
||||||
visual comparison — no manual question required for items that clearly match.
|
|
||||||
5. Flag items that require human judgment (subjective aesthetics, content accuracy)
|
|
||||||
and present only those as manual UAT questions.
|
|
||||||
|
|
||||||
If automated verification is not available, fall back to the standard manual
|
|
||||||
checkpoint questions defined in this workflow unchanged. This step is entirely
|
|
||||||
conditional: if Playwright-MCP is not configured, behavior is unchanged from today.
|
|
||||||
|
|
||||||
**Display summary line before proceeding:**
|
|
||||||
```
|
|
||||||
UI checkpoints: {N} auto-verified, {M} queued for manual review
|
|
||||||
```
|
|
||||||
|
|
||||||
</step>
|
|
||||||
|
|
||||||
<step name="find_summaries">
|
<step name="find_summaries">
|
||||||
**Find what to test:**
|
**Find what to test:**
|
||||||
@@ -195,29 +162,12 @@ Read each SUMMARY.md to extract testable deliverables.
|
|||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="extract_tests">
|
<step name="extract_tests">
|
||||||
**MVP-mode UAT framing.** When `MVP_MODE=true`, follow the rules in `@~/.claude/gsd-core/references/verify-mvp-mode.md`. Briefly:
|
<!-- gsd:section id="mvp-uat-framing" when="state:phase-mvp-mode" -->
|
||||||
|
If `section_manifest` is `null` or `"mvp-uat-framing"` is in its `included` list: read and execute `gsd-core/workflows/verify-work/steps/mvp-uat-framing.md`. Otherwise skip — do not read the file.
|
||||||
1. Generate the UAT script in three ordered sections: (a) user-flow walk-through derived from the phase's user-story goal, (b) technical checks (deferred — only run after user flow passes), (c) coverage check (goal-backward, narrowed to the user story's outcome clause).
|
<!-- /gsd:section -->
|
||||||
2. **User-flow steps run first.** Each step is one user action: open, fill, click, type, observe. No HTTP verbs, no JSON shapes, no error codes in user-flow steps.
|
|
||||||
3. **Technical checks are deferred.** They run AFTER the user flow passes — same checks as non-MVP mode (endpoint schemas, error states, edge cases), just reordered.
|
|
||||||
4. **If user-flow step N fails, do not advance.** The verdict is FAIL; technical checks do not run. The user can re-run after fixing the underlying flow.
|
|
||||||
|
|
||||||
When `MVP_MODE=false` (mode is null, absent, or the phase has no `**Mode:**` line in ROADMAP.md), fall back to the standard UAT generation path — no behavioral change.
|
When `MVP_MODE=false` (mode is null, absent, or the phase has no `**Mode:**` line in ROADMAP.md), fall back to the standard UAT generation path — no behavioral change.
|
||||||
|
|
||||||
**User-story format guard.** When `MVP_MODE=true`, also verify the phase's goal is in User Story format via the centralized validator:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
PHASE_GOAL=$(gsd_run query roadmap.get-phase "${phase_number}" ${GSD_WS} --pick goal)
|
|
||||||
USER_STORY_VALID=$(gsd_run query user-story.validate --story "$PHASE_GOAL" --pick valid)
|
|
||||||
if [ "$USER_STORY_VALID" != "true" ]; then
|
|
||||||
echo "Phase ${phase_number} has '**Mode:** mvp' in ROADMAP.md but the **Goal:** is not in user-story format."
|
|
||||||
echo "Run /gsd mvp-phase ${phase_number} to set a user-story goal before verifying."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
The verb owns the canonical regex `/^As a .+, I want to .+, so that .+\.$/` and returns slot extractions plus per-error guidance when invalid. Halt UAT generation on failure — never attempt to derive user-flow steps from a non-User-Story goal (low-quality UAT).
|
|
||||||
|
|
||||||
**Coverage-aware deterministic classification (#1602).** Before deriving checkpoints from prose, classify each SUMMARY's structured `coverage:` block. For each `*-SUMMARY.md`:
|
**Coverage-aware deterministic classification (#1602).** Before deriving checkpoints from prose, classify each SUMMARY's structured `coverage:` block. For each `*-SUMMARY.md`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<step name="automated_ui_verification">
|
||||||
|
**Automated UI Verification (when Playwright-MCP is available)**
|
||||||
|
|
||||||
|
Before UAT, check whether Playwright/Puppeteer MCP tools are available. UI-phase
|
||||||
|
activation itself (`ui_phase_active`) is already resolved at init time — this
|
||||||
|
section is only reached when that fact is `true` (see the `state:ui-phase-active`
|
||||||
|
gate above), so re-deriving it here would be redundant.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
||||||
|
```
|
||||||
|
|
||||||
|
**If Playwright-MCP tools are available in this session (`mcp__playwright__*` tools
|
||||||
|
respond to tool calls):**
|
||||||
|
|
||||||
|
For each UI checkpoint listed in the phase's UI-SPEC.md (or inferred from SUMMARY.md):
|
||||||
|
|
||||||
|
1. Use `mcp__playwright__navigate` (or equivalent) to open the component's URL.
|
||||||
|
2. Use `mcp__playwright__screenshot` to capture a screenshot.
|
||||||
|
3. Compare the screenshot visually against the spec's stated requirements
|
||||||
|
(dimensions, color, layout, spacing).
|
||||||
|
4. Automatically mark checkpoints as **passed** or **needs review** based on the
|
||||||
|
visual comparison — no manual question required for items that clearly match.
|
||||||
|
5. Flag items that require human judgment (subjective aesthetics, content accuracy)
|
||||||
|
and present only those as manual UAT questions.
|
||||||
|
|
||||||
|
If automated verification is not available, fall back to the standard manual
|
||||||
|
checkpoint questions defined in this workflow unchanged. This step is entirely
|
||||||
|
conditional: if Playwright-MCP is not configured, behavior is unchanged from today.
|
||||||
|
|
||||||
|
**Display summary line before proceeding:**
|
||||||
|
```
|
||||||
|
UI checkpoints: {N} auto-verified, {M} queued for manual review
|
||||||
|
```
|
||||||
|
|
||||||
|
</step>
|
||||||
21
gsd-core/workflows/verify-work/steps/mvp-uat-framing.md
Normal file
21
gsd-core/workflows/verify-work/steps/mvp-uat-framing.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
**MVP-mode UAT framing.** When `MVP_MODE=true`, follow the rules in `@~/.claude/gsd-core/references/verify-mvp-mode.md`. Briefly:
|
||||||
|
|
||||||
|
1. Generate the UAT script in three ordered sections: (a) user-flow walk-through derived from the phase's user-story goal, (b) technical checks (deferred — only run after user flow passes), (c) coverage check (goal-backward, narrowed to the user story's outcome clause).
|
||||||
|
2. **User-flow steps run first.** Each step is one user action: open, fill, click, type, observe. No HTTP verbs, no JSON shapes, no error codes in user-flow steps.
|
||||||
|
3. **Technical checks are deferred.** They run AFTER the user flow passes — same checks as non-MVP mode (endpoint schemas, error states, edge cases), just reordered.
|
||||||
|
4. **If user-flow step N fails, do not advance.** The verdict is FAIL; technical checks do not run. The user can re-run after fixing the underlying flow.
|
||||||
|
|
||||||
|
**User-story format guard.** When `MVP_MODE=true`, also verify the phase's goal is in User Story format via the centralized validator:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
||||||
|
PHASE_GOAL=$(gsd_run query roadmap.get-phase "${phase_number}" ${GSD_WS} --pick goal)
|
||||||
|
USER_STORY_VALID=$(gsd_run query user-story.validate --story "$PHASE_GOAL" --pick valid)
|
||||||
|
if [ "$USER_STORY_VALID" != "true" ]; then
|
||||||
|
echo "Phase ${phase_number} has '**Mode:** mvp' in ROADMAP.md but the **Goal:** is not in user-story format."
|
||||||
|
echo "Run /gsd mvp-phase ${phase_number} to set a user-story goal before verifying."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
The verb owns the canonical regex `/^As a .+, I want to .+, so that .+\.$/` and returns slot extractions plus per-error guidance when invalid. Halt UAT generation on failure — never attempt to derive user-flow steps from a non-User-Story goal (low-quality UAT).
|
||||||
@@ -344,6 +344,30 @@ export const INIT_COMMAND_ALIASES: CommandAlias[] = [
|
|||||||
"subcommand": "phase-op",
|
"subcommand": "phase-op",
|
||||||
"mutation": false
|
"mutation": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"canonical": "init.code-review",
|
||||||
|
"aliases": [
|
||||||
|
"init code-review"
|
||||||
|
],
|
||||||
|
"subcommand": "code-review",
|
||||||
|
"mutation": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"canonical": "init.review",
|
||||||
|
"aliases": [
|
||||||
|
"init review"
|
||||||
|
],
|
||||||
|
"subcommand": "review",
|
||||||
|
"mutation": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"canonical": "init.discuss-phase-assumptions",
|
||||||
|
"aliases": [
|
||||||
|
"init discuss-phase-assumptions"
|
||||||
|
],
|
||||||
|
"subcommand": "discuss-phase-assumptions",
|
||||||
|
"mutation": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"canonical": "init.todos",
|
"canonical": "init.todos",
|
||||||
"aliases": [
|
"aliases": [
|
||||||
@@ -384,6 +408,46 @@ export const INIT_COMMAND_ALIASES: CommandAlias[] = [
|
|||||||
"subcommand": "manager",
|
"subcommand": "manager",
|
||||||
"mutation": false
|
"mutation": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"canonical": "init.complete-milestone",
|
||||||
|
"aliases": [
|
||||||
|
"init complete-milestone"
|
||||||
|
],
|
||||||
|
"subcommand": "complete-milestone",
|
||||||
|
"mutation": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"canonical": "init.autonomous",
|
||||||
|
"aliases": [
|
||||||
|
"init autonomous"
|
||||||
|
],
|
||||||
|
"subcommand": "autonomous",
|
||||||
|
"mutation": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"canonical": "init.docs-update",
|
||||||
|
"aliases": [
|
||||||
|
"init docs-update"
|
||||||
|
],
|
||||||
|
"subcommand": "docs-update",
|
||||||
|
"mutation": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"canonical": "init.update",
|
||||||
|
"aliases": [
|
||||||
|
"init update"
|
||||||
|
],
|
||||||
|
"subcommand": "update",
|
||||||
|
"mutation": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"canonical": "init.transition",
|
||||||
|
"aliases": [
|
||||||
|
"init transition"
|
||||||
|
],
|
||||||
|
"subcommand": "transition",
|
||||||
|
"mutation": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"canonical": "init.new-workspace",
|
"canonical": "init.new-workspace",
|
||||||
"aliases": [
|
"aliases": [
|
||||||
|
|||||||
@@ -295,4 +295,8 @@ function cmdDocsInit(cwd: string, raw: boolean): void {
|
|||||||
output(result, raw, undefined);
|
output(result, raw, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
export = { cmdDocsInit };
|
// #2994: `detectMonorepoWorkspaces` is additionally exported so
|
||||||
|
// `cmdInitDocsUpdate` (src/init.cts) can reuse the SAME detector that backs
|
||||||
|
// this command's own `monorepo_workspaces` field, rather than a second,
|
||||||
|
// divergence-prone monorepo-glob scan (DEFECT.GENERATIVE-FIX dual surface).
|
||||||
|
export = { cmdDocsInit, detectMonorepoWorkspaces };
|
||||||
|
|||||||
@@ -34,11 +34,19 @@ interface InitModule {
|
|||||||
cmdInitResume(cwd: string, raw: boolean): void;
|
cmdInitResume(cwd: string, raw: boolean): void;
|
||||||
cmdInitVerifyWork(cwd: string, phase: string | undefined, raw: boolean): void;
|
cmdInitVerifyWork(cwd: string, phase: string | undefined, raw: boolean): void;
|
||||||
cmdInitPhaseOp(cwd: string, phase: string | undefined, raw: boolean): void;
|
cmdInitPhaseOp(cwd: string, phase: string | undefined, raw: boolean): void;
|
||||||
|
cmdInitCodeReview(cwd: string, phase: string | undefined, raw: boolean, options?: Record<string, string | boolean | null | undefined>): void;
|
||||||
|
cmdInitReview(cwd: string, phase: string | undefined, raw: boolean, options?: Record<string, string | boolean | null | undefined>): void;
|
||||||
|
cmdInitDiscussPhaseAssumptions(cwd: string, phase: string | undefined, raw: boolean, options?: Record<string, string | boolean | null | undefined>): void;
|
||||||
cmdInitTodos(cwd: string, phase: string | undefined, raw: boolean): void;
|
cmdInitTodos(cwd: string, phase: string | undefined, raw: boolean): void;
|
||||||
cmdInitMilestoneOp(cwd: string, raw: boolean): void;
|
cmdInitMilestoneOp(cwd: string, raw: boolean): void;
|
||||||
cmdInitMapCodebase(cwd: string, raw: boolean): void;
|
cmdInitMapCodebase(cwd: string, raw: boolean): void;
|
||||||
cmdInitProgress(cwd: string, raw: boolean, options?: Record<string, string | boolean | null | undefined>): void;
|
cmdInitProgress(cwd: string, raw: boolean, options?: Record<string, string | boolean | null | undefined>): void;
|
||||||
cmdInitManager(cwd: string, raw: boolean): void;
|
cmdInitManager(cwd: string, raw: boolean): void;
|
||||||
|
cmdInitCompleteMilestone(cwd: string, raw: boolean, options?: Record<string, string | boolean | null | undefined>): void;
|
||||||
|
cmdInitAutonomous(cwd: string, raw: boolean, options?: Record<string, string | boolean | null | undefined>): void;
|
||||||
|
cmdInitDocsUpdate(cwd: string, raw: boolean, options?: Record<string, string | boolean | null | undefined>): void;
|
||||||
|
cmdInitUpdate(cwd: string, raw: boolean, options?: Record<string, string | boolean | null | undefined>): void;
|
||||||
|
cmdInitTransition(cwd: string, raw: boolean, options?: Record<string, string | boolean | null | undefined>): void;
|
||||||
cmdInitNewWorkspace(cwd: string, raw: boolean): void;
|
cmdInitNewWorkspace(cwd: string, raw: boolean): void;
|
||||||
cmdInitListWorkspaces(cwd: string, raw: boolean): void;
|
cmdInitListWorkspaces(cwd: string, raw: boolean): void;
|
||||||
cmdInitRemoveWorkspace(cwd: string, name: string | undefined, raw: boolean): void;
|
cmdInitRemoveWorkspace(cwd: string, name: string | undefined, raw: boolean): void;
|
||||||
@@ -108,7 +116,18 @@ function routeInitCommand({ init, args, cwd, raw, error }: RouteInitCommandOptio
|
|||||||
},
|
},
|
||||||
quick: () => {
|
quick: () => {
|
||||||
const namedArgs = parseNamedArgs(args, [], ['discuss', 'research', 'validate', 'full']);
|
const namedArgs = parseNamedArgs(args, [], ['discuss', 'research', 'validate', 'full']);
|
||||||
init.cmdInitQuick(cwd, args.slice(2).join(' '), raw, {
|
// #2994: `args.slice(2)` is the free-text description, but section-manifest
|
||||||
|
// gating (buildSectionManifestField, src/init.cts) now requires forwarding
|
||||||
|
// --discuss/--research/--validate/--full alongside it — a plain `.join(' ')`
|
||||||
|
// would otherwise fold those recognized flag tokens straight into the
|
||||||
|
// description text. Strip them before joining so the description stays
|
||||||
|
// exactly what it was before this workflow started forwarding flags.
|
||||||
|
const quickFlagTokens = new Set(['--discuss', '--research', '--validate', '--full']);
|
||||||
|
const description = args
|
||||||
|
.slice(2)
|
||||||
|
.filter((token) => !quickFlagTokens.has(token))
|
||||||
|
.join(' ');
|
||||||
|
init.cmdInitQuick(cwd, description, raw, {
|
||||||
discuss: namedArgs['discuss'],
|
discuss: namedArgs['discuss'],
|
||||||
research: namedArgs['research'],
|
research: namedArgs['research'],
|
||||||
validate: namedArgs['validate'],
|
validate: namedArgs['validate'],
|
||||||
@@ -119,6 +138,15 @@ function routeInitCommand({ init, args, cwd, raw, error }: RouteInitCommandOptio
|
|||||||
resume: () => init.cmdInitResume(cwd, raw),
|
resume: () => init.cmdInitResume(cwd, raw),
|
||||||
'verify-work': () => init.cmdInitVerifyWork(cwd, args[2], raw),
|
'verify-work': () => init.cmdInitVerifyWork(cwd, args[2], raw),
|
||||||
'phase-op': () => init.cmdInitPhaseOp(cwd, args[2], raw),
|
'phase-op': () => init.cmdInitPhaseOp(cwd, args[2], raw),
|
||||||
|
'code-review': () => {
|
||||||
|
const namedArgs = parseNamedArgs(args, [], ['fix']);
|
||||||
|
init.cmdInitCodeReview(cwd, args[2], raw, { fix: namedArgs['fix'] });
|
||||||
|
},
|
||||||
|
review: () => init.cmdInitReview(cwd, args[2], raw, {}),
|
||||||
|
'discuss-phase-assumptions': () => {
|
||||||
|
const namedArgs = parseNamedArgs(args, [], ['auto']);
|
||||||
|
init.cmdInitDiscussPhaseAssumptions(cwd, args[2], raw, { auto: namedArgs['auto'] });
|
||||||
|
},
|
||||||
todos: () => init.cmdInitTodos(cwd, args[2], raw),
|
todos: () => init.cmdInitTodos(cwd, args[2], raw),
|
||||||
'milestone-op': () => init.cmdInitMilestoneOp(cwd, raw),
|
'milestone-op': () => init.cmdInitMilestoneOp(cwd, raw),
|
||||||
'map-codebase': () => init.cmdInitMapCodebase(cwd, raw),
|
'map-codebase': () => init.cmdInitMapCodebase(cwd, raw),
|
||||||
@@ -129,6 +157,20 @@ function routeInitCommand({ init, args, cwd, raw, error }: RouteInitCommandOptio
|
|||||||
// Keep manager on CJS for now so runtime-specific command rendering
|
// Keep manager on CJS for now so runtime-specific command rendering
|
||||||
// (e.g. $gsd-* for codex) stays consistent with runtime-slash helpers.
|
// (e.g. $gsd-* for codex) stays consistent with runtime-slash helpers.
|
||||||
manager: () => init.cmdInitManager(cwd, raw),
|
manager: () => init.cmdInitManager(cwd, raw),
|
||||||
|
'complete-milestone': () => init.cmdInitCompleteMilestone(cwd, raw),
|
||||||
|
autonomous: () => {
|
||||||
|
const namedArgs = parseNamedArgs(args, [], ['converge', 'cross-ai']);
|
||||||
|
init.cmdInitAutonomous(cwd, raw, {
|
||||||
|
converge: namedArgs['converge'],
|
||||||
|
'cross-ai': namedArgs['cross-ai'],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'docs-update': () => init.cmdInitDocsUpdate(cwd, raw, {}),
|
||||||
|
update: () => {
|
||||||
|
const namedArgs = parseNamedArgs(args, [], ['next', 'rc']);
|
||||||
|
init.cmdInitUpdate(cwd, raw, { next: namedArgs['next'], rc: namedArgs['rc'] });
|
||||||
|
},
|
||||||
|
transition: () => init.cmdInitTransition(cwd, raw, {}),
|
||||||
'new-workspace': () => init.cmdInitNewWorkspace(cwd, raw),
|
'new-workspace': () => init.cmdInitNewWorkspace(cwd, raw),
|
||||||
'list-workspaces': () => init.cmdInitListWorkspaces(cwd, raw),
|
'list-workspaces': () => init.cmdInitListWorkspaces(cwd, raw),
|
||||||
'remove-workspace': () => init.cmdInitRemoveWorkspace(cwd, args[2], raw),
|
'remove-workspace': () => init.cmdInitRemoveWorkspace(cwd, args[2], raw),
|
||||||
|
|||||||
811
src/init.cts
811
src/init.cts
@@ -49,6 +49,18 @@ import uatPredicateMod = require('./uat-predicate.cjs');
|
|||||||
import agentInstallCheck = require('./agent-install-check.cjs');
|
import agentInstallCheck = require('./agent-install-check.cjs');
|
||||||
// eslint-disable-next-line @typescript-eslint/no-require-imports -- section-manifest.cjs is compiled from section-manifest.cts's named exports; imported as a namespace to read selectSections/SelectableSection/InvocationFacts off module.exports directly (#2932).
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- section-manifest.cjs is compiled from section-manifest.cts's named exports; imported as a namespace to read selectSections/SelectableSection/InvocationFacts off module.exports directly (#2932).
|
||||||
import sectionManifest = require('./section-manifest.cjs');
|
import sectionManifest = require('./section-manifest.cjs');
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- loop-resolver.cjs is an export= CommonJS module
|
||||||
|
import loopResolverMod = require('./loop-resolver.cjs');
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- capability-loader.cjs is compiled from capability-loader.cts's named exports; imported as a namespace to read loadRegistry off module.exports directly.
|
||||||
|
import capabilityLoaderMod = require('./capability-loader.cjs');
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- capability-state.cjs is an export= CommonJS module
|
||||||
|
import capabilityStateMod = require('./capability-state.cjs');
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- docs.cjs is an export= CommonJS module
|
||||||
|
import docsMod = require('./docs.cjs');
|
||||||
|
const { detectMonorepoWorkspaces } = docsMod;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- workstream-inventory.cjs is an export= CommonJS module
|
||||||
|
import workstreamInventoryMod = require('./workstream-inventory.cjs');
|
||||||
|
const { getOtherActiveWorkstreamInventories } = workstreamInventoryMod;
|
||||||
const { checkAgentsInstalled } = agentInstallCheck;
|
const { checkAgentsInstalled } = agentInstallCheck;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-require-imports -- git-base-branch.cjs is an export= CommonJS module
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- git-base-branch.cjs is an export= CommonJS module
|
||||||
import gitBaseBranch = require('./git-base-branch.cjs');
|
import gitBaseBranch = require('./git-base-branch.cjs');
|
||||||
@@ -92,6 +104,9 @@ const { determinePhaseStatus } = commandsMod;
|
|||||||
const { extractFrontmatter } = frontmatterMod;
|
const { extractFrontmatter } = frontmatterMod;
|
||||||
const { readVerificationStatus } = verificationMod;
|
const { readVerificationStatus } = verificationMod;
|
||||||
const { evaluateUatPassed } = uatPredicateMod;
|
const { evaluateUatPassed } = uatPredicateMod;
|
||||||
|
const { resolveLoopHooks } = loopResolverMod;
|
||||||
|
const { loadRegistry } = capabilityLoaderMod;
|
||||||
|
const { resolveCapabilityRuntimeState } = capabilityStateMod;
|
||||||
|
|
||||||
// Unused but imported for structural parity
|
// Unused but imported for structural parity
|
||||||
void stripShippedMilestones;
|
void stripShippedMilestones;
|
||||||
@@ -149,6 +164,53 @@ function guardedGetRoadmapPhase(
|
|||||||
return roadmapPhase;
|
return roadmapPhase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #2994: `phase_slug` is re-derived from a roadmap-only `phase_name` (no disk
|
||||||
|
// directory exists yet) identically at every synthetic-fallback call site
|
||||||
|
// below — factored out once so the slugification formula itself cannot drift.
|
||||||
|
function slugifyPhaseName(phaseName: string | null): string | null {
|
||||||
|
return phaseName
|
||||||
|
? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #2994 (review finding, DEFECT.GENERATIVE-FIX): shared archived/not-found
|
||||||
|
* fallback applied identically by `cmdInitExecutePhase`, `cmdInitPlanPhase`,
|
||||||
|
* `cmdInitVerifyWork`, `cmdInitCodeReview`, `cmdInitReview`, and
|
||||||
|
* `cmdInitDiscussPhaseAssumptions` — 6 call sites previously reproducing the
|
||||||
|
* exact same two-branch control flow verbatim (only the synthetic
|
||||||
|
* replacement object's field set differs per caller, supplied here via
|
||||||
|
* `buildFallback`). `cmdInitPhaseOp` is deliberately left untouched (CRITICAL
|
||||||
|
* blast radius, 179 dependents) even though it follows the same shape, since
|
||||||
|
* its own fallback object differs by one field (`has_reviews` absent) and is
|
||||||
|
* not a byte-identical copy.
|
||||||
|
*
|
||||||
|
* Behavior-preserving by construction: every original call site either (a)
|
||||||
|
* unconditionally computed `roadmapPhase` once up front and then applied
|
||||||
|
* `phaseInfo?.archived && roadmapPhase?.found -> null` followed by
|
||||||
|
* `!phaseInfo && roadmapPhase?.found -> fallback`, or (b) computed
|
||||||
|
* `roadmapPhase` lazily inside each of those same two conditions. Because
|
||||||
|
* `guardedGetRoadmapPhase` is a pure, side-effect-free read for a given
|
||||||
|
* `(cwd, phase, projectCode)` within one command invocation, both shapes
|
||||||
|
* return identical results for identical inputs — so passing one
|
||||||
|
* unconditionally-resolved `roadmapPhase` in here (mirroring shape (a))
|
||||||
|
* reproduces shape (b)'s output exactly, just without the redundant second
|
||||||
|
* disk read shape (b) performed when the first branch already resolved it.
|
||||||
|
*/
|
||||||
|
function applyRoadmapFallback(
|
||||||
|
phaseInfo: Record<string, unknown> | null,
|
||||||
|
roadmapPhase: Record<string, unknown> | null,
|
||||||
|
buildFallback: (roadmapPhase: Record<string, unknown>) => Record<string, unknown>,
|
||||||
|
): Record<string, unknown> | null {
|
||||||
|
if (phaseInfo?.['archived'] && roadmapPhase?.['found']) {
|
||||||
|
phaseInfo = null;
|
||||||
|
}
|
||||||
|
if (!phaseInfo && roadmapPhase?.['found']) {
|
||||||
|
phaseInfo = buildFallback(roadmapPhase);
|
||||||
|
}
|
||||||
|
return phaseInfo;
|
||||||
|
}
|
||||||
|
|
||||||
function listPhaseSummaryFiles(phaseDir: string): string[] {
|
function listPhaseSummaryFiles(phaseDir: string): string[] {
|
||||||
return (scanPhasePlans(phaseDir) as unknown as Record<string, string[]>)['summaryFiles'];
|
return (scanPhasePlans(phaseDir) as unknown as Record<string, string[]>)['summaryFiles'];
|
||||||
}
|
}
|
||||||
@@ -462,6 +524,76 @@ function readConfigJsonBoolean(cwd: string, keyPath: readonly string[]): boolean
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bounded, non-throwing read of a dotted key path from `.planning/config.json`,
|
||||||
|
* returning the raw resolved value (any JSON type) or `undefined` on any
|
||||||
|
* degraded condition (absent file, unreadable file, malformed JSON, or a
|
||||||
|
* non-object intermediate segment) — the generic sibling of
|
||||||
|
* {@link readConfigJsonBoolean} for callers that need the actual value
|
||||||
|
* (a string like `code_quality.fallow.profile`) rather than a strict
|
||||||
|
* boolean coercion. `keyPath` is always a fixed literal supplied by this
|
||||||
|
* module, never attacker/user input, so a plain bracket traversal carries
|
||||||
|
* no prototype hazard here (same discipline as `readConfigJsonBoolean`).
|
||||||
|
*/
|
||||||
|
function readConfigJsonValue(cwd: string, keyPath: readonly string[]): unknown {
|
||||||
|
try {
|
||||||
|
const raw = fs.readFileSync(path.join(planningDir(cwd), 'config.json'), 'utf8');
|
||||||
|
let cursor: unknown = JSON.parse(raw);
|
||||||
|
for (const segment of keyPath) {
|
||||||
|
if (cursor === null || typeof cursor !== 'object' || Array.isArray(cursor)) return undefined;
|
||||||
|
cursor = (cursor as Record<string, unknown>)[segment];
|
||||||
|
}
|
||||||
|
return cursor;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `state:fallow-enabled` ground truth (#2994): resolves `code-review.md`'s
|
||||||
|
* `structural_pre_pass` fallow config gate — previously re-derived INSIDE the
|
||||||
|
* gated section body itself (`gsd_run query config-get code_quality.fallow.*`),
|
||||||
|
* which is circular/self-disabling the moment a section is gated on a fact
|
||||||
|
* its own body computes (the same hazard `state:chunked-mode` /
|
||||||
|
* `state:ui-phase-active` document for a compound condition). Fail-closed
|
||||||
|
* default `false` for `enabled`/`mcp`, matching the pre-hoist bash resolver's
|
||||||
|
* `2>/dev/null || echo "false"` fallback; `scope`/`profile` default to
|
||||||
|
* `"phase"`/`"standard"` matching that same resolver's `|| echo` fallbacks.
|
||||||
|
* `maxCrap` mirrors the step body's profile->threshold mapping (minimal=50,
|
||||||
|
* strict=15, else standard=30) so the step file never has to re-derive it.
|
||||||
|
*/
|
||||||
|
function detectFallowConfig(cwd: string): {
|
||||||
|
enabled: boolean;
|
||||||
|
scope: string;
|
||||||
|
profile: string;
|
||||||
|
mcp: boolean;
|
||||||
|
maxCrap: number;
|
||||||
|
} {
|
||||||
|
const enabled = readConfigJsonValue(cwd, ['code_quality', 'fallow', 'enabled']) === true;
|
||||||
|
const rawScope = readConfigJsonValue(cwd, ['code_quality', 'fallow', 'scope']);
|
||||||
|
const scope = typeof rawScope === 'string' && rawScope ? rawScope : 'phase';
|
||||||
|
const rawProfile = readConfigJsonValue(cwd, ['code_quality', 'fallow', 'profile']);
|
||||||
|
const profile = typeof rawProfile === 'string' && rawProfile ? rawProfile : 'standard';
|
||||||
|
const mcp = readConfigJsonValue(cwd, ['code_quality', 'fallow', 'mcp']) === true;
|
||||||
|
const maxCrap = profile === 'minimal' ? 50 : profile === 'strict' ? 15 : 30;
|
||||||
|
return { enabled, scope, profile, mcp, maxCrap };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `state:git-create-tag` ground truth (#2994): resolves `complete-milestone.md`'s
|
||||||
|
* `git_tag` step config gate — previously re-derived INSIDE a `<config-check>`
|
||||||
|
* sub-tag at the top of the step itself (`gsd-tools.cjs query config-get
|
||||||
|
* git.create_tag 2>/dev/null || echo "true"`), gating the step's OWN inclusion
|
||||||
|
* on a fact only that same step computed. Fail-OPEN default `true` (an unset
|
||||||
|
* or missing `git.create_tag` key means "create the tag"), matching the
|
||||||
|
* pre-hoist resolver's `|| echo "true"` fallback exactly — this is
|
||||||
|
* deliberately the inverse polarity of `detectFallowConfig`'s fail-closed
|
||||||
|
* default, mirroring the two source resolvers' own opposite defaults.
|
||||||
|
*/
|
||||||
|
function detectGitCreateTag(cwd: string): boolean {
|
||||||
|
return readConfigJsonValue(cwd, ['git', 'create_tag']) !== false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `state:phase-mvp-mode` ground truth (design doc §Behavior table: ROADMAP.md
|
* `state:phase-mvp-mode` ground truth (design doc §Behavior table: ROADMAP.md
|
||||||
* `**Mode:** mvp` for the CURRENT phase). Bounded, non-throwing — an absent
|
* `**Mode:** mvp` for the CURRENT phase). Bounded, non-throwing — an absent
|
||||||
@@ -502,6 +634,59 @@ function detectPhaseMvpMode(cwd: string, phaseNumber: string | null): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `state:ui-phase-active` ground truth (#2994): whether the phase's active
|
||||||
|
* `plan:pre` loop hooks include the `ui-phase` step (`capabilities/ui/
|
||||||
|
* capability.json`'s `plan:pre` step, `ref.skill: "ui-phase"`, gated on
|
||||||
|
* config `workflow.ui_phase`), OR the phase directory already contains a
|
||||||
|
* `*-UI-SPEC.md` file. The disjunction is resolved to ONE boolean here —
|
||||||
|
* same discipline as `chunkedMode` above — so the `when=` grammar never
|
||||||
|
* sees an OR. Mirrors `cmdLoopRenderHooks`'s own registry/capability-state
|
||||||
|
* setup (`src/loop-resolver.cts`) rather than reinventing a second loop-hook
|
||||||
|
* resolution path. Bounded, non-throwing: any failure in loop-hook /
|
||||||
|
* registry / capability-state resolution degrades that half of the OR to
|
||||||
|
* `false`, never throws; the UI-SPEC file check is independently bounded.
|
||||||
|
*/
|
||||||
|
function detectUiPhaseActive(cwd: string, phaseInfo: Record<string, unknown> | null): boolean {
|
||||||
|
let hasActiveUiStep = false;
|
||||||
|
try {
|
||||||
|
const config = loadConfig(cwd);
|
||||||
|
const state = resolveCapabilityRuntimeState(cwd, undefined, config) as {
|
||||||
|
capabilities: Array<{ id: string; enabled?: boolean; active: boolean }>;
|
||||||
|
};
|
||||||
|
const registry = loadRegistry({ includeInstalled: true, cwd, gsdHome: process.env['GSD_HOME'] });
|
||||||
|
const capabilityStatesById = new Map<string, { enabled?: boolean; active: boolean }>();
|
||||||
|
for (const cap of state.capabilities || []) {
|
||||||
|
capabilityStatesById.set(cap.id, cap);
|
||||||
|
}
|
||||||
|
const resolved = resolveLoopHooks({ point: 'plan:pre', registry, config, cwd, capabilityStatesById }) as {
|
||||||
|
activeHooks: Array<{ kind?: string; ref?: { skill?: string } }>;
|
||||||
|
};
|
||||||
|
hasActiveUiStep = resolved.activeHooks.some(
|
||||||
|
(h) => h.kind === 'step' && h.ref?.skill === 'ui-phase',
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
hasActiveUiStep = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasUiSpecFile = false;
|
||||||
|
const rawDir = phaseInfo?.['directory'];
|
||||||
|
if (typeof rawDir === 'string' && rawDir) {
|
||||||
|
try {
|
||||||
|
// Re-derive under planningDir(cwd)/phases/<basename> rather than trusting
|
||||||
|
// rawDir's own absolute/relative-ness (callers mix both — see the #2376
|
||||||
|
// comments elsewhere in this file), same technique as detectHasPriorPhases above.
|
||||||
|
const dirName = path.basename(rawDir);
|
||||||
|
const files = fs.readdirSync(path.join(planningDir(cwd), 'phases', dirName));
|
||||||
|
hasUiSpecFile = files.some((f) => f.endsWith('-UI-SPEC.md') || f === 'UI-SPEC.md');
|
||||||
|
} catch {
|
||||||
|
hasUiSpecFile = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasActiveUiStep || hasUiSpecFile;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the `section_manifest` init-bundle field (#2932 Deliverable 2): resolves
|
* Builds the `section_manifest` init-bundle field (#2932 Deliverable 2): resolves
|
||||||
* {@link sectionManifest.InvocationFacts} from this invocation, loads the generated
|
* {@link sectionManifest.InvocationFacts} from this invocation, loads the generated
|
||||||
@@ -538,7 +723,19 @@ function buildSectionManifestField(
|
|||||||
phaseInfo: Record<string, unknown> | null,
|
phaseInfo: Record<string, unknown> | null,
|
||||||
options: Record<string, unknown>,
|
options: Record<string, unknown>,
|
||||||
workflow: string,
|
workflow: string,
|
||||||
overrides: { needsCodebaseMap?: boolean } = {},
|
overrides: {
|
||||||
|
needsCodebaseMap?: boolean;
|
||||||
|
fallowEnabled?: boolean;
|
||||||
|
gitCreateTag?: boolean;
|
||||||
|
planStrategyConverge?: boolean;
|
||||||
|
reviewerInstancesConfigured?: boolean;
|
||||||
|
autoAdvanceActive?: boolean;
|
||||||
|
isMonorepo?: boolean;
|
||||||
|
nextChannel?: boolean;
|
||||||
|
workstreamActive?: boolean;
|
||||||
|
flatMode?: boolean;
|
||||||
|
uiPhaseActive?: boolean;
|
||||||
|
} = {},
|
||||||
): Record<string, unknown> | null {
|
): Record<string, unknown> | null {
|
||||||
const sections = loadSectionManifestSections(workflow);
|
const sections = loadSectionManifestSections(workflow);
|
||||||
if (!sections) return null;
|
if (!sections) return null;
|
||||||
@@ -573,6 +770,16 @@ function buildSectionManifestField(
|
|||||||
phaseMvpMode: detectPhaseMvpMode(cwd, phaseNumber),
|
phaseMvpMode: detectPhaseMvpMode(cwd, phaseNumber),
|
||||||
needsCodebaseMap: overrides.needsCodebaseMap,
|
needsCodebaseMap: overrides.needsCodebaseMap,
|
||||||
chunkedMode,
|
chunkedMode,
|
||||||
|
uiPhaseActive: overrides.uiPhaseActive,
|
||||||
|
fallowEnabled: overrides.fallowEnabled,
|
||||||
|
gitCreateTag: overrides.gitCreateTag,
|
||||||
|
planStrategyConverge: overrides.planStrategyConverge,
|
||||||
|
reviewerInstancesConfigured: overrides.reviewerInstancesConfigured,
|
||||||
|
autoAdvanceActive: overrides.autoAdvanceActive,
|
||||||
|
isMonorepo: overrides.isMonorepo,
|
||||||
|
nextChannel: overrides.nextChannel,
|
||||||
|
workstreamActive: overrides.workstreamActive,
|
||||||
|
flatMode: overrides.flatMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -606,21 +813,14 @@ function cmdInitExecutePhase(
|
|||||||
const milestone = getMilestoneInfo(cwd) as unknown as Record<string, unknown>;
|
const milestone = getMilestoneInfo(cwd) as unknown as Record<string, unknown>;
|
||||||
|
|
||||||
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
||||||
|
phaseInfo = applyRoadmapFallback(phaseInfo, roadmapPhase, (rp) => {
|
||||||
if (phaseInfo?.['archived'] && roadmapPhase?.['found']) {
|
const phaseName = rp['phase_name'] as string | null;
|
||||||
phaseInfo = null;
|
return {
|
||||||
}
|
|
||||||
|
|
||||||
if (!phaseInfo && roadmapPhase?.['found']) {
|
|
||||||
const phaseName = roadmapPhase['phase_name'] as string | null;
|
|
||||||
phaseInfo = {
|
|
||||||
found: true,
|
found: true,
|
||||||
directory: null,
|
directory: null,
|
||||||
phase_number: roadmapPhase['phase_number'],
|
phase_number: rp['phase_number'],
|
||||||
phase_name: phaseName,
|
phase_name: phaseName,
|
||||||
phase_slug: phaseName
|
phase_slug: slugifyPhaseName(phaseName),
|
||||||
? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')
|
|
||||||
: null,
|
|
||||||
plans: [],
|
plans: [],
|
||||||
summaries: [],
|
summaries: [],
|
||||||
incomplete_plans: [],
|
incomplete_plans: [],
|
||||||
@@ -629,7 +829,7 @@ function cmdInitExecutePhase(
|
|||||||
has_verification: false,
|
has_verification: false,
|
||||||
has_reviews: false,
|
has_reviews: false,
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
const reqMatch = (roadmapPhase?.['section'] as string | undefined)?.match(REQUIREMENTS_HEADER_RE);
|
const reqMatch = (roadmapPhase?.['section'] as string | undefined)?.match(REQUIREMENTS_HEADER_RE);
|
||||||
const reqExtracted = reqMatch
|
const reqExtracted = reqMatch
|
||||||
? reqMatch[1].replace(/[\[\]]/g, '').split(',').map((s) => s.trim()).filter(Boolean).join(', ')
|
? reqMatch[1].replace(/[\[\]]/g, '').split(',').map((s) => s.trim()).filter(Boolean).join(', ')
|
||||||
@@ -744,21 +944,14 @@ function cmdInitPlanPhase(
|
|||||||
// #2056/#2104: foreign-prefixed queries must not collapse to numeric phases.
|
// #2056/#2104: foreign-prefixed queries must not collapse to numeric phases.
|
||||||
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
||||||
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
||||||
|
phaseInfo = applyRoadmapFallback(phaseInfo, roadmapPhase, (rp) => {
|
||||||
if (phaseInfo?.['archived'] && roadmapPhase?.['found']) {
|
const phaseName = rp['phase_name'] as string | null;
|
||||||
phaseInfo = null;
|
return {
|
||||||
}
|
|
||||||
|
|
||||||
if (!phaseInfo && roadmapPhase?.['found']) {
|
|
||||||
const phaseName = roadmapPhase['phase_name'] as string | null;
|
|
||||||
phaseInfo = {
|
|
||||||
found: true,
|
found: true,
|
||||||
directory: null,
|
directory: null,
|
||||||
phase_number: roadmapPhase['phase_number'],
|
phase_number: rp['phase_number'],
|
||||||
phase_name: phaseName,
|
phase_name: phaseName,
|
||||||
phase_slug: phaseName
|
phase_slug: slugifyPhaseName(phaseName),
|
||||||
? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')
|
|
||||||
: null,
|
|
||||||
plans: [],
|
plans: [],
|
||||||
summaries: [],
|
summaries: [],
|
||||||
incomplete_plans: [],
|
incomplete_plans: [],
|
||||||
@@ -767,7 +960,7 @@ function cmdInitPlanPhase(
|
|||||||
has_verification: false,
|
has_verification: false,
|
||||||
has_reviews: false,
|
has_reviews: false,
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
const reqMatch = (roadmapPhase?.['section'] as string | undefined)?.match(REQUIREMENTS_HEADER_RE);
|
const reqMatch = (roadmapPhase?.['section'] as string | undefined)?.match(REQUIREMENTS_HEADER_RE);
|
||||||
const reqExtracted = reqMatch
|
const reqExtracted = reqMatch
|
||||||
? reqMatch[1].replace(/[\[\]]/g, '').split(',').map((s) => s.trim()).filter(Boolean).join(', ')
|
? reqMatch[1].replace(/[\[\]]/g, '').split(',').map((s) => s.trim()).filter(Boolean).join(', ')
|
||||||
@@ -1041,8 +1234,30 @@ function cmdInitNewMilestone(cwd: string, raw: boolean, options: Record<string,
|
|||||||
milestones_path: toPosixPath(path.join(planningDir(cwd), 'MILESTONES.md')),
|
milestones_path: toPosixPath(path.join(planningDir(cwd), 'MILESTONES.md')),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// `state:flat-mode` (#2994): whether NO workstream is active — the inverse
|
||||||
|
// of `state:workstream-active` (introduced for `cmdInitTransition` below).
|
||||||
|
// `new-milestone.md`'s Step 4 Part A (milestone-state write) runs ONLY in
|
||||||
|
// flat mode; a workstream's own `.planning/workstreams/<name>/STATE.md`/
|
||||||
|
// `ROADMAP.md`/`REQUIREMENTS.md` already carry the milestone state, so
|
||||||
|
// writing the shared `## Current Milestone` heading here would clobber it
|
||||||
|
// (#2308). The `when=` grammar has no negation operator (ADR-1671:69), so
|
||||||
|
// Part A's condition — "skip when a workstream IS active" — cannot be
|
||||||
|
// expressed by negating `state:workstream-active` in the marker; a
|
||||||
|
// SEPARATE, positively-phrased atom whose fact is the inverse is the
|
||||||
|
// sanctioned resolution (same discipline as `state:chunked-mode` folding
|
||||||
|
// an OR — never an operator in the grammar itself). Same authoritative
|
||||||
|
// source as `cmdInitTransition`: `GSD_WORKSTREAM` env, falling back to the
|
||||||
|
// stored active-workstream pointer (mirrors `cmdInitProgress`'s own
|
||||||
|
// resolution above).
|
||||||
|
const resolvedWorkstream = process.env['GSD_WORKSTREAM'] || getActiveWorkstream(cwd);
|
||||||
|
const workstreamActive = !!resolvedWorkstream;
|
||||||
|
const flatMode = !workstreamActive;
|
||||||
|
|
||||||
// #2992 (Phase 6.1): additive, optional field — degrades to null, never throws.
|
// #2992 (Phase 6.1): additive, optional field — degrades to null, never throws.
|
||||||
result['section_manifest'] = buildSectionManifestField(cwd, null, options, 'new-milestone');
|
result['section_manifest'] = buildSectionManifestField(cwd, null, options, 'new-milestone', {
|
||||||
|
workstreamActive,
|
||||||
|
flatMode,
|
||||||
|
});
|
||||||
|
|
||||||
output(withProjectRoot(cwd, result), raw);
|
output(withProjectRoot(cwd, result), raw);
|
||||||
}
|
}
|
||||||
@@ -1103,8 +1318,22 @@ function cmdInitQuick(
|
|||||||
planning_exists: fs.existsSync(planningRoot(cwd)),
|
planning_exists: fs.existsSync(planningRoot(cwd)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// #2994: `--full` IMPLIES `--discuss`/`--research`/`--validate` — resolved to
|
||||||
|
// ONE set of facts HERE, in fact computation, never in the `when=` grammar
|
||||||
|
// itself (mirrors `state:chunked-mode`'s disjunction fold at
|
||||||
|
// `buildSectionManifestField`'s `chunkedMode` computation above). The three
|
||||||
|
// implied tokens are folded into the flags BEFORE `buildSectionManifestField`
|
||||||
|
// builds its `InvocationFacts.flags` Set, so `discussion-phase`/`research-phase`/
|
||||||
|
// `plan-checker-loop`/`quick-verification` (all gated on their own single
|
||||||
|
// `flag:--discuss`/`flag:--research`/`flag:--validate` atom) include correctly
|
||||||
|
// for a bare `/gsd:quick --full` invocation that never passed the individual
|
||||||
|
// tokens — the grammar still sees exactly one atom per marker, no OR.
|
||||||
|
const sectionManifestOptions: Record<string, unknown> = options['full']
|
||||||
|
? { ...options, discuss: true, research: true, validate: true }
|
||||||
|
: options;
|
||||||
|
|
||||||
// #2992 (Phase 6.1): additive, optional field — degrades to null, never throws.
|
// #2992 (Phase 6.1): additive, optional field — degrades to null, never throws.
|
||||||
result['section_manifest'] = buildSectionManifestField(cwd, null, options, 'quick');
|
result['section_manifest'] = buildSectionManifestField(cwd, null, sectionManifestOptions, 'quick');
|
||||||
|
|
||||||
output(withProjectRoot(cwd, result), raw);
|
output(withProjectRoot(cwd, result), raw);
|
||||||
}
|
}
|
||||||
@@ -1188,35 +1417,23 @@ function cmdInitVerifyWork(cwd: string, phase: string, raw: boolean): void {
|
|||||||
const config = loadConfig(cwd);
|
const config = loadConfig(cwd);
|
||||||
const _slashRuntime = resolveRuntime(cwd);
|
const _slashRuntime = resolveRuntime(cwd);
|
||||||
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
||||||
|
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
||||||
if (phaseInfo?.['archived']) {
|
phaseInfo = applyRoadmapFallback(phaseInfo, roadmapPhase, (rp) => {
|
||||||
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
const phaseName = rp['phase_name'] as string | null;
|
||||||
if (roadmapPhase?.['found']) {
|
return {
|
||||||
phaseInfo = null;
|
found: true,
|
||||||
}
|
directory: null,
|
||||||
}
|
phase_number: rp['phase_number'],
|
||||||
|
phase_name: phaseName,
|
||||||
if (!phaseInfo) {
|
phase_slug: slugifyPhaseName(phaseName),
|
||||||
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
plans: [],
|
||||||
if (roadmapPhase?.['found']) {
|
summaries: [],
|
||||||
const phaseName = roadmapPhase['phase_name'] as string | null;
|
incomplete_plans: [],
|
||||||
phaseInfo = {
|
has_research: false,
|
||||||
found: true,
|
has_context: false,
|
||||||
directory: null,
|
has_verification: false,
|
||||||
phase_number: roadmapPhase['phase_number'],
|
};
|
||||||
phase_name: phaseName,
|
});
|
||||||
phase_slug: phaseName
|
|
||||||
? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')
|
|
||||||
: null,
|
|
||||||
plans: [],
|
|
||||||
summaries: [],
|
|
||||||
incomplete_plans: [],
|
|
||||||
has_research: false,
|
|
||||||
has_context: false,
|
|
||||||
has_verification: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const phaseDir = (phaseInfo?.['directory'] as string | null | undefined) || null;
|
const phaseDir = (phaseInfo?.['directory'] as string | null | undefined) || null;
|
||||||
const planCount = (phaseInfo?.['plans'] as unknown[] | undefined)?.length || 0;
|
const planCount = (phaseInfo?.['plans'] as unknown[] | undefined)?.length || 0;
|
||||||
@@ -1234,6 +1451,7 @@ function cmdInitVerifyWork(cwd: string, phase: string, raw: boolean): void {
|
|||||||
policy: { requireVerification: true },
|
policy: { requireVerification: true },
|
||||||
})
|
})
|
||||||
: null;
|
: null;
|
||||||
|
const uiPhaseActive = detectUiPhaseActive(cwd, phaseInfo);
|
||||||
|
|
||||||
const result: Record<string, unknown> = {
|
const result: Record<string, unknown> = {
|
||||||
planner_model: resolveModelInternal(cwd, 'gsd-planner'),
|
planner_model: resolveModelInternal(cwd, 'gsd-planner'),
|
||||||
@@ -1260,8 +1478,270 @@ function cmdInitVerifyWork(cwd: string, phase: string, raw: boolean): void {
|
|||||||
uat_blockers: uatReport?.blockers ?? [],
|
uat_blockers: uatReport?.blockers ?? [],
|
||||||
ready_to_transition: completion.phase_complete && (uatReport?.passed ?? false),
|
ready_to_transition: completion.phase_complete && (uatReport?.passed ?? false),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// #2994 (resolver-hoist-guard G5): hoisted `state:ui-phase-active` ground
|
||||||
|
// truth (previously re-derived inline inside the automated_ui_verification
|
||||||
|
// step body via its own `gsd_run loop render-hooks plan:pre --raw` call —
|
||||||
|
// a circular, self-disabling resolver, since the section is only read
|
||||||
|
// when this same fact is already true). Resolved once here, exposed so
|
||||||
|
// the step body can consume it directly instead of recomputing it.
|
||||||
|
ui_phase_active: uiPhaseActive,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// #2994 (Phase 6.3): additive, optional field — degrades to null, never throws.
|
||||||
|
// phaseInfo is passed through directly (mirrors cmdInitExecutePhase / cmdInitPlanPhase)
|
||||||
|
// so buildSectionManifestField's internal detectPhaseMvpMode call gets a real
|
||||||
|
// phase_number/directory rather than permanently-false facts. uiPhaseActive is
|
||||||
|
// computed once above (not re-derived here) and threaded through via overrides,
|
||||||
|
// mirroring the fallow/git-create-tag hoist pattern.
|
||||||
|
result['section_manifest'] = buildSectionManifestField(cwd, phaseInfo, {}, 'verify-work', {
|
||||||
|
uiPhaseActive,
|
||||||
|
});
|
||||||
|
|
||||||
|
output(withProjectRoot(cwd, result), raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `code-review.md`'s dedicated init entry point (#2994, epic #1671 Phase
|
||||||
|
* 6.3). `code-review.md` previously routed through the shared, 20+-caller
|
||||||
|
* `init.phase-op` (`cmdInitPhaseOp` below), reading only 6 of its fields
|
||||||
|
* (`phase_found`, `phase_dir`, `phase_number`, `phase_name`, `padded_phase`,
|
||||||
|
* `commit_docs` — verified against the workflow's own "Parse from init
|
||||||
|
* JSON" line). `cmdInitPhaseOp` is CRITICAL blast radius (179 dependents
|
||||||
|
* across 24 processes per the #2994 dispatch) and is never modified for
|
||||||
|
* this — this function resolves phase info itself via the SAME shared
|
||||||
|
* primitives `cmdInitPhaseOp` calls (`guardedFindPhase`/
|
||||||
|
* `guardedGetRoadmapPhase`, plus the shared `applyRoadmapFallback` archived/
|
||||||
|
* not-found fallback also used by execute-phase, plan-phase, verify-work
|
||||||
|
* and review — see `applyRoadmapFallback`'s own doc comment; `cmdInitPhaseOp`
|
||||||
|
* is deliberately excluded from that shared helper), producing the identical
|
||||||
|
* 6-field shape rather than a second, hand-maintained copy of
|
||||||
|
* `cmdInitPhaseOp`'s full ~60-field bundle. See
|
||||||
|
* `tests/init-code-review-parity.test.cjs` for the DEFECT.GENERATIVE-FIX
|
||||||
|
* parity guard between the two.
|
||||||
|
*
|
||||||
|
* Two further facts are resolved and exposed here that `init.phase-op`
|
||||||
|
* never carried: the fallow structural-pre-pass config gate
|
||||||
|
* (`detectFallowConfig`, `state:fallow-enabled`) and the `--fix` flag
|
||||||
|
* (folded into `options` so `buildSectionManifestField` picks it up as
|
||||||
|
* `flag:--fix`).
|
||||||
|
*/
|
||||||
|
function cmdInitCodeReview(
|
||||||
|
cwd: string,
|
||||||
|
phase: string,
|
||||||
|
raw: boolean,
|
||||||
|
options: Record<string, unknown> = {},
|
||||||
|
): void {
|
||||||
|
const config = loadConfig(cwd);
|
||||||
|
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
||||||
|
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
||||||
|
phaseInfo = applyRoadmapFallback(phaseInfo, roadmapPhase, (rp) => {
|
||||||
|
const rpName = rp['phase_name'] as string | null;
|
||||||
|
return {
|
||||||
|
found: true,
|
||||||
|
directory: null,
|
||||||
|
phase_number: rp['phase_number'],
|
||||||
|
phase_name: rpName,
|
||||||
|
phase_slug: slugifyPhaseName(rpName),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const phaseDir = (phaseInfo?.['directory'] as string | undefined) || null;
|
||||||
|
const phaseNumber = (phaseInfo?.['phase_number'] as string | undefined) || null;
|
||||||
|
const phaseName = (phaseInfo?.['phase_name'] as string | undefined) || null;
|
||||||
|
|
||||||
|
const fallow = detectFallowConfig(cwd);
|
||||||
|
|
||||||
|
const result: Record<string, unknown> = {
|
||||||
|
commit_docs: config.commit_docs,
|
||||||
|
|
||||||
|
phase_found: !!phaseInfo,
|
||||||
|
// #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
|
||||||
|
phase_dir: phaseDir ? toPosixPath(path.join(cwd, phaseDir)) : null,
|
||||||
|
phase_number: phaseNumber,
|
||||||
|
phase_name: phaseName,
|
||||||
|
padded_phase: phaseNumber ? normalizePhaseName(phaseNumber) : null,
|
||||||
|
|
||||||
|
// #2994: hoisted fallow config-gate resolution (previously re-derived
|
||||||
|
// inline inside code-review.md's structural_pre_pass step body — a
|
||||||
|
// circular self-disabling gate now resolved once here at init time).
|
||||||
|
fallow_enabled: fallow.enabled,
|
||||||
|
fallow_scope: fallow.scope,
|
||||||
|
fallow_profile: fallow.profile,
|
||||||
|
fallow_mcp: fallow.mcp,
|
||||||
|
fallow_max_crap: fallow.maxCrap,
|
||||||
|
};
|
||||||
|
|
||||||
|
// #2994 (Phase 6.3): additive, optional field — degrades to null, never throws.
|
||||||
|
const sectionManifestOptions: Record<string, unknown> = {
|
||||||
|
...options,
|
||||||
|
fix: options['fix'] || undefined,
|
||||||
|
};
|
||||||
|
result['section_manifest'] = buildSectionManifestField(cwd, phaseInfo, sectionManifestOptions, 'code-review', {
|
||||||
|
fallowEnabled: fallow.enabled,
|
||||||
|
});
|
||||||
|
|
||||||
|
output(withProjectRoot(cwd, result), raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `review.md`'s dedicated init entry point (#2994, epic #1671 Phase 6.3
|
||||||
|
* amendment). `review.md` previously routed through the shared, 20+-caller
|
||||||
|
* `init.phase-op` (`cmdInitPhaseOp` below), reading only 3 of its fields
|
||||||
|
* (`phase_dir`, `phase_number`, `padded_phase` — verified against the
|
||||||
|
* workflow's own "Read from init" line in `gather_context`). `cmdInitPhaseOp`
|
||||||
|
* is CRITICAL blast radius (179 dependents across 24 processes) and is never
|
||||||
|
* modified for this — this function resolves phase info itself via the SAME
|
||||||
|
* shared primitives `cmdInitPhaseOp` calls (`guardedFindPhase`/
|
||||||
|
* `guardedGetRoadmapPhase`), plus the shared `applyRoadmapFallback`
|
||||||
|
* archived/not-found fallback (see its own doc comment), producing the
|
||||||
|
* identical 3-field shape rather than a second, hand-maintained copy of
|
||||||
|
* `cmdInitPhaseOp`'s full ~60-field bundle.
|
||||||
|
*
|
||||||
|
* One further fact is resolved and exposed here that `init.phase-op` never
|
||||||
|
* carried: whether reviewer instances are configured
|
||||||
|
* (`.planning/config.json`'s `review.reviewer_instances`, present AND
|
||||||
|
* non-empty — `state:reviewer-instances-configured`), reusing
|
||||||
|
* `readConfigJsonValue` (added for `detectFallowConfig`) rather than a
|
||||||
|
* second, divergence-prone config reader (DEFECT.GENERATIVE-FIX).
|
||||||
|
*/
|
||||||
|
function cmdInitReview(
|
||||||
|
cwd: string,
|
||||||
|
phase: string,
|
||||||
|
raw: boolean,
|
||||||
|
options: Record<string, unknown> = {},
|
||||||
|
): void {
|
||||||
|
const config = loadConfig(cwd);
|
||||||
|
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
||||||
|
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
||||||
|
phaseInfo = applyRoadmapFallback(phaseInfo, roadmapPhase, (rp) => ({
|
||||||
|
found: true,
|
||||||
|
directory: null,
|
||||||
|
phase_number: rp['phase_number'],
|
||||||
|
phase_name: rp['phase_name'],
|
||||||
|
}));
|
||||||
|
|
||||||
|
const phaseDir = (phaseInfo?.['directory'] as string | undefined) || null;
|
||||||
|
const phaseNumber = (phaseInfo?.['phase_number'] as string | undefined) || null;
|
||||||
|
|
||||||
|
// #2994: `state:reviewer-instances-configured` ground truth — present AND
|
||||||
|
// non-empty `review.reviewer_instances` object. A missing key, a non-object
|
||||||
|
// value, or an empty object all resolve to `false` (fail-closed, matching
|
||||||
|
// the workflow's own pre-hoist prose gate — "Unconfigured -> default path
|
||||||
|
// unchanged").
|
||||||
|
const rawReviewerInstances = readConfigJsonValue(cwd, ['review', 'reviewer_instances']);
|
||||||
|
const reviewerInstancesConfigured =
|
||||||
|
rawReviewerInstances !== null &&
|
||||||
|
typeof rawReviewerInstances === 'object' &&
|
||||||
|
!Array.isArray(rawReviewerInstances) &&
|
||||||
|
Object.keys(rawReviewerInstances).length > 0;
|
||||||
|
|
||||||
|
const result: Record<string, unknown> = {
|
||||||
|
// #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
|
||||||
|
phase_dir: phaseDir ? toPosixPath(path.join(cwd, phaseDir)) : null,
|
||||||
|
phase_number: phaseNumber,
|
||||||
|
padded_phase: phaseNumber ? normalizePhaseName(phaseNumber) : null,
|
||||||
|
};
|
||||||
|
|
||||||
|
result['section_manifest'] = buildSectionManifestField(cwd, phaseInfo, options, 'review', {
|
||||||
|
reviewerInstancesConfigured,
|
||||||
|
});
|
||||||
|
|
||||||
|
output(withProjectRoot(cwd, result), raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `discuss-phase-assumptions.md`'s dedicated init entry point (#2994, epic
|
||||||
|
* #1671 Phase 6.3 amendment). Previously routed through the shared,
|
||||||
|
* 20+-caller `init.phase-op` (`cmdInitPhaseOp` below), reading 14 of its
|
||||||
|
* fields (`commit_docs`, `phase_found`, `phase_dir`, `phase_number`,
|
||||||
|
* `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`,
|
||||||
|
* `has_plans`, `has_verification`, `plan_count`, `roadmap_exists`,
|
||||||
|
* `planning_exists` — verified against the workflow's own "Parse JSON for"
|
||||||
|
* line). `cmdInitPhaseOp` is CRITICAL blast radius (179 dependents across 24
|
||||||
|
* processes) and is never modified for this — this function resolves phase
|
||||||
|
* info itself via the SAME shared primitives `cmdInitPhaseOp` calls
|
||||||
|
* (`guardedFindPhase`/`guardedGetRoadmapPhase`), plus the shared
|
||||||
|
* `applyRoadmapFallback` archived/not-found fallback (see its own doc
|
||||||
|
* comment) producing the identical fallback shape (`plans: []`,
|
||||||
|
* `has_research: false`, `has_context: false`, `has_verification: false`)
|
||||||
|
* rather than a second, hand-maintained copy of `cmdInitPhaseOp`'s full
|
||||||
|
* ~60-field bundle.
|
||||||
|
*
|
||||||
|
* One further fact is resolved and exposed here that `init.phase-op` never
|
||||||
|
* carried: `state:auto-advance-active` — the workflow's own `auto_advance`
|
||||||
|
* step resolves `--auto` OR a consolidated `check auto-mode --pick active`
|
||||||
|
* fact (itself `workflow._auto_chain_active` OR `workflow.auto_advance`) via
|
||||||
|
* a runtime `gsd_run` call; that identical disjunction is folded into ONE
|
||||||
|
* boolean FACT here (same discipline as `state:chunked-mode` /
|
||||||
|
* `state:plan-strategy-converge`), exposed as `auto_advance_active`.
|
||||||
|
*/
|
||||||
|
function cmdInitDiscussPhaseAssumptions(
|
||||||
|
cwd: string,
|
||||||
|
phase: string,
|
||||||
|
raw: boolean,
|
||||||
|
options: Record<string, unknown> = {},
|
||||||
|
): void {
|
||||||
|
const config = loadConfig(cwd);
|
||||||
|
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
||||||
|
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
||||||
|
phaseInfo = applyRoadmapFallback(phaseInfo, roadmapPhase, (rp) => {
|
||||||
|
const rpName = rp['phase_name'] as string | null;
|
||||||
|
return {
|
||||||
|
found: true,
|
||||||
|
directory: null,
|
||||||
|
phase_number: rp['phase_number'],
|
||||||
|
phase_name: rpName,
|
||||||
|
phase_slug: slugifyPhaseName(rpName),
|
||||||
|
plans: [],
|
||||||
|
has_research: false,
|
||||||
|
has_context: false,
|
||||||
|
has_verification: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const phaseDir = (phaseInfo?.['directory'] as string | undefined) || null;
|
||||||
|
const phaseNumber = (phaseInfo?.['phase_number'] as string | undefined) || null;
|
||||||
|
const phaseName = (phaseInfo?.['phase_name'] as string | undefined) || null;
|
||||||
|
|
||||||
|
// #2994: mirrors discuss-phase-assumptions.md's own auto_advance step
|
||||||
|
// resolver — `--auto` flag OR the consolidated `check auto-mode --pick
|
||||||
|
// active` fact (workflow._auto_chain_active OR workflow.auto_advance).
|
||||||
|
const autoAdvanceActive =
|
||||||
|
options['auto'] === true ||
|
||||||
|
readConfigJsonBoolean(cwd, ['workflow', '_auto_chain_active']) ||
|
||||||
|
readConfigJsonBoolean(cwd, ['workflow', 'auto_advance']);
|
||||||
|
|
||||||
|
const result: Record<string, unknown> = {
|
||||||
|
commit_docs: config.commit_docs,
|
||||||
|
|
||||||
|
phase_found: !!phaseInfo,
|
||||||
|
// #2376: absolute — see comment on phase_dir in cmdInitExecutePhase.
|
||||||
|
phase_dir: phaseDir ? toPosixPath(path.join(cwd, phaseDir)) : null,
|
||||||
|
phase_number: phaseNumber,
|
||||||
|
phase_name: phaseName,
|
||||||
|
phase_slug: phaseInfo?.['phase_slug'] || null,
|
||||||
|
padded_phase: phaseNumber ? normalizePhaseName(phaseNumber) : null,
|
||||||
|
|
||||||
|
has_research: phaseInfo?.['has_research'] || false,
|
||||||
|
has_context: phaseInfo?.['has_context'] || false,
|
||||||
|
has_plans: ((phaseInfo?.['plans'] as unknown[] | undefined)?.length || 0) > 0,
|
||||||
|
has_verification: phaseInfo?.['has_verification'] || false,
|
||||||
|
plan_count: (phaseInfo?.['plans'] as unknown[] | undefined)?.length || 0,
|
||||||
|
|
||||||
|
roadmap_exists: fs.existsSync(path.join(planningDir(cwd), 'ROADMAP.md')),
|
||||||
|
planning_exists: fs.existsSync(planningDir(cwd)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// #2994 (Phase 6.3): additive, optional field — degrades to null, never throws.
|
||||||
|
const sectionManifestOptions: Record<string, unknown> = {
|
||||||
|
...options,
|
||||||
|
auto: options['auto'] || undefined,
|
||||||
|
};
|
||||||
|
result['section_manifest'] = buildSectionManifestField(cwd, phaseInfo, sectionManifestOptions, 'discuss-phase-assumptions', {
|
||||||
|
autoAdvanceActive,
|
||||||
|
});
|
||||||
|
|
||||||
output(withProjectRoot(cwd, result), raw);
|
output(withProjectRoot(cwd, result), raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1988,6 +2468,191 @@ function cmdInitManager(cwd: string, raw: boolean): void {
|
|||||||
output(withProjectRoot(cwd, result), raw);
|
output(withProjectRoot(cwd, result), raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `complete-milestone.md`'s dedicated init entry point (#2994, epic #1671
|
||||||
|
* Phase 6.3). Additive alongside the workflow's existing `init.manager`
|
||||||
|
* (readiness/phase-projection, `cmdInitManager` above — CRITICAL blast
|
||||||
|
* radius, never modified) and `init.execute-phase` (branching-strategy
|
||||||
|
* fields) calls; `cmdInitCompleteMilestone` carries NO phase-listing logic
|
||||||
|
* of its own to delegate — its only job is the `git.create_tag` config-gate
|
||||||
|
* fact the `git_tag` step's `<config-check>` sub-tag used to re-derive
|
||||||
|
* inline (gating the step's own inclusion on a fact only that step
|
||||||
|
* computed), now hoisted here and exposed as `git_create_tag`, plus the
|
||||||
|
* `section_manifest` field neither `init.manager` nor `init.execute-phase`
|
||||||
|
* carries.
|
||||||
|
*/
|
||||||
|
function cmdInitCompleteMilestone(
|
||||||
|
cwd: string,
|
||||||
|
raw: boolean,
|
||||||
|
options: Record<string, unknown> = {},
|
||||||
|
): void {
|
||||||
|
const gitCreateTag = detectGitCreateTag(cwd);
|
||||||
|
|
||||||
|
const result: Record<string, unknown> = {
|
||||||
|
// #2994: hoisted from complete-milestone.md's git_tag step
|
||||||
|
// <config-check> resolver (git.create_tag, fail-open default true).
|
||||||
|
git_create_tag: gitCreateTag,
|
||||||
|
};
|
||||||
|
|
||||||
|
result['section_manifest'] = buildSectionManifestField(cwd, null, options, 'complete-milestone', {
|
||||||
|
gitCreateTag,
|
||||||
|
});
|
||||||
|
|
||||||
|
output(withProjectRoot(cwd, result), raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `autonomous.md`'s dedicated init entry point (#2994, epic #1671 Phase
|
||||||
|
* 6.3). Additive alongside the workflow's existing `init.milestone-op`
|
||||||
|
* (`cmdInitMilestoneOp`), `init.manager` (`cmdInitManager`), and
|
||||||
|
* `init.phase-op` (`cmdInitPhaseOp`) calls — all three are CRITICAL blast
|
||||||
|
* radius (179 dependents across 24 processes) and are never modified for
|
||||||
|
* this; `autonomous.md` keeps every one of those calls exactly as it had
|
||||||
|
* them. `cmdInitAutonomous` carries NO phase-listing logic of its own to
|
||||||
|
* delegate — its only job is the `PLAN_STRATEGY` disjunction the workflow's
|
||||||
|
* own bash resolver (`PLAN_STRATEGY="converge"` on `--converge` OR
|
||||||
|
* `--cross-ai`) already computes at the top of the `initialize` step, now
|
||||||
|
* mirrored here as a single boolean FACT (same discipline as
|
||||||
|
* `state:chunked-mode`/`state:ui-phase-active`: the disjunction is resolved
|
||||||
|
* ONCE, in fact computation, never in the `when=` grammar), exposed as
|
||||||
|
* `plan_strategy_converge`, plus the `section_manifest` field none of the
|
||||||
|
* three existing calls carries.
|
||||||
|
*/
|
||||||
|
function cmdInitAutonomous(
|
||||||
|
cwd: string,
|
||||||
|
raw: boolean,
|
||||||
|
options: Record<string, unknown> = {},
|
||||||
|
): void {
|
||||||
|
const planStrategyConverge = options['converge'] === true || options['cross-ai'] === true;
|
||||||
|
|
||||||
|
const result: Record<string, unknown> = {
|
||||||
|
// #2994: mirrors autonomous.md's own PLAN_STRATEGY resolver
|
||||||
|
// (--converge OR its documented alias --cross-ai).
|
||||||
|
plan_strategy_converge: planStrategyConverge,
|
||||||
|
};
|
||||||
|
|
||||||
|
result['section_manifest'] = buildSectionManifestField(cwd, null, options, 'autonomous', {
|
||||||
|
planStrategyConverge,
|
||||||
|
});
|
||||||
|
|
||||||
|
output(withProjectRoot(cwd, result), raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `docs-update.md`'s dedicated init entry point (#2994, epic #1671 Phase
|
||||||
|
* 6.3 — final slice). `docs-update.md` previously carried NO `gsd_run query
|
||||||
|
* init.*` call at all: its own `docs-init` command (`cmdDocsInit`,
|
||||||
|
* src/docs.cts) is a SEPARATE, pre-existing entry point outside this
|
||||||
|
* `init.*` family and is left untouched here. This function's only job is
|
||||||
|
* the `section_manifest` field neither `docs-init` nor any other call
|
||||||
|
* carries, gating `docs-update.md`'s `dispatch-monorepo-packages` section.
|
||||||
|
*
|
||||||
|
* `state:is-monorepo` ground truth: the project's monorepo workspaces list
|
||||||
|
* is non-empty — reuses `detectMonorepoWorkspaces` (src/docs.cts, exported
|
||||||
|
* for this purpose) rather than a second, divergence-prone workspace-glob
|
||||||
|
* scan (DEFECT.GENERATIVE-FIX dual surface); this is the SAME detector that
|
||||||
|
* already backs `docs-init`'s own `monorepo_workspaces` field.
|
||||||
|
*/
|
||||||
|
function cmdInitDocsUpdate(cwd: string, raw: boolean, options: Record<string, unknown> = {}): void {
|
||||||
|
const isMonorepo = detectMonorepoWorkspaces(cwd).length > 0;
|
||||||
|
|
||||||
|
const result: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
result['section_manifest'] = buildSectionManifestField(cwd, null, options, 'docs-update', {
|
||||||
|
isMonorepo,
|
||||||
|
});
|
||||||
|
|
||||||
|
output(withProjectRoot(cwd, result), raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `update.md`'s dedicated init entry point (#2994, epic #1671 Phase 6.3 —
|
||||||
|
* final slice). `update.md` previously carried NO `gsd_run query init.*`
|
||||||
|
* call at all: it resolves `gsd-tools.cjs` itself (its own bespoke
|
||||||
|
* `PREFERRED_CONFIG_DIR`/`PREFERRED_RUNTIME`-aware `$GSD_TOOLS` cascade,
|
||||||
|
* `update.md` ~lines 13-45) because the update workflow must run BEFORE any
|
||||||
|
* install can be assumed resolvable — the canonical launcher preamble's
|
||||||
|
* fixed candidate list is not a substitute for that cascade, and both
|
||||||
|
* resolutions assign the identical `$GSD_TOOLS` shell variable, so copying
|
||||||
|
* the canonical preamble in ADDITION to the existing cascade would silently
|
||||||
|
* clobber the value `backup_custom_files`/`restore_custom_files` (later
|
||||||
|
* steps) still depend on. This function is invoked via that ALREADY
|
||||||
|
* resolved `$GSD_TOOLS`, not a redundant `gsd_run()` shell function.
|
||||||
|
*
|
||||||
|
* `state:next-channel` ground truth: `--next` OR its documented alias
|
||||||
|
* `--rc` (same disjunction-to-one-boolean discipline as
|
||||||
|
* `state:chunked-mode`/`state:plan-strategy-converge`). This DELIBERATELY
|
||||||
|
* does not replace `update.md`'s own `parse_update_channel` case-statement
|
||||||
|
* (`TAG="next"`/`TAG="latest"`) — issue #815's regression test
|
||||||
|
* (`tests/issue-815-update-next-channel.test.cjs`) asserts that literal
|
||||||
|
* case-statement text stays in `update.md` verbatim (the npm dist-tag
|
||||||
|
* selection has to run in the workflow's own shell before any `gsd_run`
|
||||||
|
* round-trip), so `next_channel` exists purely to gate the `channel-banner`
|
||||||
|
* section's admission — a parallel, consistent-but-not-replacing
|
||||||
|
* resolution of the same flags.
|
||||||
|
*/
|
||||||
|
function cmdInitUpdate(cwd: string, raw: boolean, options: Record<string, unknown> = {}): void {
|
||||||
|
const nextChannel = options['next'] === true || options['rc'] === true;
|
||||||
|
|
||||||
|
const result: Record<string, unknown> = {
|
||||||
|
next_channel: nextChannel,
|
||||||
|
};
|
||||||
|
|
||||||
|
result['section_manifest'] = buildSectionManifestField(cwd, null, options, 'update', {
|
||||||
|
nextChannel,
|
||||||
|
});
|
||||||
|
|
||||||
|
output(withProjectRoot(cwd, result), raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `transition.md`'s dedicated init entry point (#2994, epic #1671 Phase
|
||||||
|
* 6.3 — final slice). `transition.md` is an internal workflow (no
|
||||||
|
* user-facing `/gsd-transition` command) that previously carried NO
|
||||||
|
* `gsd_run query init.*` call at all; it already establishes `gsd_run()`
|
||||||
|
* via the canonical launcher preamble in its `update_roadmap_and_state`
|
||||||
|
* step (before this call's insertion point in `offer_next_phase`), so no
|
||||||
|
* second preamble copy is needed in the host file.
|
||||||
|
*
|
||||||
|
* `state:workstream-active` ground truth: a workstream is active — resolved
|
||||||
|
* via `GSD_WORKSTREAM` env, falling back to the stored active-workstream
|
||||||
|
* pointer (mirrors `cmdInitProgress`'s own `_resolvedWorkstream` resolution
|
||||||
|
* above, the established authoritative source for "is a workstream active"
|
||||||
|
* in this file).
|
||||||
|
*
|
||||||
|
* `other_active_workstreams` hoists the resolver-in-body hazard out of
|
||||||
|
* `transition.md`'s `workstream-collision-check` step: that step's body
|
||||||
|
* previously re-derived this via an inline `gsd_run query workstream.list
|
||||||
|
* --raw` call gated on the identical `if [ -n "$GSD_WORKSTREAM" ]`
|
||||||
|
* condition that now backs this section's OWN admission — resolving it here
|
||||||
|
* instead reuses `getOtherActiveWorkstreamInventories` (src/workstream-
|
||||||
|
* inventory.cts), the SAME primitive `workstream.list` itself calls
|
||||||
|
* (`cmdWorkstreamList`, src/workstream.cts), pre-filtered exactly as the
|
||||||
|
* step's own prose described (excludes the current workstream and any
|
||||||
|
* workstream whose status contains "milestone complete" or "archived",
|
||||||
|
* case-insensitively — `isCompletedInventory`), so the step body becomes a
|
||||||
|
* pure JSON consumer with no `gsd_run` call of its own.
|
||||||
|
*/
|
||||||
|
function cmdInitTransition(cwd: string, raw: boolean, options: Record<string, unknown> = {}): void {
|
||||||
|
const resolvedWorkstream = process.env['GSD_WORKSTREAM'] || getActiveWorkstream(cwd);
|
||||||
|
const workstreamActive = !!resolvedWorkstream;
|
||||||
|
|
||||||
|
const result: Record<string, unknown> = {
|
||||||
|
other_active_workstreams: workstreamActive
|
||||||
|
? getOtherActiveWorkstreamInventories(cwd, resolvedWorkstream).map((inv) => ({
|
||||||
|
name: inv.name,
|
||||||
|
status: inv.status,
|
||||||
|
}))
|
||||||
|
: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
result['section_manifest'] = buildSectionManifestField(cwd, null, options, 'transition', {
|
||||||
|
workstreamActive,
|
||||||
|
});
|
||||||
|
|
||||||
|
output(withProjectRoot(cwd, result), raw);
|
||||||
|
}
|
||||||
|
|
||||||
function cmdInitProgress(cwd: string, raw: boolean, options: Record<string, unknown> = {}): void {
|
function cmdInitProgress(cwd: string, raw: boolean, options: Record<string, unknown> = {}): void {
|
||||||
try {
|
try {
|
||||||
(pruneOrphanedWorktrees as (cwd: string) => void)(cwd);
|
(pruneOrphanedWorktrees as (cwd: string) => void)(cwd);
|
||||||
@@ -2166,6 +2831,20 @@ function cmdInitProgress(cwd: string, raw: boolean, options: Record<string, unkn
|
|||||||
if (pauseMatch) pausedAt = pauseMatch[1].trim();
|
if (pauseMatch) pausedAt = pauseMatch[1].trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #2994: the CURRENT phase's number, used both to expose `phase_mvp_mode`
|
||||||
|
// at the top level (so the `mvp-display` step body can consume an
|
||||||
|
// already-resolved fact instead of re-invoking `gsd_run query
|
||||||
|
// phase.mvp-mode` itself — that inline resolver would otherwise gate a
|
||||||
|
// section on a fact the section's own body recomputes, which is circular
|
||||||
|
// and self-disabling) and to thread a real `phase_number` into
|
||||||
|
// `buildSectionManifestField` below so `state:phase-mvp-mode` is genuinely
|
||||||
|
// computed for this workflow rather than permanently false (the previous
|
||||||
|
// `buildSectionManifestField(cwd, null, ...)` call passed no phase info at
|
||||||
|
// all, so `detectPhaseMvpMode` always short-circuited on the `!phaseNumber`
|
||||||
|
// guard).
|
||||||
|
const currentPhaseNumber = (currentPhase?.['number'] as string | undefined) ?? null;
|
||||||
|
const phaseMvpMode = detectPhaseMvpMode(cwd, currentPhaseNumber);
|
||||||
|
|
||||||
const result: Record<string, unknown> = {
|
const result: Record<string, unknown> = {
|
||||||
executor_model: resolveModelInternal(cwd, 'gsd-executor'),
|
executor_model: resolveModelInternal(cwd, 'gsd-executor'),
|
||||||
planner_model: resolveModelInternal(cwd, 'gsd-planner'),
|
planner_model: resolveModelInternal(cwd, 'gsd-planner'),
|
||||||
@@ -2186,6 +2865,7 @@ function cmdInitProgress(cwd: string, raw: boolean, options: Record<string, unkn
|
|||||||
next_phase: nextPhase,
|
next_phase: nextPhase,
|
||||||
paused_at: pausedAt,
|
paused_at: pausedAt,
|
||||||
has_work_in_progress: !!currentPhase,
|
has_work_in_progress: !!currentPhase,
|
||||||
|
phase_mvp_mode: phaseMvpMode,
|
||||||
|
|
||||||
project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
|
project_exists: pathExistsInternal(cwd, '.planning/PROJECT.md'),
|
||||||
roadmap_exists: fs.existsSync(path.join(planningDir(cwd), 'ROADMAP.md')),
|
roadmap_exists: fs.existsSync(path.join(planningDir(cwd), 'ROADMAP.md')),
|
||||||
@@ -2198,7 +2878,12 @@ function cmdInitProgress(cwd: string, raw: boolean, options: Record<string, unkn
|
|||||||
};
|
};
|
||||||
|
|
||||||
// #2992 (Phase 6.1): additive, optional field — degrades to null, never throws.
|
// #2992 (Phase 6.1): additive, optional field — degrades to null, never throws.
|
||||||
result['section_manifest'] = buildSectionManifestField(cwd, null, options, 'progress');
|
result['section_manifest'] = buildSectionManifestField(
|
||||||
|
cwd,
|
||||||
|
currentPhaseNumber ? { phase_number: currentPhaseNumber } : null,
|
||||||
|
options,
|
||||||
|
'progress',
|
||||||
|
);
|
||||||
|
|
||||||
output(withProjectRoot(cwd, result), raw);
|
output(withProjectRoot(cwd, result), raw);
|
||||||
}
|
}
|
||||||
@@ -2973,11 +3658,19 @@ export = {
|
|||||||
cmdInitResume,
|
cmdInitResume,
|
||||||
cmdInitVerifyWork,
|
cmdInitVerifyWork,
|
||||||
cmdInitPhaseOp,
|
cmdInitPhaseOp,
|
||||||
|
cmdInitCodeReview,
|
||||||
|
cmdInitReview,
|
||||||
|
cmdInitDiscussPhaseAssumptions,
|
||||||
cmdInitTodos,
|
cmdInitTodos,
|
||||||
cmdInitMilestoneOp,
|
cmdInitMilestoneOp,
|
||||||
cmdInitMapCodebase,
|
cmdInitMapCodebase,
|
||||||
cmdInitProgress,
|
cmdInitProgress,
|
||||||
cmdInitManager,
|
cmdInitManager,
|
||||||
|
cmdInitCompleteMilestone,
|
||||||
|
cmdInitAutonomous,
|
||||||
|
cmdInitDocsUpdate,
|
||||||
|
cmdInitUpdate,
|
||||||
|
cmdInitTransition,
|
||||||
cmdInitNewWorkspace,
|
cmdInitNewWorkspace,
|
||||||
cmdInitListWorkspaces,
|
cmdInitListWorkspaces,
|
||||||
cmdInitRemoveWorkspace,
|
cmdInitRemoveWorkspace,
|
||||||
|
|||||||
@@ -20,7 +20,16 @@
|
|||||||
* Phase 6.1; see `.gsd/phase/chore-2992-widen-when-vocabulary/
|
* Phase 6.1; see `.gsd/phase/chore-2992-widen-when-vocabulary/
|
||||||
* 40-design.md`), then from 14 to 19 via the ADR-1671 amendment for #2993
|
* 40-design.md`), then from 14 to 19 via the ADR-1671 amendment for #2993
|
||||||
* (epic #1671 Phase 6.2; see `.gsd/phase/chore-2993-fragmentize-plan-phase/
|
* (epic #1671 Phase 6.2; see `.gsd/phase/chore-2993-fragmentize-plan-phase/
|
||||||
* 40-design.md`). {@link WHEN_PREDICATES} is a total map from each frozen
|
* 40-design.md`), then from 19 to 20 via the ADR-1671 amendment for #2994
|
||||||
|
* (epic #1671 Phase 6.3), then from 20 to 23 via a further #2994 amendment
|
||||||
|
* fragmentizing `code-review.md` and `complete-milestone.md`, then from 23
|
||||||
|
* to 24 via a still further #2994 amendment fragmentizing `autonomous.md`,
|
||||||
|
* then from 24 to 26 via a still further #2994 amendment fragmentizing
|
||||||
|
* `review.md` and `discuss-phase-assumptions.md`, then from 26 to 30 — and
|
||||||
|
* finally to 29, `flag:--full` having been retired as dead vocabulary — via the
|
||||||
|
* final #2994 slice fragmentizing `docs-update.md`, `update.md`,
|
||||||
|
* `transition.md`, and `new-milestone.md`.
|
||||||
|
* {@link WHEN_PREDICATES} is a total map from each frozen
|
||||||
* vocabulary entry to exactly one predicate over {@link InvocationFacts}.
|
* vocabulary entry to exactly one predicate over {@link InvocationFacts}.
|
||||||
* It MUST NOT tokenize, split on operators, or interpret structure in the
|
* It MUST NOT tokenize, split on operators, or interpret structure in the
|
||||||
* `when=` string — the moment it parses, the ad-hoc language has begun.
|
* `when=` string — the moment it parses, the ad-hoc language has begun.
|
||||||
@@ -92,6 +101,110 @@ export interface InvocationFacts {
|
|||||||
* Absent/undefined is falsy, never throws.
|
* Absent/undefined is falsy, never throws.
|
||||||
*/
|
*/
|
||||||
readonly chunkedMode?: boolean;
|
readonly chunkedMode?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether the current phase's UI-phase surface is active (#2994):
|
||||||
|
* the phase's active `plan:pre` loop hooks include the `ui-phase` step OR
|
||||||
|
* the phase directory already contains a `*-UI-SPEC.md` file. Same
|
||||||
|
* discipline as {@link chunkedMode} — the disjunction is resolved by the
|
||||||
|
* caller (the init seam) into this single boolean BEFORE it reaches this
|
||||||
|
* module. Absent/undefined is falsy, never throws.
|
||||||
|
*/
|
||||||
|
readonly uiPhaseActive?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether the fallow structural cross-module pre-pass is enabled
|
||||||
|
* (`.planning/config.json` `code_quality.fallow.enabled`, #2994). Fail-closed
|
||||||
|
* default `false` — resolved by the caller (`cmdInitCodeReview`, the init
|
||||||
|
* seam) from the same config key `code-review.md`'s `structural_pre_pass`
|
||||||
|
* step used to re-derive inline (a circular self-disabling gate); the
|
||||||
|
* resolved value is exposed as the init-bundle's `fallow_enabled` field for
|
||||||
|
* the step body to consume directly. Absent/undefined is falsy, never throws.
|
||||||
|
*/
|
||||||
|
readonly fallowEnabled?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether git tag creation is enabled on milestone close
|
||||||
|
* (`.planning/config.json` `git.create_tag`, #2994). Fail-OPEN default
|
||||||
|
* `true` (unset/missing config means "create the tag"), matching
|
||||||
|
* `complete-milestone.md`'s pre-hoist `gsd-tools.cjs query config-get
|
||||||
|
* git.create_tag 2>/dev/null || echo "true"` resolver — now hoisted into
|
||||||
|
* `cmdInitCompleteMilestone` and exposed as the init-bundle's
|
||||||
|
* `git_create_tag` field. Absent/undefined is falsy at the PREDICATE level
|
||||||
|
* (per the totality convention every other atom here follows), so a caller
|
||||||
|
* MUST always populate this fact with a real boolean (never omit it) or the
|
||||||
|
* `git-tag` section will incorrectly evaluate to excluded.
|
||||||
|
*/
|
||||||
|
readonly gitCreateTag?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether `autonomous.md`'s planning step should route through plan-review
|
||||||
|
* convergence instead of `gsd-plan-phase` (#2994): `--converge` flag OR its
|
||||||
|
* documented alias `--cross-ai`. Same discipline as {@link chunkedMode} /
|
||||||
|
* {@link uiPhaseActive} — the disjunction is resolved by the caller
|
||||||
|
* (`cmdInitAutonomous`, the init seam) into this single boolean BEFORE it
|
||||||
|
* reaches this module; `state:plan-strategy-converge`'s predicate below
|
||||||
|
* reads only this field, never `--converge`/`--cross-ai` separately.
|
||||||
|
* Absent/undefined is falsy, never throws.
|
||||||
|
*/
|
||||||
|
readonly planStrategyConverge?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether `review.md`'s `review.reviewer_instances` config surface is
|
||||||
|
* configured — present AND non-empty (#2994). Resolved by the caller
|
||||||
|
* (`cmdInitReview`, the init seam) via `readConfigJsonValue`; the same
|
||||||
|
* fact backs both `reviewer-instances-note-1` and `reviewer-instances-note-2`
|
||||||
|
* (two sections sharing one atom — legal, mirrors `plan-phase.md`'s
|
||||||
|
* `research-only-*` pair). Absent/undefined is falsy, never throws.
|
||||||
|
*/
|
||||||
|
readonly reviewerInstancesConfigured?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether `discuss-phase-assumptions.md`'s `auto_advance` step should
|
||||||
|
* dispatch (#2994): `--auto` flag OR a consolidated auto-mode config fact
|
||||||
|
* (`workflow._auto_chain_active` OR `workflow.auto_advance`). Same
|
||||||
|
* discipline as {@link chunkedMode} — the disjunction is resolved by the
|
||||||
|
* caller (`cmdInitDiscussPhaseAssumptions`, the init seam) into this
|
||||||
|
* single boolean BEFORE it reaches this module. Absent/undefined is
|
||||||
|
* falsy, never throws.
|
||||||
|
*/
|
||||||
|
readonly autoAdvanceActive?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether the project is a monorepo with a non-empty workspaces list
|
||||||
|
* (#2994, final slice): the SAME `monorepo_workspaces` glob list
|
||||||
|
* `docs-init` (`src/docs.cts`) already produces, resolved by the caller
|
||||||
|
* (`cmdInitDocsUpdate`, the init seam) via the exported
|
||||||
|
* `detectMonorepoWorkspaces` detector rather than a second scan. Absent/
|
||||||
|
* undefined is falsy, never throws.
|
||||||
|
*/
|
||||||
|
readonly isMonorepo?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether `update.md`'s release channel is `next` (#2994, final slice):
|
||||||
|
* `--next` flag OR its documented alias `--rc`. Same disjunction-to-one-
|
||||||
|
* boolean discipline as {@link chunkedMode} — resolved by the caller
|
||||||
|
* (`cmdInitUpdate`, the init seam) into this single boolean BEFORE it
|
||||||
|
* reaches this module. This fact exists purely to gate the
|
||||||
|
* `channel-banner` section's admission; it deliberately does NOT replace
|
||||||
|
* `update.md`'s own `TAG="next"`/`TAG="latest"` case-statement (issue
|
||||||
|
* #815's regression test asserts that literal text stays in the
|
||||||
|
* workflow). Absent/undefined is falsy, never throws.
|
||||||
|
*/
|
||||||
|
readonly nextChannel?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether a workstream is active (#2994, final slice): `GSD_WORKSTREAM`
|
||||||
|
* env, falling back to the stored active-workstream pointer — resolved by
|
||||||
|
* the caller (`cmdInitTransition`/`cmdInitNewMilestone`, the init seam),
|
||||||
|
* mirroring `cmdInitProgress`'s own established resolution of the same
|
||||||
|
* question. Shared across two workflows' `cmdInit*` entry points (not
|
||||||
|
* redefined per-caller). Absent/undefined is falsy, never throws.
|
||||||
|
*/
|
||||||
|
readonly workstreamActive?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether NO workstream is active — the positively-phrased INVERSE of
|
||||||
|
* {@link workstreamActive} (#2994, final slice). The `when=` grammar has
|
||||||
|
* no negation operator (ADR-1671:69), so a section whose true condition is
|
||||||
|
* "skip when a workstream IS active" (`new-milestone.md`'s Step 4 Part A)
|
||||||
|
* cannot negate `state:workstream-active` in its marker; a separate atom
|
||||||
|
* whose fact is independently resolved to the inverse is the sanctioned
|
||||||
|
* pattern instead. Resolved by the caller (`cmdInitNewMilestone`, the init
|
||||||
|
* seam) as `!workstreamActive` from the SAME authoritative source.
|
||||||
|
* Absent/undefined is falsy, never throws.
|
||||||
|
*/
|
||||||
|
readonly flatMode?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A single input to {@link selectSections}: structurally compatible with {@link workflowFragments.WorkflowSection}. */
|
/** A single input to {@link selectSections}: structurally compatible with {@link workflowFragments.WorkflowSection}. */
|
||||||
@@ -177,8 +290,8 @@ export const WHEN_PREDICATES: Readonly<Record<string, (facts: InvocationFacts) =
|
|||||||
'state:has-prior-phases': (facts: InvocationFacts) => facts.hasPriorPhases === true,
|
'state:has-prior-phases': (facts: InvocationFacts) => facts.hasPriorPhases === true,
|
||||||
'flag:--auto': (facts: InvocationFacts) => hasFlag(facts, '--auto'),
|
'flag:--auto': (facts: InvocationFacts) => hasFlag(facts, '--auto'),
|
||||||
'flag:--discuss': (facts: InvocationFacts) => hasFlag(facts, '--discuss'),
|
'flag:--discuss': (facts: InvocationFacts) => hasFlag(facts, '--discuss'),
|
||||||
|
'flag:--fix': (facts: InvocationFacts) => hasFlag(facts, '--fix'),
|
||||||
'flag:--forensic': (facts: InvocationFacts) => hasFlag(facts, '--forensic'),
|
'flag:--forensic': (facts: InvocationFacts) => hasFlag(facts, '--forensic'),
|
||||||
'flag:--full': (facts: InvocationFacts) => hasFlag(facts, '--full'),
|
|
||||||
'flag:--ingest': (facts: InvocationFacts) => hasFlag(facts, '--ingest'),
|
'flag:--ingest': (facts: InvocationFacts) => hasFlag(facts, '--ingest'),
|
||||||
'flag:--prd': (facts: InvocationFacts) => hasFlag(facts, '--prd'),
|
'flag:--prd': (facts: InvocationFacts) => hasFlag(facts, '--prd'),
|
||||||
'flag:--research': (facts: InvocationFacts) => hasFlag(facts, '--research'),
|
'flag:--research': (facts: InvocationFacts) => hasFlag(facts, '--research'),
|
||||||
@@ -186,9 +299,19 @@ export const WHEN_PREDICATES: Readonly<Record<string, (facts: InvocationFacts) =
|
|||||||
'flag:--reset-phase-numbers': (facts: InvocationFacts) => hasFlag(facts, '--reset-phase-numbers'),
|
'flag:--reset-phase-numbers': (facts: InvocationFacts) => hasFlag(facts, '--reset-phase-numbers'),
|
||||||
'flag:--reviews': (facts: InvocationFacts) => hasFlag(facts, '--reviews'),
|
'flag:--reviews': (facts: InvocationFacts) => hasFlag(facts, '--reviews'),
|
||||||
'flag:--validate': (facts: InvocationFacts) => hasFlag(facts, '--validate'),
|
'flag:--validate': (facts: InvocationFacts) => hasFlag(facts, '--validate'),
|
||||||
|
'state:auto-advance-active': (facts: InvocationFacts) => facts.autoAdvanceActive === true,
|
||||||
'state:chunked-mode': (facts: InvocationFacts) => facts.chunkedMode === true,
|
'state:chunked-mode': (facts: InvocationFacts) => facts.chunkedMode === true,
|
||||||
|
'state:fallow-enabled': (facts: InvocationFacts) => facts.fallowEnabled === true,
|
||||||
|
'state:flat-mode': (facts: InvocationFacts) => facts.flatMode === true,
|
||||||
|
'state:git-create-tag': (facts: InvocationFacts) => facts.gitCreateTag === true,
|
||||||
|
'state:is-monorepo': (facts: InvocationFacts) => facts.isMonorepo === true,
|
||||||
'state:needs-codebase-map': (facts: InvocationFacts) => facts.needsCodebaseMap === true,
|
'state:needs-codebase-map': (facts: InvocationFacts) => facts.needsCodebaseMap === true,
|
||||||
|
'state:next-channel': (facts: InvocationFacts) => facts.nextChannel === true,
|
||||||
'state:phase-mvp-mode': (facts: InvocationFacts) => facts.phaseMvpMode === true,
|
'state:phase-mvp-mode': (facts: InvocationFacts) => facts.phaseMvpMode === true,
|
||||||
|
'state:plan-strategy-converge': (facts: InvocationFacts) => facts.planStrategyConverge === true,
|
||||||
|
'state:reviewer-instances-configured': (facts: InvocationFacts) => facts.reviewerInstancesConfigured === true,
|
||||||
|
'state:ui-phase-active': (facts: InvocationFacts) => facts.uiPhaseActive === true,
|
||||||
|
'state:workstream-active': (facts: InvocationFacts) => facts.workstreamActive === true,
|
||||||
'state:worktrees-enabled': (facts: InvocationFacts) => facts.worktreesEnabled === true,
|
'state:worktrees-enabled': (facts: InvocationFacts) => facts.worktreesEnabled === true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -95,19 +95,50 @@ import contextComposer = require('./context-composer.cjs');
|
|||||||
* #1671 Phase 6.1; see `.gsd/phase/chore-2992-widen-when-vocabulary/
|
* #1671 Phase 6.1; see `.gsd/phase/chore-2992-widen-when-vocabulary/
|
||||||
* 40-design.md`), then from 14 to 19 via the ADR-1671 amendment for #2993
|
* 40-design.md`), then from 14 to 19 via the ADR-1671 amendment for #2993
|
||||||
* (epic #1671 Phase 6.2; see `.gsd/phase/chore-2993-fragmentize-plan-phase/
|
* (epic #1671 Phase 6.2; see `.gsd/phase/chore-2993-fragmentize-plan-phase/
|
||||||
* 40-design.md`). The vocabulary remains CLOSED: no operators, no negation,
|
* 40-design.md`), then from 19 to 20 via the ADR-1671 amendment for #2994
|
||||||
* no nesting. Cardinality is not expressiveness — a 19-entry flat list with
|
* (epic #1671 Phase 6.3, `verify-work.md`), then from 20 to 23 via a further
|
||||||
* no composition is still not a language.
|
* #2994 amendment fragmentizing `code-review.md` and `complete-milestone.md`,
|
||||||
|
* then from 23 to 24 via a further #2994 amendment fragmentizing
|
||||||
|
* `autonomous.md`, then from 24 to 26 via a still further #2994 amendment
|
||||||
|
* fragmentizing `review.md` and `discuss-phase-assumptions.md`, then from 26
|
||||||
|
* to 30 (then 29; `flag:--full` retired as dead vocabulary) via the FINAL
|
||||||
|
* #2994 slice (epic #1671 Phase 6.3) fragmentizing
|
||||||
|
* `docs-update.md`, `update.md`, `transition.md`, and `new-milestone.md` —
|
||||||
|
* every one of the 13 workflows targeted by ADR-1671 is now on the fragment
|
||||||
|
* model. The vocabulary remains CLOSED: no operators, no negation, no nesting.
|
||||||
|
* Cardinality is not expressiveness — a 29-entry flat list with no
|
||||||
|
* composition is still not a language.
|
||||||
*
|
*
|
||||||
* Held at 14, not wider: an atom whose fact is never computed always
|
* Held at 14, not wider: an atom whose fact is never computed always
|
||||||
* evaluates FALSE, so a section marked with it would silently never
|
* evaluates FALSE, so a section marked with it would silently never
|
||||||
* include — a silent-exclusion bug, not a feature. Six further atoms
|
* include — a silent-exclusion bug, not a feature. One further atom
|
||||||
* (`flag:--converge`, `flag:--fix`, `flag:--verify-only`,
|
* (`flag:--verify-only`) was surveyed but is NOT admitted even now that
|
||||||
* `state:fallow-enabled`, `state:git-create-tag`, `state:is-monorepo`) were
|
* `docs-update` has its own `cmdInit*` entry point (`cmdInitDocsUpdate`):
|
||||||
* surveyed and are justified in principle, but their workflows
|
* the flag's control flow is INTERLEAVED across three non-contiguous
|
||||||
* (docs-update, autonomous, code-review, complete-milestone) have no
|
* touch-points in `docs-update.md` (an inline early-exit check in
|
||||||
* dedicated `cmdInit*` entry point yet to compute the backing fact, so they
|
* `init_context` — "If `--verify-only` is present…skip to
|
||||||
* are withheld until that entry point exists (#2992 / ADR-1671 Phase 6.1).
|
* verify_only_report" — a "Skip condition" note embedded in another step's
|
||||||
|
* body, and the `verify_only_report` step itself) rather than a single
|
||||||
|
* contiguous, whole-line, purely-additive region — admitting the atom to
|
||||||
|
* gate only the `verify_only_report` step would leave the other two
|
||||||
|
* touch-points as un-migrated raw `$ARGUMENTS` checks. `state:is-monorepo`
|
||||||
|
* (`dispatch-monorepo-packages` section) IS admitted in this slice — see the
|
||||||
|
* paragraph below.
|
||||||
|
* `flag:--fix`, `state:fallow-enabled`, and `state:git-create-tag` were
|
||||||
|
* withheld for the same reason until a further #2994 amendment gave
|
||||||
|
* `code-review` and `complete-milestone` their own dedicated `cmdInit*`
|
||||||
|
* entry points (`cmdInitCodeReview`, `cmdInitCompleteMilestone`). The
|
||||||
|
* originally-surveyed `flag:--converge` never shipped under that name: once
|
||||||
|
* `autonomous` gained its own dedicated `cmdInitAutonomous` entry point, the
|
||||||
|
* admitted atom is `state:plan-strategy-converge` instead — `--cross-ai` is
|
||||||
|
* a documented alias for `--converge` (`autonomous.md`'s own `PLAN_STRATEGY`
|
||||||
|
* resolver folds both into one value), so a `flag:--converge`-only atom
|
||||||
|
* would have left `--cross-ai`-only invocations silently excluded from the
|
||||||
|
* same sections; see the `state:plan-strategy-converge` paragraph below.
|
||||||
|
* `state:reviewer-instances-configured` and `state:auto-advance-active` were
|
||||||
|
* withheld the same way until `review` and `discuss-phase-assumptions`
|
||||||
|
* gained their own dedicated `cmdInit*` entry points (`cmdInitReview`,
|
||||||
|
* `cmdInitDiscussPhaseAssumptions`).
|
||||||
*
|
*
|
||||||
* The #2993 widening adds 5 entries fragmentizing `plan-phase.md`:
|
* The #2993 widening adds 5 entries fragmentizing `plan-phase.md`:
|
||||||
* `flag:--ingest`, `flag:--prd`, `flag:--research-phase`, `flag:--reviews`,
|
* `flag:--ingest`, `flag:--prd`, `flag:--research-phase`, `flag:--reviews`,
|
||||||
@@ -115,6 +146,64 @@ import contextComposer = require('./context-composer.cjs');
|
|||||||
* flag OR `.planning/config.json` `workflow.plan_chunked`) resolved to a
|
* flag OR `.planning/config.json` `workflow.plan_chunked`) resolved to a
|
||||||
* single boolean FACT by the init seam (`src/init.cts`) — the grammar still
|
* single boolean FACT by the init seam (`src/init.cts`) — the grammar still
|
||||||
* sees exactly one atom with no operator, preserving the same guard.
|
* sees exactly one atom with no operator, preserving the same guard.
|
||||||
|
*
|
||||||
|
* The #2994 widening adds 1 entry fragmentizing `verify-work.md`:
|
||||||
|
* `state:ui-phase-active`. Like `state:chunked-mode`, it is a disjunction —
|
||||||
|
* the phase's active `plan:pre` loop hooks include the `ui-phase` step OR
|
||||||
|
* the phase directory already contains a `*-UI-SPEC.md` — resolved to a
|
||||||
|
* single boolean FACT by the init seam before it ever reaches this grammar.
|
||||||
|
*
|
||||||
|
* A further #2994 widening (epic #1671 Phase 6.3) adds 3 entries
|
||||||
|
* fragmentizing `code-review.md` and `complete-milestone.md`: `flag:--fix`
|
||||||
|
* (`dispatch-fix` section), `state:fallow-enabled` (`structural-pre-pass`
|
||||||
|
* section — the fallow config-gate resolver, previously re-derived inside
|
||||||
|
* the section body itself, is hoisted into `cmdInitCodeReview` and exposed
|
||||||
|
* as top-level `fallow_*` init-bundle fields), and `state:git-create-tag`
|
||||||
|
* (`git-tag` section — the `git.create_tag` config-gate resolver is hoisted
|
||||||
|
* into `cmdInitCompleteMilestone`).
|
||||||
|
*
|
||||||
|
* A still further #2994 widening (epic #1671 Phase 6.3) adds 1 entry
|
||||||
|
* fragmentizing `autonomous.md`: `state:plan-strategy-converge`, gating five
|
||||||
|
* sections (`converge-fail-fast`, `converge-banner`, `converge-dispatch-bg`,
|
||||||
|
* `converge-dispatch-inline`, `converge-loop`) that all share the same atom
|
||||||
|
* — legal and precedented (`plan-phase.md`'s `research-only-*` pair already
|
||||||
|
* shares `flag:--research-phase`). It is a disjunction — `--converge` OR its
|
||||||
|
* documented alias `--cross-ai` — resolved to a single boolean FACT by the
|
||||||
|
* new `cmdInitAutonomous` entry point (`flags.has('--converge') ||
|
||||||
|
* flags.has('--cross-ai')`) before it ever reaches this grammar, same
|
||||||
|
* discipline as `state:chunked-mode`/`state:ui-phase-active` above.
|
||||||
|
*
|
||||||
|
* A still further #2994 widening (epic #1671 Phase 6.3) adds 2 entries
|
||||||
|
* fragmentizing `review.md` and `discuss-phase-assumptions.md`:
|
||||||
|
* `state:reviewer-instances-configured` (`reviewer-instances-note-1` and
|
||||||
|
* `reviewer-instances-note-2` sections — two peripheral notes sharing one
|
||||||
|
* atom, the same sharing pattern `plan-phase.md`'s `research-only-*` pair
|
||||||
|
* already established) and `state:auto-advance-active` (`auto-advance-dispatch`
|
||||||
|
* section — a disjunction, `--auto` flag OR a consolidated auto-mode config
|
||||||
|
* fact, resolved to a single boolean FACT by the new
|
||||||
|
* `cmdInitDiscussPhaseAssumptions` entry point before it ever reaches this
|
||||||
|
* grammar, same discipline as `state:chunked-mode` above).
|
||||||
|
*
|
||||||
|
* The FINAL #2994 widening (epic #1671 Phase 6.3) adds 4 entries, closing
|
||||||
|
* out the last four workflows on ADR-1671's fragmentization list —
|
||||||
|
* `docs-update.md`, `update.md`, `transition.md`, `new-milestone.md` — none
|
||||||
|
* of which carried a `gsd_run query init.*` call before this slice:
|
||||||
|
* `state:is-monorepo` (`dispatch-monorepo-packages` section, new
|
||||||
|
* `cmdInitDocsUpdate` entry point — reuses `docs.cts`'s own
|
||||||
|
* `detectMonorepoWorkspaces` detector rather than a second scan);
|
||||||
|
* `state:next-channel` (`channel-banner` section, new `cmdInitUpdate` entry
|
||||||
|
* point — `--next` OR its documented alias `--rc`, resolved in PARALLEL
|
||||||
|
* with, not in place of, `update.md`'s own `TAG="next"` case-statement,
|
||||||
|
* which issue #815's regression test requires to stay literal in the
|
||||||
|
* workflow); `state:workstream-active` (`workstream-collision-check`
|
||||||
|
* section, new `cmdInitTransition` entry point — a workstream is active,
|
||||||
|
* `GSD_WORKSTREAM` env falling back to the stored active-workstream
|
||||||
|
* pointer, the same authoritative source `cmdInitProgress` already uses);
|
||||||
|
* and `state:flat-mode` (`project-md-milestone-write` section,
|
||||||
|
* `cmdInitNewMilestone` — the positively-phrased INVERSE of
|
||||||
|
* `state:workstream-active`, introduced because the grammar has no negation
|
||||||
|
* operator and `new-milestone.md`'s Step 4 Part A is gated on the OPPOSITE
|
||||||
|
* condition from `transition.md`'s section).
|
||||||
*/
|
*/
|
||||||
export const WHEN_VOCABULARY: readonly string[] = Object.freeze([
|
export const WHEN_VOCABULARY: readonly string[] = Object.freeze([
|
||||||
'always',
|
'always',
|
||||||
@@ -123,8 +212,8 @@ export const WHEN_VOCABULARY: readonly string[] = Object.freeze([
|
|||||||
'state:has-prior-phases',
|
'state:has-prior-phases',
|
||||||
'flag:--auto',
|
'flag:--auto',
|
||||||
'flag:--discuss',
|
'flag:--discuss',
|
||||||
|
'flag:--fix',
|
||||||
'flag:--forensic',
|
'flag:--forensic',
|
||||||
'flag:--full',
|
|
||||||
'flag:--ingest',
|
'flag:--ingest',
|
||||||
'flag:--prd',
|
'flag:--prd',
|
||||||
'flag:--research',
|
'flag:--research',
|
||||||
@@ -132,9 +221,19 @@ export const WHEN_VOCABULARY: readonly string[] = Object.freeze([
|
|||||||
'flag:--reset-phase-numbers',
|
'flag:--reset-phase-numbers',
|
||||||
'flag:--reviews',
|
'flag:--reviews',
|
||||||
'flag:--validate',
|
'flag:--validate',
|
||||||
|
'state:auto-advance-active',
|
||||||
'state:chunked-mode',
|
'state:chunked-mode',
|
||||||
|
'state:fallow-enabled',
|
||||||
|
'state:flat-mode',
|
||||||
|
'state:git-create-tag',
|
||||||
|
'state:is-monorepo',
|
||||||
'state:needs-codebase-map',
|
'state:needs-codebase-map',
|
||||||
|
'state:next-channel',
|
||||||
'state:phase-mvp-mode',
|
'state:phase-mvp-mode',
|
||||||
|
'state:plan-strategy-converge',
|
||||||
|
'state:reviewer-instances-configured',
|
||||||
|
'state:ui-phase-active',
|
||||||
|
'state:workstream-active',
|
||||||
'state:worktrees-enabled',
|
'state:worktrees-enabled',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ const WORKFLOW_PATH = path.join(REPO_ROOT, 'gsd-core', 'workflows', 'autonomous.
|
|||||||
const COMMANDS_DOC_PATH = path.join(REPO_ROOT, 'docs', 'COMMANDS.md');
|
const COMMANDS_DOC_PATH = path.join(REPO_ROOT, 'docs', 'COMMANDS.md');
|
||||||
const HOW_TO_PATH = path.join(REPO_ROOT, 'docs', 'how-to', 'run-phases-autonomously.md');
|
const HOW_TO_PATH = path.join(REPO_ROOT, 'docs', 'how-to', 'run-phases-autonomously.md');
|
||||||
const TOOLS = path.join(REPO_ROOT, 'gsd-core', 'bin', 'gsd-tools.cjs');
|
const TOOLS = path.join(REPO_ROOT, 'gsd-core', 'bin', 'gsd-tools.cjs');
|
||||||
|
// #2994: fragmentization moved the five converge-gated regions out of the host
|
||||||
|
// autonomous.md into dedicated step files (state:plan-strategy-converge) —
|
||||||
|
// see docs/reference/workflow-fragments.md. Tests that assert on this moved
|
||||||
|
// content read the step file directly rather than the host.
|
||||||
|
const STEP_FAIL_FAST_PATH = path.join(REPO_ROOT, 'gsd-core', 'workflows', 'autonomous', 'steps', 'converge-fail-fast.md');
|
||||||
|
const STEP_DISPATCH_BG_PATH = path.join(REPO_ROOT, 'gsd-core', 'workflows', 'autonomous', 'steps', 'converge-dispatch-bg.md');
|
||||||
|
const STEP_DISPATCH_INLINE_PATH = path.join(REPO_ROOT, 'gsd-core', 'workflows', 'autonomous', 'steps', 'converge-dispatch-inline.md');
|
||||||
|
const STEP_LOOP_PATH = path.join(REPO_ROOT, 'gsd-core', 'workflows', 'autonomous', 'steps', 'converge-loop.md');
|
||||||
|
|
||||||
function read(filePath) {
|
function read(filePath) {
|
||||||
return fs.readFileSync(filePath, 'utf8');
|
return fs.readFileSync(filePath, 'utf8');
|
||||||
@@ -47,30 +55,50 @@ describe('autonomous --converge flag (#711)', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('workflow fails fast when convergence is requested but disabled', () => {
|
test('workflow fails fast when convergence is requested but disabled', () => {
|
||||||
|
// #2994: this check lives in the converge-fail-fast step file now
|
||||||
|
// (state:plan-strategy-converge) — the host only carries the gated
|
||||||
|
// conditional-read stub.
|
||||||
const workflow = read(WORKFLOW_PATH);
|
const workflow = read(WORKFLOW_PATH);
|
||||||
|
const step = read(STEP_FAIL_FAST_PATH);
|
||||||
|
|
||||||
assert.match(
|
assert.match(
|
||||||
workflow,
|
workflow,
|
||||||
/config-get workflow\.plan_review_convergence/,
|
/gsd:section id="converge-fail-fast" when="state:plan-strategy-converge"/,
|
||||||
'workflow should check workflow.plan_review_convergence before planning',
|
'workflow should gate the fail-fast check behind state:plan-strategy-converge',
|
||||||
);
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
workflow,
|
step,
|
||||||
|
/config-get workflow\.plan_review_convergence/,
|
||||||
|
'converge-fail-fast step should check workflow.plan_review_convergence before planning',
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
step,
|
||||||
/gsd config-set workflow\.plan_review_convergence true/,
|
/gsd config-set workflow\.plan_review_convergence true/,
|
||||||
'workflow should print the enable command instead of silently downgrading',
|
'converge-fail-fast step should print the enable command instead of silently downgrading',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('workflow routes planning through plan-review-convergence when enabled', () => {
|
test('workflow routes planning through plan-review-convergence when enabled', () => {
|
||||||
|
// #2994: the converge dispatch/loop bodies live in dedicated step files
|
||||||
|
// now (state:plan-strategy-converge) — only the local-planning fallback
|
||||||
|
// remains inline in the host.
|
||||||
const workflow = read(WORKFLOW_PATH);
|
const workflow = read(WORKFLOW_PATH);
|
||||||
|
const dispatchInline = read(STEP_DISPATCH_INLINE_PATH);
|
||||||
|
const loop = read(STEP_LOOP_PATH);
|
||||||
|
const dispatchBg = read(STEP_DISPATCH_BG_PATH);
|
||||||
|
|
||||||
assert.match(
|
assert.match(
|
||||||
workflow,
|
dispatchInline,
|
||||||
/Skill\(skill="gsd-plan-review-convergence", args="\$\{PHASE_NUM\} \$\{CONVERGENCE_ARGS\}"\)/,
|
/Skill\(skill="gsd-plan-review-convergence", args="\$\{PHASE_NUM\} \$\{CONVERGENCE_ARGS\}"\)/,
|
||||||
'non-interactive converge mode should call gsd-plan-review-convergence',
|
'inline converge dispatch step should call gsd-plan-review-convergence',
|
||||||
);
|
);
|
||||||
assert.match(
|
assert.match(
|
||||||
workflow,
|
loop,
|
||||||
|
/Skill\(skill="gsd-plan-review-convergence", args="\$\{PHASE_NUM\} \$\{CONVERGENCE_ARGS\}"\)/,
|
||||||
|
'default converge loop step should call gsd-plan-review-convergence',
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
dispatchBg,
|
||||||
/Run plan convergence for phase \$\{PHASE_NUM\}: Skill\(skill=\\"gsd-plan-review-convergence\\"/,
|
/Run plan convergence for phase \$\{PHASE_NUM\}: Skill\(skill=\\"gsd-plan-review-convergence\\"/,
|
||||||
'interactive converge mode should dispatch plan convergence in the background agent',
|
'interactive converge mode should dispatch plan convergence in the background agent',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,13 +12,32 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const WORKFLOW_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'autonomous.md');
|
const WORKFLOW_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'autonomous.md');
|
||||||
|
const UI_DESIGN_CONTRACT_REF_PATH = path.join(
|
||||||
|
__dirname, '..', 'gsd-core', 'references', 'autonomous-ui-design-contract.md'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #2994 fragmentization moved §3a.5's body out of autonomous.md into
|
||||||
|
* gsd-core/references/autonomous-ui-design-contract.md, leaving an
|
||||||
|
* unconditional `Read and execute:` pointer in the host. Splice the
|
||||||
|
* reference file's content back in at the pointer's position so §3a.5
|
||||||
|
* slicing/ordering assertions still see the real step body.
|
||||||
|
*/
|
||||||
|
function readAutonomousCombined() {
|
||||||
|
const raw = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
|
||||||
|
const pointerRe = /Read and execute: `\$HOME\/\.claude\/gsd-core\/references\/autonomous-ui-design-contract\.md`/;
|
||||||
|
assert.ok(pointerRe.test(raw), 'autonomous.md must still contain the §3a.5 reference pointer');
|
||||||
|
const refContent = fs.readFileSync(UI_DESIGN_CONTRACT_REF_PATH, 'utf-8');
|
||||||
|
return raw.replace(pointerRe, refContent);
|
||||||
|
}
|
||||||
|
|
||||||
describe('autonomous workflow ui-phase and ui-review integration (#1375)', () => {
|
describe('autonomous workflow ui-phase and ui-review integration (#1375)', () => {
|
||||||
let content;
|
let content;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
assert.ok(fs.existsSync(WORKFLOW_PATH), 'workflows/autonomous.md should exist');
|
assert.ok(fs.existsSync(WORKFLOW_PATH), 'workflows/autonomous.md should exist');
|
||||||
content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
|
assert.ok(fs.existsSync(UI_DESIGN_CONTRACT_REF_PATH), 'references/autonomous-ui-design-contract.md should exist');
|
||||||
|
content = readAutonomousCombined();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('step 3a.5 — UI design contract before planning', () => {
|
describe('step 3a.5 — UI design contract before planning', () => {
|
||||||
|
|||||||
@@ -301,9 +301,25 @@ const { cleanup } = require('./helpers.cjs');
|
|||||||
const HELPER_PATH = path.join(__dirname, '..', 'bin', 'lib', 'ui-safety-gate.cjs');
|
const HELPER_PATH = path.join(__dirname, '..', 'bin', 'lib', 'ui-safety-gate.cjs');
|
||||||
const PLAN_PHASE_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'plan-phase.md');
|
const PLAN_PHASE_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'plan-phase.md');
|
||||||
const AUTONOMOUS_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'autonomous.md');
|
const AUTONOMOUS_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'autonomous.md');
|
||||||
|
const AUTONOMOUS_UI_DESIGN_CONTRACT_REF_PATH = path.join(
|
||||||
|
__dirname, '..', 'gsd-core', 'references', 'autonomous-ui-design-contract.md'
|
||||||
|
);
|
||||||
|
|
||||||
const { checkUiPresence } = require(HELPER_PATH);
|
const { checkUiPresence } = require(HELPER_PATH);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #2994 fragmentization moved §3a.5's body out of autonomous.md into
|
||||||
|
* gsd-core/references/autonomous-ui-design-contract.md, leaving an
|
||||||
|
* unconditional `Read and execute:` pointer in the host. Read host +
|
||||||
|
* reference file combined so the structural guards below still see the
|
||||||
|
* real §3a.5 body (and its absence of the retired ui-safety-gate.cjs /
|
||||||
|
* RUNTIME_DIR / LC_ALL=C grep patterns).
|
||||||
|
*/
|
||||||
|
function readAutonomousCombined() {
|
||||||
|
return fs.readFileSync(AUTONOMOUS_PATH, 'utf-8') +
|
||||||
|
'\n' + fs.readFileSync(AUTONOMOUS_UI_DESIGN_CONTRACT_REF_PATH, 'utf-8');
|
||||||
|
}
|
||||||
|
|
||||||
// ── Helper ────────────────────────────────────────────────────────────────────
|
// ── Helper ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -362,7 +378,7 @@ describe('Workflow .md structural guard (#3718)', () => {
|
|||||||
// was intentionally removed — cross-shell portability is provided by the CLI layer.
|
// was intentionally removed — cross-shell portability is provided by the CLI layer.
|
||||||
test('autonomous.md must invoke check ui-plan-gate (capability-driven UI gate — #1031)', () => {
|
test('autonomous.md must invoke check ui-plan-gate (capability-driven UI gate — #1031)', () => {
|
||||||
const label = 'autonomous.md';
|
const label = 'autonomous.md';
|
||||||
const content = fs.readFileSync(AUTONOMOUS_PATH, 'utf-8');
|
const content = readAutonomousCombined();
|
||||||
|
|
||||||
// §3a.5 must delegate to the capability-driven gate, not inline shell code
|
// §3a.5 must delegate to the capability-driven gate, not inline shell code
|
||||||
assert.ok(
|
assert.ok(
|
||||||
|
|||||||
@@ -235,6 +235,18 @@ describe('#3727 — parseCodeReviewFlags: typed IR for code-review argv', () =>
|
|||||||
|
|
||||||
describe('#3727 — code-review.md structural dispatch contract', () => {
|
describe('#3727 — code-review.md structural dispatch contract', () => {
|
||||||
const src = fs.readFileSync(WORKFLOW_PATH, 'utf8');
|
const src = fs.readFileSync(WORKFLOW_PATH, 'utf8');
|
||||||
|
// #2994: dispatch_fix moved wholly into a marker-gated step file
|
||||||
|
// (gsd-core/workflows/code-review/steps/dispatch-fix.md, when="flag:--fix")
|
||||||
|
// — the host now carries only the gsd:section marker + read-or-skip stub.
|
||||||
|
const DISPATCH_FIX_STEP_PATH = path.join(
|
||||||
|
ROOT,
|
||||||
|
'gsd-core',
|
||||||
|
'workflows',
|
||||||
|
'code-review',
|
||||||
|
'steps',
|
||||||
|
'dispatch-fix.md',
|
||||||
|
);
|
||||||
|
const dispatchFixSrc = fs.readFileSync(DISPATCH_FIX_STEP_PATH, 'utf8');
|
||||||
|
|
||||||
test('initialize step references code-review-flags.cjs for flag parsing', () => {
|
test('initialize step references code-review-flags.cjs for flag parsing', () => {
|
||||||
const initStart = src.indexOf('<step name="initialize">');
|
const initStart = src.indexOf('<step name="initialize">');
|
||||||
@@ -247,20 +259,21 @@ describe('#3727 — code-review.md structural dispatch contract', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('workflow has a <step name="dispatch_fix"> step', () => {
|
test('workflow gates a dispatch-fix section (flag:--fix) whose step file has the dispatch_fix step', () => {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
src.includes('<step name="dispatch_fix">'),
|
src.includes('<!-- gsd:section id="dispatch-fix" when="flag:--fix" -->') &&
|
||||||
'code-review.md must have a dispatch_fix step (missing = --fix is silently no-op)'
|
src.includes('gsd-core/workflows/code-review/steps/dispatch-fix.md'),
|
||||||
|
'code-review.md must gate a dispatch-fix section pointing at dispatch-fix.md (missing = --fix is silently no-op)'
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
dispatchFixSrc.includes('<step name="dispatch_fix">'),
|
||||||
|
'dispatch-fix.md step file must contain the dispatch_fix step'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('dispatch_fix step delegates to code-review-fix.md when fix flag is true', () => {
|
test('dispatch_fix step delegates to code-review-fix.md when fix flag is true', () => {
|
||||||
const stepStart = src.indexOf('<step name="dispatch_fix">');
|
|
||||||
const stepEnd = src.indexOf('</step>', stepStart);
|
|
||||||
assert.ok(stepStart !== -1, 'dispatch_fix step must exist');
|
|
||||||
const stepSection = src.slice(stepStart, stepEnd);
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
stepSection.includes('code-review-fix.md'),
|
dispatchFixSrc.includes('code-review-fix.md'),
|
||||||
'dispatch_fix step must reference code-review-fix.md workflow'
|
'dispatch_fix step must reference code-review-fix.md workflow'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -268,11 +281,8 @@ describe('#3727 — code-review.md structural dispatch contract', () => {
|
|||||||
test('dispatch_fix step references gsd-code-fixer agent (via code-review-fix.md chain)', () => {
|
test('dispatch_fix step references gsd-code-fixer agent (via code-review-fix.md chain)', () => {
|
||||||
// The dispatch step must show that fixing will occur, either directly or
|
// The dispatch step must show that fixing will occur, either directly or
|
||||||
// by loading code-review-fix.md (which in turn spawns gsd-code-fixer).
|
// by loading code-review-fix.md (which in turn spawns gsd-code-fixer).
|
||||||
const stepStart = src.indexOf('<step name="dispatch_fix">');
|
|
||||||
const stepEnd = src.indexOf('</step>', stepStart);
|
|
||||||
const stepSection = src.slice(stepStart, stepEnd);
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
stepSection.includes('gsd-code-fixer') || stepSection.includes('code-review-fix.md'),
|
dispatchFixSrc.includes('gsd-code-fixer') || dispatchFixSrc.includes('code-review-fix.md'),
|
||||||
'dispatch_fix step must reference gsd-code-fixer agent or code-review-fix.md workflow'
|
'dispatch_fix step must reference gsd-code-fixer agent or code-review-fix.md workflow'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -531,7 +531,14 @@ describe('CR-INTEGRATION: workflow integration points', () => {
|
|||||||
test('quick.md resolves code-review capability hook', () => {
|
test('quick.md resolves code-review capability hook', () => {
|
||||||
const content = fs.readFileSync(path.join(WORKFLOWS_DIR, 'quick.md'), 'utf-8');
|
const content = fs.readFileSync(path.join(WORKFLOWS_DIR, 'quick.md'), 'utf-8');
|
||||||
const start = content.indexOf('**Step 6.25: Code review (auto)**');
|
const start = content.indexOf('**Step 6.25: Code review (auto)**');
|
||||||
const end = content.indexOf('**Step 6.5: Verification', start);
|
// #2994 (pre-existing since #2994's earlier quick-verification.md extraction,
|
||||||
|
// 18ff35d20): Step 6.5's content moved into
|
||||||
|
// gsd-core/workflows/quick/steps/quick-verification.md behind a
|
||||||
|
// `<!-- gsd:section id="quick-verification" -->` marker, so the literal
|
||||||
|
// "**Step 6.5: Verification" heading text no longer follows Step 6.25 in
|
||||||
|
// this file — the marker is the correct end-of-step delimiter now (mirrors
|
||||||
|
// phase6-review-capabilities.test.cjs's identical retarget for the same move).
|
||||||
|
const end = content.indexOf('<!-- gsd:section id="quick-verification"', start);
|
||||||
assert.ok(start !== -1 && end !== -1, 'quick.md missing Step 6.25 code review section');
|
assert.ok(start !== -1 && end !== -1, 'quick.md missing Step 6.25 code review section');
|
||||||
const reviewContent = content.slice(start, end);
|
const reviewContent = content.slice(start, end);
|
||||||
|
|
||||||
|
|||||||
@@ -1672,10 +1672,16 @@ describe('bug-853 — manager/autonomous gate background dispatch by runtime', (
|
|||||||
assert.match(AUTONOMOUS, /If `FLATTEN` is `false`[\s\S]{0,1200}?run_in_background=true[\s\S]{0,600}?gsd-plan-phase/);
|
assert.match(AUTONOMOUS, /If `FLATTEN` is `false`[\s\S]{0,1200}?run_in_background=true[\s\S]{0,600}?gsd-plan-phase/);
|
||||||
// Background block: run_in_background=true appears within the FLATTEN=false branch and gsd-execute-phase is nearby
|
// Background block: run_in_background=true appears within the FLATTEN=false branch and gsd-execute-phase is nearby
|
||||||
assert.match(AUTONOMOUS, /If `FLATTEN` is `false`[\s\S]{0,3000}?run_in_background=true[\s\S]{0,200}?gsd-execute-phase/);
|
assert.match(AUTONOMOUS, /If `FLATTEN` is `false`[\s\S]{0,3000}?run_in_background=true[\s\S]{0,200}?gsd-execute-phase/);
|
||||||
// Inline is the otherwise/else branch for plan — anchored on FLATTEN=true language (not runtime name)
|
// Inline is the otherwise/else branch for plan — anchored on FLATTEN=true language (not runtime name).
|
||||||
|
// #2994: the window between `FLATTEN` and the inline Skill() call widened
|
||||||
|
// past manager.md's 400 because this branch now carries a
|
||||||
|
// state:plan-strategy-converge conditional-read stub (converge-dispatch-inline,
|
||||||
|
// gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md) ahead of
|
||||||
|
// the local-planning fallback — the stub's full step-file path is more
|
||||||
|
// verbose than the terse `Skill()` call it replaced.
|
||||||
assert.match(
|
assert.match(
|
||||||
AUTONOMOUS,
|
AUTONOMOUS,
|
||||||
/Otherwise[\s\S]{0,100}?`FLATTEN`[\s\S]{0,400}?Skill\(skill="gsd-plan-phase"/,
|
/Otherwise[\s\S]{0,100}?`FLATTEN`[\s\S]{0,700}?Skill\(skill="gsd-plan-phase"/,
|
||||||
);
|
);
|
||||||
// Inline is the otherwise/else branch for execute — anchored on FLATTEN=true language (not runtime name)
|
// Inline is the otherwise/else branch for execute — anchored on FLATTEN=true language (not runtime name)
|
||||||
assert.match(
|
assert.match(
|
||||||
|
|||||||
@@ -943,48 +943,60 @@ describe('feat-3210 / #1012: code-review workflow invokes fallow with the real C
|
|||||||
path.join(ROOT, 'gsd-core', 'workflows', 'code-review.md'),
|
path.join(ROOT, 'gsd-core', 'workflows', 'code-review.md'),
|
||||||
'utf8',
|
'utf8',
|
||||||
);
|
);
|
||||||
|
// #2994: the actual fallow invocation (audit --format json, --changed-since,
|
||||||
|
// --max-crap, verdict-based exit handling) moved to a marker-gated step file
|
||||||
|
// (gsd-core/workflows/code-review/steps/structural-pre-pass.md, when=
|
||||||
|
// "state:fallow-enabled") — read it for the invocation-specific assertions
|
||||||
|
// below; the negative "removed flags" assertion checks BOTH files.
|
||||||
|
const fallowStepSrc = fs.readFileSync(
|
||||||
|
path.join(ROOT, 'gsd-core', 'workflows', 'code-review', 'steps', 'structural-pre-pass.md'),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
const combinedSrc = workflowSrc + '\n' + fallowStepSrc;
|
||||||
|
|
||||||
test('uses audit --format json and --quiet (real fallow 2.x flags)', () => {
|
test('uses audit --format json and --quiet (real fallow 2.x flags)', () => {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
workflowSrc.includes('audit --format json'),
|
fallowStepSrc.includes('audit --format json'),
|
||||||
'workflow must invoke: audit --format json',
|
'workflow must invoke: audit --format json',
|
||||||
);
|
);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
workflowSrc.includes('--quiet'),
|
fallowStepSrc.includes('--quiet'),
|
||||||
'workflow must pass --quiet to suppress progress output',
|
'workflow must pass --quiet to suppress progress output',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('does NOT use removed flags: --json , --profile, --stdin-files', () => {
|
test('does NOT use removed flags: --json , --profile, --stdin-files', () => {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
!workflowSrc.includes('--json '),
|
!combinedSrc.includes('--json '),
|
||||||
'workflow must not use old --json flag (note trailing space to avoid matching --format json)',
|
'workflow must not use old --json flag (note trailing space to avoid matching --format json)',
|
||||||
);
|
);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
!workflowSrc.includes('--profile'),
|
!combinedSrc.includes('--profile'),
|
||||||
'workflow must not use --profile (fallow has no native profile concept)',
|
'workflow must not use --profile (fallow has no native profile concept)',
|
||||||
);
|
);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
!workflowSrc.includes('--stdin-files'),
|
!combinedSrc.includes('--stdin-files'),
|
||||||
'workflow must not use --stdin-files (removed in fallow 2.x)',
|
'workflow must not use --stdin-files (removed in fallow 2.x)',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('uses --max-crap for threshold control (profile maps to max-crap)', () => {
|
test('uses --max-crap for threshold control (profile maps to max-crap)', () => {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
workflowSrc.includes('--max-crap'),
|
fallowStepSrc.includes('--max-crap'),
|
||||||
'workflow must use --max-crap to control threshold (profile mapped to this flag)',
|
'workflow must use --max-crap to control threshold (profile mapped to this flag)',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('scopes phase via --changed-since (native fallow git-ref scoping)', () => {
|
test('scopes phase via --changed-since (native fallow git-ref scoping)', () => {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
workflowSrc.includes('--changed-since'),
|
fallowStepSrc.includes('--changed-since'),
|
||||||
'workflow must use --changed-since for phase scoping',
|
'workflow must use --changed-since for phase scoping',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('normalizes fallow output via normalizeFallowReportFile before embedding', () => {
|
test('normalizes fallow output via normalizeFallowReportFile before embedding', () => {
|
||||||
|
// Still in the host (spawn_reviewer step, unconditional) — not gated by
|
||||||
|
// state:fallow-enabled, since the reviewer prompt assembly always runs.
|
||||||
assert.ok(
|
assert.ok(
|
||||||
workflowSrc.includes('normalizeFallowReportFile'),
|
workflowSrc.includes('normalizeFallowReportFile'),
|
||||||
'workflow must call normalizeFallowReportFile to normalize before embedding into reviewer prompt',
|
'workflow must call normalizeFallowReportFile to normalize before embedding into reviewer prompt',
|
||||||
@@ -993,7 +1005,7 @@ describe('feat-3210 / #1012: code-review workflow invokes fallow with the real C
|
|||||||
|
|
||||||
test('exit-handling gates on valid JSON (verdict in o), not on exit code', () => {
|
test('exit-handling gates on valid JSON (verdict in o), not on exit code', () => {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
workflowSrc.includes("'verdict' in o"),
|
fallowStepSrc.includes("'verdict' in o"),
|
||||||
"workflow exit-handling must use 'verdict' in o to decide success (not exit code)",
|
"workflow exit-handling must use 'verdict' in o to decide success (not exit code)",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -1047,6 +1059,13 @@ describe('feat-3210: workflow and config contracts', () => {
|
|||||||
// The workflow .md uses XML-like <step> tags as its runtime DSL; we parse the step block
|
// The workflow .md uses XML-like <step> tags as its runtime DSL; we parse the step block
|
||||||
// structurally and assert on structural properties, not on prose strings.
|
// structurally and assert on structural properties, not on prose strings.
|
||||||
test('code-review workflow structural_pre_pass step is parseable and references FALLOW.json output', () => {
|
test('code-review workflow structural_pre_pass step is parseable and references FALLOW.json output', () => {
|
||||||
|
// #2994: structural_pre_pass's gated execution (fallow binary resolve + run
|
||||||
|
// + FALLOW.json persistence) moved to a marker-gated step file
|
||||||
|
// (gsd-core/workflows/code-review/steps/structural-pre-pass.md, when=
|
||||||
|
// "state:fallow-enabled"). The config-gate resolver itself — previously an
|
||||||
|
// inline `gsd_run query config-get code_quality.fallow.enabled` call inside
|
||||||
|
// this step's own body (a circular self-disabling gate) — was hoisted into
|
||||||
|
// `detectFallowConfig` (src/init.cts), consumed by `cmdInitCodeReview`.
|
||||||
const workflow = fs.readFileSync(
|
const workflow = fs.readFileSync(
|
||||||
path.join(ROOT, 'gsd-core', 'workflows', 'code-review.md'),
|
path.join(ROOT, 'gsd-core', 'workflows', 'code-review.md'),
|
||||||
'utf8',
|
'utf8',
|
||||||
@@ -1061,16 +1080,30 @@ describe('feat-3210: workflow and config contracts', () => {
|
|||||||
|
|
||||||
const stepBody = stepMatch[1];
|
const stepBody = stepMatch[1];
|
||||||
|
|
||||||
// Structural property: the step body must reference the FALLOW.json output artifact
|
// Structural property: the host stub gates the FALLOW.json-producing
|
||||||
|
// execution behind state:fallow-enabled and points at the step file.
|
||||||
assert.ok(
|
assert.ok(
|
||||||
stepBody.includes('FALLOW.json'),
|
stepBody.includes('when="state:fallow-enabled"') &&
|
||||||
'structural_pre_pass step body must reference the FALLOW.json output artifact',
|
stepBody.includes('gsd-core/workflows/code-review/steps/structural-pre-pass.md'),
|
||||||
|
'structural_pre_pass step must gate its FALLOW.json-producing execution behind state:fallow-enabled',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Structural property: the step body must gate on the fallow enabled config key
|
const stepFile = fs.readFileSync(
|
||||||
|
path.join(ROOT, 'gsd-core', 'workflows', 'code-review', 'steps', 'structural-pre-pass.md'),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
stepBody.includes('code_quality.fallow.enabled'),
|
stepFile.includes('FALLOW.json'),
|
||||||
'structural_pre_pass step body must gate on code_quality.fallow.enabled',
|
'structural-pre-pass.md step file must reference the FALLOW.json output artifact',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Structural property: the config-gate fact is resolved from the real
|
||||||
|
// code_quality.fallow.enabled config key — checked against the resolver
|
||||||
|
// it was hoisted into (src/init.cts's detectFallowConfig).
|
||||||
|
const initSource = fs.readFileSync(path.join(ROOT, 'src', 'init.cts'), 'utf8');
|
||||||
|
assert.ok(
|
||||||
|
/detectFallowConfig[\s\S]{0,600}'code_quality'[\s\S]{0,40}'fallow'[\s\S]{0,40}'enabled'/.test(initSource),
|
||||||
|
'detectFallowConfig (src/init.cts) must gate on code_quality.fallow.enabled',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1884,15 +1884,38 @@ describe('#3086: git.create_tag config key', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('D. complete-milestone.md contains <config-check> gate for git.create_tag', () => {
|
test('D. complete-milestone.md gates git_tag behind state:git-create-tag (#2994: <config-check> hoisted into cmdInitCompleteMilestone)', () => {
|
||||||
|
// #2994: the inline <config-check> (`gsd-tools.cjs query config-get
|
||||||
|
// git.create_tag ... || echo "true"`) that used to gate the git_tag step's
|
||||||
|
// own inclusion was hoisted into `detectGitCreateTag` (src/init.cts),
|
||||||
|
// consumed by the new `cmdInitCompleteMilestone` entry point and exposed
|
||||||
|
// as the init-bundle's `git_create_tag` field / the `state:git-create-tag`
|
||||||
|
// when= atom. The workflow markdown no longer contains a literal
|
||||||
|
// <config-check> block or `git.create_tag` string — it gates the whole
|
||||||
|
// git_tag step behind a gsd:section marker instead.
|
||||||
const content = fs.readFileSync(WORKFLOW_PATH, 'utf8');
|
const content = fs.readFileSync(WORKFLOW_PATH, 'utf8');
|
||||||
assert.ok(
|
assert.ok(
|
||||||
content.includes('git.create_tag'),
|
content.includes('<!-- gsd:section id="git-tag" when="state:git-create-tag" -->'),
|
||||||
'complete-milestone.md must reference git.create_tag in a <config-check> block',
|
'complete-milestone.md must gate the git_tag step behind the state:git-create-tag section marker',
|
||||||
);
|
);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
content.includes('<config-check>'),
|
content.includes('gsd-core/workflows/complete-milestone/steps/git-tag.md'),
|
||||||
'complete-milestone.md must have a <config-check> block in the git_tag step',
|
'complete-milestone.md must point the git-tag section at its step file',
|
||||||
|
);
|
||||||
|
|
||||||
|
const stepFile = fs.readFileSync(
|
||||||
|
path.join(__dirname, '..', 'gsd-core', 'workflows', 'complete-milestone', 'steps', 'git-tag.md'),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
stepFile.includes('<step name="git_tag">') && stepFile.includes('git tag -a'),
|
||||||
|
'git-tag.md step file must contain the git_tag step body (git tag creation)',
|
||||||
|
);
|
||||||
|
|
||||||
|
const initSource = fs.readFileSync(path.join(__dirname, '..', 'src', 'init.cts'), 'utf8');
|
||||||
|
assert.ok(
|
||||||
|
/detectGitCreateTag[\s\S]{0,300}'git'[\s\S]{0,40}'create_tag'/.test(initSource),
|
||||||
|
'src/init.cts detectGitCreateTag must resolve the git.create_tag config key',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"paths": {
|
||||||
|
"update.md": "#2994 (epic #1671 Phase 6.3, FINAL slice): fragmentizes update.md onto the marker grammar, admitting `state:next-channel` (channel-banner section) and a new dedicated `init.update` entry point (cmdInitUpdate). update.md previously carried NO `gsd_run query init.*` call at all; the new INIT_UPDATE fetch (reusing the already-resolved $GSD_TOOLS from get_installed_version, dual .cjs/PATH-shim invocation style, matching the pre-existing update-context call) plus its surrounding comment outweighs the single `<!-- gsd:section -->` marker-pair stub that replaces the extracted channel-banner prose (now living in gsd-core/workflows/update/steps/channel-banner.md). Net SOURCE growth is +703 bytes (26,024 -> 26,727). The pre-existing TAG=\"next\"/TAG=\"latest\" case-statement in parse_update_channel is deliberately left byte-identical — issue #815's regression test (tests/issue-815-update-next-channel.test.cjs) asserts that literal text stays in the workflow, since the npm dist-tag selection must run in the workflow's own shell before any gsd_run round-trip. The EMITTED artifact composeWorkflow produces at install time still includes the extracted prose verbatim (markers strip, gap+section bodies re-join byte-for-byte) when the atom is unresolved (section_manifest null -> read-everything fallback), so installed behavior is unchanged; only the SOURCE file's on-disk byte count moves."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"paths": {
|
||||||
|
"review.md": "#2994 (epic #1671 Phase 6.3, further amendment): fragmentizes review.md onto the marker grammar, admitting `state:reviewer-instances-configured` (shared by two peripheral notes, `reviewer-instances-note-1` in detect_clis and `reviewer-instances-note-2` in invoke_reviewers — the core reviewer-lane dispatch itself stays unmarked). Also retargets the init line from the shared `init.phase-op` to a new dedicated `init.review` entry point (cmdInitReview), which computes the reviewer-instances-configured fact via `review.reviewer_instances` config presence. Net SOURCE growth is +55 bytes (29,063 -> 29,118): two `<!-- gsd:section -->` marker-pair stubs (~331 B and ~195 B) replace their extracted prose bodies (328 B and 277 B respectively, now living in gsd-core/workflows/review/steps/*.md), and `init.phase-op` shrinks by 2 bytes to `init.review` on the gather_context init line. The EMITTED artifact composeWorkflow produces at install time still includes the extracted prose verbatim (markers strip, gap+section bodies re-join byte-for-byte) when the atom is unresolved (section_manifest null -> read-everything fallback), so installed behavior is unchanged; only the SOURCE file's on-disk byte count moves."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -228,7 +228,11 @@ describe('#2287 cmdAuditUat: deferred-items.md awareness', () => {
|
|||||||
|
|
||||||
// ─── forensic_audit workflow-prose source-contract guard ──────────────────
|
// ─── forensic_audit workflow-prose source-contract guard ──────────────────
|
||||||
|
|
||||||
const PROGRESS_MD = path.join(__dirname, '..', 'gsd-core', 'workflows', 'progress.md');
|
// #2994 fragmentization moved the --forensic-gated forensic_audit step out of
|
||||||
|
// progress.md into gsd-core/workflows/progress/steps/forensic-audit.md behind
|
||||||
|
// a section marker. Read that step file directly — it is the sole remaining
|
||||||
|
// source of the forensic_audit step body these guards assert on.
|
||||||
|
const PROGRESS_MD = path.join(__dirname, '..', 'gsd-core', 'workflows', 'progress', 'steps', 'forensic-audit.md');
|
||||||
|
|
||||||
describe('#2287 progress.md forensic_audit: deferred-items.md contract', () => {
|
describe('#2287 progress.md forensic_audit: deferred-items.md contract', () => {
|
||||||
const content = fs.readFileSync(PROGRESS_MD, 'utf-8');
|
const content = fs.readFileSync(PROGRESS_MD, 'utf-8');
|
||||||
@@ -237,7 +241,7 @@ describe('#2287 progress.md forensic_audit: deferred-items.md contract', () => {
|
|||||||
const section = stepStart !== -1 && stepEnd !== -1 ? content.slice(stepStart, stepEnd) : '';
|
const section = stepStart !== -1 && stepEnd !== -1 ? content.slice(stepStart, stepEnd) : '';
|
||||||
|
|
||||||
test('forensic_audit step exists', () => {
|
test('forensic_audit step exists', () => {
|
||||||
assert.notEqual(stepStart, -1, 'progress.md must contain the forensic_audit step');
|
assert.notEqual(stepStart, -1, 'progress.md (or its extracted progress/steps/forensic-audit.md) must contain the forensic_audit step');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('forensic_audit now runs 7 checks (was 6) and globs deferred-items.md', () => {
|
test('forensic_audit now runs 7 checks (was 6) and globs deferred-items.md', () => {
|
||||||
|
|||||||
29
tests/fixtures/install-tree/antigravity.json
vendored
29
tests/fixtures/install-tree/antigravity.json
vendored
@@ -56,6 +56,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -221,15 +222,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -246,6 +256,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -282,7 +293,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -306,12 +322,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -330,14 +355,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-check-update-worker.js",
|
"hooks/gsd-check-update-worker.js",
|
||||||
"hooks/gsd-check-update.js",
|
"hooks/gsd-check-update.js",
|
||||||
"hooks/gsd-config-reload.js",
|
"hooks/gsd-config-reload.js",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/augment.json
vendored
29
tests/fixtures/install-tree/augment.json
vendored
@@ -127,6 +127,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -292,15 +293,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -317,6 +327,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -353,7 +364,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -377,12 +393,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -401,14 +426,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-check-update-worker.js",
|
"hooks/gsd-check-update-worker.js",
|
||||||
"hooks/gsd-check-update.js",
|
"hooks/gsd-check-update.js",
|
||||||
"hooks/gsd-config-reload.js",
|
"hooks/gsd-config-reload.js",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/claude-local.json
vendored
29
tests/fixtures/install-tree/claude-local.json
vendored
@@ -126,6 +126,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -291,15 +292,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -316,6 +326,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -352,7 +363,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -376,12 +392,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -400,14 +425,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-check-update-worker.js",
|
"hooks/gsd-check-update-worker.js",
|
||||||
"hooks/gsd-check-update.js",
|
"hooks/gsd-check-update.js",
|
||||||
"hooks/gsd-config-reload.js",
|
"hooks/gsd-config-reload.js",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/claude.json
vendored
29
tests/fixtures/install-tree/claude.json
vendored
@@ -55,6 +55,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -220,15 +221,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -245,6 +255,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -281,7 +292,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -305,12 +321,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -329,14 +354,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-check-update-worker.js",
|
"hooks/gsd-check-update-worker.js",
|
||||||
"hooks/gsd-check-update.js",
|
"hooks/gsd-check-update.js",
|
||||||
"hooks/gsd-config-reload.js",
|
"hooks/gsd-config-reload.js",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/cline.json
vendored
29
tests/fixtures/install-tree/cline.json
vendored
@@ -59,6 +59,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -224,15 +225,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -249,6 +259,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -285,7 +296,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -309,12 +325,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -333,14 +358,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"scripts/changeset/README.md",
|
"scripts/changeset/README.md",
|
||||||
"scripts/changeset/cli.cjs",
|
"scripts/changeset/cli.cjs",
|
||||||
"scripts/changeset/github-release-notes.cjs",
|
"scripts/changeset/github-release-notes.cjs",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/codebuddy.json
vendored
29
tests/fixtures/install-tree/codebuddy.json
vendored
@@ -127,6 +127,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -292,15 +293,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -317,6 +327,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -353,7 +364,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -377,12 +393,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -401,14 +426,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-check-update-worker.js",
|
"hooks/gsd-check-update-worker.js",
|
||||||
"hooks/gsd-check-update.js",
|
"hooks/gsd-check-update.js",
|
||||||
"hooks/gsd-config-reload.js",
|
"hooks/gsd-config-reload.js",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/codex.json
vendored
29
tests/fixtures/install-tree/codex.json
vendored
@@ -162,6 +162,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -327,15 +328,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -352,6 +362,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -388,7 +399,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -412,12 +428,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -436,14 +461,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-check-update-worker.js",
|
"hooks/gsd-check-update-worker.js",
|
||||||
"hooks/gsd-check-update.js",
|
"hooks/gsd-check-update.js",
|
||||||
"hooks/gsd-context-monitor.js",
|
"hooks/gsd-context-monitor.js",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/copilot.json
vendored
29
tests/fixtures/install-tree/copilot.json
vendored
@@ -57,6 +57,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -222,15 +223,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -247,6 +257,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -283,7 +294,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -307,12 +323,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -331,14 +356,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-session.json",
|
"hooks/gsd-session.json",
|
||||||
"scripts/changeset/README.md",
|
"scripts/changeset/README.md",
|
||||||
"scripts/changeset/cli.cjs",
|
"scripts/changeset/cli.cjs",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/cursor.json
vendored
29
tests/fixtures/install-tree/cursor.json
vendored
@@ -56,6 +56,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -221,15 +222,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -246,6 +256,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -282,7 +293,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -306,12 +322,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -330,14 +355,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-cursor-post-tool.js",
|
"hooks/gsd-cursor-post-tool.js",
|
||||||
"hooks/gsd-cursor-pre-tool.js",
|
"hooks/gsd-cursor-pre-tool.js",
|
||||||
"hooks/gsd-cursor-session-start.js",
|
"hooks/gsd-cursor-session-start.js",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/hermes.json
vendored
29
tests/fixtures/install-tree/hermes.json
vendored
@@ -56,6 +56,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -221,15 +222,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -246,6 +256,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -282,7 +293,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -306,12 +322,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -330,14 +355,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-check-update-worker.js",
|
"hooks/gsd-check-update-worker.js",
|
||||||
"hooks/gsd-check-update.js",
|
"hooks/gsd-check-update.js",
|
||||||
"hooks/gsd-config-reload.js",
|
"hooks/gsd-config-reload.js",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/kilo.json
vendored
29
tests/fixtures/install-tree/kilo.json
vendored
@@ -127,6 +127,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -292,15 +293,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -317,6 +327,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -353,7 +364,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -377,12 +393,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -401,14 +426,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-check-update-worker.js",
|
"hooks/gsd-check-update-worker.js",
|
||||||
"hooks/gsd-check-update.js",
|
"hooks/gsd-check-update.js",
|
||||||
"hooks/gsd-config-reload.js",
|
"hooks/gsd-config-reload.js",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/kimi-code.json
vendored
29
tests/fixtures/install-tree/kimi-code.json
vendored
@@ -86,6 +86,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -251,15 +252,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -276,6 +286,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -312,7 +323,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -336,12 +352,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -360,14 +385,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"scripts/changeset/README.md",
|
"scripts/changeset/README.md",
|
||||||
"scripts/changeset/cli.cjs",
|
"scripts/changeset/cli.cjs",
|
||||||
"scripts/changeset/github-release-notes.cjs",
|
"scripts/changeset/github-release-notes.cjs",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/kimi.json
vendored
29
tests/fixtures/install-tree/kimi.json
vendored
@@ -122,6 +122,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -287,15 +288,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -312,6 +322,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -348,7 +359,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -372,12 +388,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -396,14 +421,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"scripts/changeset/README.md",
|
"scripts/changeset/README.md",
|
||||||
"scripts/changeset/cli.cjs",
|
"scripts/changeset/cli.cjs",
|
||||||
"scripts/changeset/github-release-notes.cjs",
|
"scripts/changeset/github-release-notes.cjs",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/opencode.json
vendored
29
tests/fixtures/install-tree/opencode.json
vendored
@@ -127,6 +127,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -292,15 +293,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -317,6 +327,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -353,7 +364,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -377,12 +393,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -401,14 +426,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-check-update-worker.js",
|
"hooks/gsd-check-update-worker.js",
|
||||||
"hooks/gsd-check-update.js",
|
"hooks/gsd-check-update.js",
|
||||||
"hooks/gsd-config-reload.js",
|
"hooks/gsd-config-reload.js",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/pi.json
vendored
29
tests/fixtures/install-tree/pi.json
vendored
@@ -24,6 +24,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -189,15 +190,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -214,6 +224,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -250,7 +261,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -274,12 +290,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -298,14 +323,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-check-update-worker.js",
|
"hooks/gsd-check-update-worker.js",
|
||||||
"hooks/gsd-check-update.js",
|
"hooks/gsd-check-update.js",
|
||||||
"hooks/gsd-config-reload.js",
|
"hooks/gsd-config-reload.js",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/qwen.json
vendored
29
tests/fixtures/install-tree/qwen.json
vendored
@@ -56,6 +56,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -221,15 +222,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -246,6 +256,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -282,7 +293,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -306,12 +322,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -330,14 +355,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-check-update-worker.js",
|
"hooks/gsd-check-update-worker.js",
|
||||||
"hooks/gsd-check-update.js",
|
"hooks/gsd-check-update.js",
|
||||||
"hooks/gsd-config-reload.js",
|
"hooks/gsd-config-reload.js",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/trae.json
vendored
29
tests/fixtures/install-tree/trae.json
vendored
@@ -56,6 +56,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -221,15 +222,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -246,6 +256,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -282,7 +293,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -306,12 +322,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -330,14 +355,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"scripts/changeset/README.md",
|
"scripts/changeset/README.md",
|
||||||
"scripts/changeset/cli.cjs",
|
"scripts/changeset/cli.cjs",
|
||||||
"scripts/changeset/github-release-notes.cjs",
|
"scripts/changeset/github-release-notes.cjs",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/windsurf.json
vendored
29
tests/fixtures/install-tree/windsurf.json
vendored
@@ -56,6 +56,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -221,15 +222,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -246,6 +256,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -282,7 +293,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -306,12 +322,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -330,14 +355,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"hooks/gsd-windsurf-pre-command.js",
|
"hooks/gsd-windsurf-pre-command.js",
|
||||||
"hooks/gsd-windsurf-pre-write.js",
|
"hooks/gsd-windsurf-pre-write.js",
|
||||||
"hooks/package.json",
|
"hooks/package.json",
|
||||||
|
|||||||
29
tests/fixtures/install-tree/zcode.json
vendored
29
tests/fixtures/install-tree/zcode.json
vendored
@@ -127,6 +127,7 @@
|
|||||||
"gsd-core/references/api-coverage.md",
|
"gsd-core/references/api-coverage.md",
|
||||||
"gsd-core/references/artifact-types.md",
|
"gsd-core/references/artifact-types.md",
|
||||||
"gsd-core/references/autonomous-smart-discuss.md",
|
"gsd-core/references/autonomous-smart-discuss.md",
|
||||||
|
"gsd-core/references/autonomous-ui-design-contract.md",
|
||||||
"gsd-core/references/checkpoints.md",
|
"gsd-core/references/checkpoints.md",
|
||||||
"gsd-core/references/common-bug-patterns.md",
|
"gsd-core/references/common-bug-patterns.md",
|
||||||
"gsd-core/references/context-budget.md",
|
"gsd-core/references/context-budget.md",
|
||||||
@@ -292,15 +293,24 @@
|
|||||||
"gsd-core/workflows/audit-milestone.md",
|
"gsd-core/workflows/audit-milestone.md",
|
||||||
"gsd-core/workflows/audit-uat.md",
|
"gsd-core/workflows/audit-uat.md",
|
||||||
"gsd-core/workflows/autonomous.md",
|
"gsd-core/workflows/autonomous.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-banner.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-fail-fast.md",
|
||||||
|
"gsd-core/workflows/autonomous/steps/converge-loop.md",
|
||||||
"gsd-core/workflows/check-todos.md",
|
"gsd-core/workflows/check-todos.md",
|
||||||
"gsd-core/workflows/cleanup.md",
|
"gsd-core/workflows/cleanup.md",
|
||||||
"gsd-core/workflows/code-review-fix.md",
|
"gsd-core/workflows/code-review-fix.md",
|
||||||
"gsd-core/workflows/code-review.md",
|
"gsd-core/workflows/code-review.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/dispatch-fix.md",
|
||||||
|
"gsd-core/workflows/code-review/steps/structural-pre-pass.md",
|
||||||
"gsd-core/workflows/complete-milestone.md",
|
"gsd-core/workflows/complete-milestone.md",
|
||||||
|
"gsd-core/workflows/complete-milestone/steps/git-tag.md",
|
||||||
"gsd-core/workflows/debug.md",
|
"gsd-core/workflows/debug.md",
|
||||||
"gsd-core/workflows/diagnose-issues.md",
|
"gsd-core/workflows/diagnose-issues.md",
|
||||||
"gsd-core/workflows/discovery-phase.md",
|
"gsd-core/workflows/discovery-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase-assumptions.md",
|
"gsd-core/workflows/discuss-phase-assumptions.md",
|
||||||
|
"gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md",
|
||||||
"gsd-core/workflows/discuss-phase-power.md",
|
"gsd-core/workflows/discuss-phase-power.md",
|
||||||
"gsd-core/workflows/discuss-phase.md",
|
"gsd-core/workflows/discuss-phase.md",
|
||||||
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
"gsd-core/workflows/discuss-phase/modes/advisor.md",
|
||||||
@@ -317,6 +327,7 @@
|
|||||||
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
"gsd-core/workflows/discuss-phase/templates/discussion-log.md",
|
||||||
"gsd-core/workflows/do.md",
|
"gsd-core/workflows/do.md",
|
||||||
"gsd-core/workflows/docs-update.md",
|
"gsd-core/workflows/docs-update.md",
|
||||||
|
"gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md",
|
||||||
"gsd-core/workflows/edit-phase.md",
|
"gsd-core/workflows/edit-phase.md",
|
||||||
"gsd-core/workflows/eval-review.md",
|
"gsd-core/workflows/eval-review.md",
|
||||||
"gsd-core/workflows/execute-phase.md",
|
"gsd-core/workflows/execute-phase.md",
|
||||||
@@ -353,7 +364,12 @@
|
|||||||
"gsd-core/workflows/milestone-summary.md",
|
"gsd-core/workflows/milestone-summary.md",
|
||||||
"gsd-core/workflows/mvp-phase.md",
|
"gsd-core/workflows/mvp-phase.md",
|
||||||
"gsd-core/workflows/new-milestone.md",
|
"gsd-core/workflows/new-milestone.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md",
|
||||||
|
"gsd-core/workflows/new-milestone/steps/reset-phase-safety.md",
|
||||||
"gsd-core/workflows/new-project.md",
|
"gsd-core/workflows/new-project.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-config.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/auto-mode-detection.md",
|
||||||
|
"gsd-core/workflows/new-project/steps/codebase-map-offer.md",
|
||||||
"gsd-core/workflows/new-workspace.md",
|
"gsd-core/workflows/new-workspace.md",
|
||||||
"gsd-core/workflows/next.md",
|
"gsd-core/workflows/next.md",
|
||||||
"gsd-core/workflows/node-repair.md",
|
"gsd-core/workflows/node-repair.md",
|
||||||
@@ -377,12 +393,21 @@
|
|||||||
"gsd-core/workflows/pr-branch.md",
|
"gsd-core/workflows/pr-branch.md",
|
||||||
"gsd-core/workflows/profile-user.md",
|
"gsd-core/workflows/profile-user.md",
|
||||||
"gsd-core/workflows/progress.md",
|
"gsd-core/workflows/progress.md",
|
||||||
|
"gsd-core/workflows/progress/steps/forensic-audit.md",
|
||||||
|
"gsd-core/workflows/progress/steps/mvp-display.md",
|
||||||
"gsd-core/workflows/quick.md",
|
"gsd-core/workflows/quick.md",
|
||||||
|
"gsd-core/workflows/quick/steps/discussion-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/plan-checker-loop.md",
|
||||||
|
"gsd-core/workflows/quick/steps/quick-verification.md",
|
||||||
|
"gsd-core/workflows/quick/steps/research-phase.md",
|
||||||
|
"gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md",
|
||||||
"gsd-core/workflows/reapply-patches.md",
|
"gsd-core/workflows/reapply-patches.md",
|
||||||
"gsd-core/workflows/remove-phase.md",
|
"gsd-core/workflows/remove-phase.md",
|
||||||
"gsd-core/workflows/remove-workspace.md",
|
"gsd-core/workflows/remove-workspace.md",
|
||||||
"gsd-core/workflows/resume-project.md",
|
"gsd-core/workflows/resume-project.md",
|
||||||
"gsd-core/workflows/review.md",
|
"gsd-core/workflows/review.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-1.md",
|
||||||
|
"gsd-core/workflows/review/steps/reviewer-instances-note-2.md",
|
||||||
"gsd-core/workflows/scan.md",
|
"gsd-core/workflows/scan.md",
|
||||||
"gsd-core/workflows/section-manifest.json",
|
"gsd-core/workflows/section-manifest.json",
|
||||||
"gsd-core/workflows/secure-phase.md",
|
"gsd-core/workflows/secure-phase.md",
|
||||||
@@ -401,14 +426,18 @@
|
|||||||
"gsd-core/workflows/sync-skills.md",
|
"gsd-core/workflows/sync-skills.md",
|
||||||
"gsd-core/workflows/thread.md",
|
"gsd-core/workflows/thread.md",
|
||||||
"gsd-core/workflows/transition.md",
|
"gsd-core/workflows/transition.md",
|
||||||
|
"gsd-core/workflows/transition/steps/workstream-collision-check.md",
|
||||||
"gsd-core/workflows/ui-phase.md",
|
"gsd-core/workflows/ui-phase.md",
|
||||||
"gsd-core/workflows/ui-review.md",
|
"gsd-core/workflows/ui-review.md",
|
||||||
"gsd-core/workflows/ultraplan-phase.md",
|
"gsd-core/workflows/ultraplan-phase.md",
|
||||||
"gsd-core/workflows/undo.md",
|
"gsd-core/workflows/undo.md",
|
||||||
"gsd-core/workflows/update.md",
|
"gsd-core/workflows/update.md",
|
||||||
|
"gsd-core/workflows/update/steps/channel-banner.md",
|
||||||
"gsd-core/workflows/validate-phase.md",
|
"gsd-core/workflows/validate-phase.md",
|
||||||
"gsd-core/workflows/verify-phase.md",
|
"gsd-core/workflows/verify-phase.md",
|
||||||
"gsd-core/workflows/verify-work.md",
|
"gsd-core/workflows/verify-work.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/automated-ui-verification.md",
|
||||||
|
"gsd-core/workflows/verify-work/steps/mvp-uat-framing.md",
|
||||||
"scripts/changeset/README.md",
|
"scripts/changeset/README.md",
|
||||||
"scripts/changeset/cli.cjs",
|
"scripts/changeset/cli.cjs",
|
||||||
"scripts/changeset/github-release-notes.cjs",
|
"scripts/changeset/github-release-notes.cjs",
|
||||||
|
|||||||
@@ -715,29 +715,40 @@ describe('workflow gsd_run query init.<workflow> invocations forward every flag:
|
|||||||
const workflowMdPath = path.join(ROOT, 'gsd-core', 'workflows', `${workflow}.md`);
|
const workflowMdPath = path.join(ROOT, 'gsd-core', 'workflows', `${workflow}.md`);
|
||||||
const content = fs.readFileSync(workflowMdPath, 'utf8');
|
const content = fs.readFileSync(workflowMdPath, 'utf8');
|
||||||
|
|
||||||
const initLineRe = new RegExp(`^.*gsd_run query init\\.${workflow}\\b.*$`, 'm');
|
// A workflow may call `gsd_run query init.<workflow>` more than once
|
||||||
const initLineMatch = content.match(initLineRe);
|
// (e.g. new-milestone.md's early INIT_EARLY call, whose section_manifest
|
||||||
assert.ok(initLineMatch, `${workflow}.md must contain a "gsd_run query init.${workflow}" invocation line`);
|
// gates ONLY project-md-milestone-write per its own prose, followed by
|
||||||
const initLine = initLineMatch[0];
|
// a later full INIT call that gates every other flag-gated section and
|
||||||
|
// does forward the flag). Every occurrence is a candidate carrier —
|
||||||
// Everything up to and including the invocation line: real workflows
|
// a flagAtom only needs forwarding by AT LEAST ONE invocation line,
|
||||||
// parse $ARGUMENTS into a param variable earlier in the same bash
|
// not necessarily the first.
|
||||||
// block, then reference that variable on the INIT= line itself.
|
const initLineRe = new RegExp(`^.*gsd_run query init\\.${workflow}\\b.*$`, 'mg');
|
||||||
const scope = content.slice(0, content.indexOf(initLine) + initLine.length);
|
const initLineMatches = [...content.matchAll(initLineRe)];
|
||||||
const referencedVars = varsReferencedIn(initLine);
|
assert.ok(initLineMatches.length > 0, `${workflow}.md must contain a "gsd_run query init.${workflow}" invocation line`);
|
||||||
|
|
||||||
for (const atomFlag of flagAtoms) {
|
for (const atomFlag of flagAtoms) {
|
||||||
const directlyPresent = initLine
|
const forwardedByAnyInvocation = initLineMatches.some((match) => {
|
||||||
.split(/\s+/)
|
const initLine = match[0];
|
||||||
.includes(atomFlag);
|
// Everything up to and including this invocation line: real
|
||||||
const carriers = varsCarryingFlagToken(scope, atomFlag);
|
// workflows parse $ARGUMENTS into a param variable earlier in the
|
||||||
const forwardedViaVar = [...carriers].some((v) => referencedVars.has(v));
|
// same bash block, then reference that variable on the INIT= line
|
||||||
|
// itself.
|
||||||
|
const scope = content.slice(0, match.index + initLine.length);
|
||||||
|
const referencedVars = varsReferencedIn(initLine);
|
||||||
|
|
||||||
|
const directlyPresent = initLine
|
||||||
|
.split(/\s+/)
|
||||||
|
.includes(atomFlag);
|
||||||
|
const carriers = varsCarryingFlagToken(scope, atomFlag);
|
||||||
|
const forwardedViaVar = [...carriers].some((v) => referencedVars.has(v));
|
||||||
|
return directlyPresent || forwardedViaVar;
|
||||||
|
});
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
directlyPresent || forwardedViaVar,
|
forwardedByAnyInvocation,
|
||||||
`${workflow}.md's "gsd_run query init.${workflow}" invocation line must forward a parameter for ` +
|
`${workflow}.md's "gsd_run query init.${workflow}" invocation line(s) must forward a parameter for ` +
|
||||||
`${atomFlag} (its gated section is otherwise permanently excluded — see BLOCKER, epic #1671 Phase 6.2). ` +
|
`${atomFlag} (its gated section is otherwise permanently excluded — see BLOCKER, epic #1671 Phase 6.2). ` +
|
||||||
`Invocation line: ${initLine}`,
|
`Invocation line(s): ${initLineMatches.map((m) => m[0]).join(' | ')}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const assert = require('node:assert/strict');
|
|||||||
const fs = require('node:fs');
|
const fs = require('node:fs');
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const fc = require('./helpers/fast-check-setup.cjs');
|
const fc = require('./helpers/fast-check-setup.cjs');
|
||||||
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
const { runGsdTools, createTempProject, cleanup, readWorkflowCombined } = require('./helpers.cjs');
|
||||||
|
|
||||||
const ROOT = path.resolve(__dirname, '..');
|
const ROOT = path.resolve(__dirname, '..');
|
||||||
const WORKFLOWS = path.join(ROOT, 'gsd-core', 'workflows');
|
const WORKFLOWS = path.join(ROOT, 'gsd-core', 'workflows');
|
||||||
@@ -49,12 +49,20 @@ function statesOmitRule(content) {
|
|||||||
* reported green across 15 non-compliant files for no better reason than that
|
* reported green across 15 non-compliant files for no better reason than that
|
||||||
* nobody had added them to it. Deriving the set is what makes a 16th file
|
* nobody had added them to it. Deriving the set is what makes a 16th file
|
||||||
* impossible to add silently.
|
* impossible to add silently.
|
||||||
|
*
|
||||||
|
* #2994: `content` is read via `readWorkflowCombined` (host + its
|
||||||
|
* `workflows/<wf>/steps/*.md` fragments), not the bare host file. The
|
||||||
|
* fragment model can move a workflow's own omit-rule prose (e.g.
|
||||||
|
* quick.md's rule lives in `quick/steps/research-phase.md` behind a
|
||||||
|
* `<!-- gsd:section -->` stub) out of the host without moving its
|
||||||
|
* `model="{…}"` dispatch site, so a host-only read would report a
|
||||||
|
* false positive for a workflow that still documents the rule.
|
||||||
*/
|
*/
|
||||||
function workflowsThatDispatchWithAModel() {
|
function workflowsThatDispatchWithAModel() {
|
||||||
return fs
|
return fs
|
||||||
.readdirSync(WORKFLOWS)
|
.readdirSync(WORKFLOWS)
|
||||||
.filter((f) => f.endsWith('.md'))
|
.filter((f) => f.endsWith('.md'))
|
||||||
.map((f) => ({ file: f, content: fs.readFileSync(path.join(WORKFLOWS, f), 'utf8') }))
|
.map((f) => ({ file: f, content: readWorkflowCombined(path.join(WORKFLOWS, f)) }))
|
||||||
.filter((w) => /model="\{/.test(w.content));
|
.filter((w) => /model="\{/.test(w.content));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -580,6 +580,37 @@ describe('new-milestone.md: workstream-aware PROJECT.md guard (#2308)', () => {
|
|||||||
// would break bash mid-script (DEFECT.TEST-SHELL-PIPELINE-NONPORTABLE, #2650).
|
// would break bash mid-script (DEFECT.TEST-SHELL-PIPELINE-NONPORTABLE, #2650).
|
||||||
const content = readFileNormalized(workflowPath);
|
const content = readFileNormalized(workflowPath);
|
||||||
|
|
||||||
|
// #2994 fragmentization moved the 7.5 reset-phase-safety section (including
|
||||||
|
// its "/gsd:new-milestone --reset-phase-numbers ${GSD_WS}" rerun hint) out
|
||||||
|
// of new-milestone.md into gsd-core/workflows/new-milestone/steps/reset-phase-safety.md
|
||||||
|
// behind a section marker. Only the routing-interpolation test below needs
|
||||||
|
// that moved text, so it reads host + step file combined instead of
|
||||||
|
// widening `content` (used for host-only fence extraction elsewhere in
|
||||||
|
// this describe block).
|
||||||
|
const contentWithSteps = content + '\n' + fs.readFileSync(
|
||||||
|
path.join(__dirname, '..', 'gsd-core', 'workflows', 'new-milestone', 'steps', 'reset-phase-safety.md'),
|
||||||
|
'utf8'
|
||||||
|
);
|
||||||
|
|
||||||
|
// #2994 final slice: Step 4's "Part A" milestone-state write moved out of
|
||||||
|
// new-milestone.md into gsd-core/workflows/new-milestone/steps/
|
||||||
|
// project-md-milestone-write.md behind a section marker. The
|
||||||
|
// "step 4 scopes the workstream skip" test below asserts on Part A's
|
||||||
|
// actual body (GSD_WS mentions, the workstream skip description) — rather
|
||||||
|
// than widen `content` for every test in this block, SPLICE the step
|
||||||
|
// file's content into the marker's exact position so partAIdx/partBIdx
|
||||||
|
// position-sensitive slicing below still works. A non-vacuity check
|
||||||
|
// (blank the step file, confirm the splice fails, restore) backs this.
|
||||||
|
const PROJECT_MD_STEP_PATH = path.join(
|
||||||
|
__dirname, '..', 'gsd-core', 'workflows', 'new-milestone', 'steps', 'project-md-milestone-write.md'
|
||||||
|
);
|
||||||
|
const PROJECT_MD_MARKER_RE = /<!-- gsd:section id="project-md-milestone-write"[\s\S]*?<!-- \/gsd:section -->/;
|
||||||
|
function contentWithProjectMdStepSpliced() {
|
||||||
|
const stepBody = fs.readFileSync(PROJECT_MD_STEP_PATH, 'utf8');
|
||||||
|
assert.match(content, PROJECT_MD_MARKER_RE, 'project-md-milestone-write marker not found in new-milestone.md');
|
||||||
|
return content.replace(PROJECT_MD_MARKER_RE, stepBody);
|
||||||
|
}
|
||||||
|
|
||||||
// Locate the first ```bash fence strictly between two headings.
|
// Locate the first ```bash fence strictly between two headings.
|
||||||
function extractFenceBetween(markdown, startHeading, endHeading) {
|
function extractFenceBetween(markdown, startHeading, endHeading) {
|
||||||
const startIdx = markdown.indexOf(startHeading);
|
const startIdx = markdown.indexOf(startHeading);
|
||||||
@@ -677,7 +708,7 @@ describe('new-milestone.md: workstream-aware PROJECT.md guard (#2308)', () => {
|
|||||||
|
|
||||||
test('routing interpolations still propagate ${GSD_WS} at the documented lines', () => {
|
test('routing interpolations still propagate ${GSD_WS} at the documented lines', () => {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
content.includes('/gsd:new-milestone --reset-phase-numbers ${GSD_WS}'),
|
contentWithSteps.includes('/gsd:new-milestone --reset-phase-numbers ${GSD_WS}'),
|
||||||
'reset-phase-numbers rerun hint should propagate ${GSD_WS}'
|
'reset-phase-numbers rerun hint should propagate ${GSD_WS}'
|
||||||
);
|
);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
@@ -699,10 +730,11 @@ describe('new-milestone.md: workstream-aware PROJECT.md guard (#2308)', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('step 4 scopes the workstream skip to the milestone-state write only; Evolution repair always runs (finding 2)', () => {
|
test('step 4 scopes the workstream skip to the milestone-state write only; Evolution repair always runs (finding 2)', () => {
|
||||||
const step4Idx = content.indexOf('## 4. Update PROJECT.md');
|
const splicedContent = contentWithProjectMdStepSpliced();
|
||||||
const step5Idx = content.indexOf('## 5. Update STATE.md');
|
const step4Idx = splicedContent.indexOf('## 4. Update PROJECT.md');
|
||||||
|
const step5Idx = splicedContent.indexOf('## 5. Update STATE.md');
|
||||||
assert.ok(step4Idx !== -1 && step5Idx !== -1 && step4Idx < step5Idx, 'steps 4 and 5 should be locatable');
|
assert.ok(step4Idx !== -1 && step5Idx !== -1 && step4Idx < step5Idx, 'steps 4 and 5 should be locatable');
|
||||||
const step4Body = content.slice(step4Idx, step5Idx);
|
const step4Body = splicedContent.slice(step4Idx, step5Idx);
|
||||||
|
|
||||||
const partAIdx = step4Body.indexOf('Part A');
|
const partAIdx = step4Body.indexOf('Part A');
|
||||||
const partBIdx = step4Body.indexOf('Part B');
|
const partBIdx = step4Body.indexOf('Part B');
|
||||||
|
|||||||
@@ -53,8 +53,27 @@ describe('new-project — MVP mode prompt', () => {
|
|||||||
// the #3784 two-question split already shipped for /gsd:settings. new-project.md has no
|
// the #3784 two-question split already shipped for /gsd:settings. new-project.md has no
|
||||||
// <step> tags, so blocks are located by the `header: "AI Models"` marker.
|
// <step> tags, so blocks are located by the `header: "AI Models"` marker.
|
||||||
|
|
||||||
|
// #2994 fragmentization moved the Step 2a "AI Models" auto-mode prompt (and its
|
||||||
|
// config-new-project example payload) out of new-project.md into
|
||||||
|
// gsd-core/workflows/new-project/steps/auto-mode-config.md behind a section
|
||||||
|
// marker (Step 5's interactive prompt stays in the host). Read host + every
|
||||||
|
// step file combined so the "at least two AI Models prompts" / "both example
|
||||||
|
// payloads" counts are preserved across the split rather than reduced.
|
||||||
|
const NEW_PROJECT_STEPS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows', 'new-project', 'steps');
|
||||||
|
function readNewProjectCombined() {
|
||||||
|
let combined = fs.readFileSync(WORKFLOW, 'utf8');
|
||||||
|
if (fs.existsSync(NEW_PROJECT_STEPS_DIR)) {
|
||||||
|
for (const entry of fs.readdirSync(NEW_PROJECT_STEPS_DIR).sort()) {
|
||||||
|
if (entry.endsWith('.md')) {
|
||||||
|
combined += '\n' + fs.readFileSync(path.join(NEW_PROJECT_STEPS_DIR, entry), 'utf8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
|
||||||
describe('bug #1516: new-project AI Models prompt exposes all 5 model profiles', () => {
|
describe('bug #1516: new-project AI Models prompt exposes all 5 model profiles', () => {
|
||||||
const content = fs.readFileSync(WORKFLOW, 'utf-8');
|
const content = readNewProjectCombined();
|
||||||
|
|
||||||
// Locate every `header: "AI Models"` AskUserQuestion block and grab a window large
|
// Locate every `header: "AI Models"` AskUserQuestion block and grab a window large
|
||||||
// enough to include its conditional Q2 successor (the standard-tier picker).
|
// enough to include its conditional Q2 successor (the standard-tier picker).
|
||||||
|
|||||||
@@ -116,7 +116,13 @@ describe('ADR-857 phase 6 verification and review capability migration', () => {
|
|||||||
|
|
||||||
test('quick full-mode code review resolves execute:post hooks instead of inlining code_review config', () => {
|
test('quick full-mode code review resolves execute:post hooks instead of inlining code_review config', () => {
|
||||||
const content = workflow('quick.md');
|
const content = workflow('quick.md');
|
||||||
const section = sectionBetween(content, '**Step 6.25: Code review (auto)**', '**Step 6.5: Verification');
|
// #2994 fragmentization moved Step 6.5 (Verification) out of quick.md into
|
||||||
|
// gsd-core/workflows/quick/steps/quick-verification.md; the parent now marks
|
||||||
|
// that boundary with a `<!-- gsd:section id="quick-verification" -->` comment
|
||||||
|
// immediately after Step 6.25's content, so it remains the correct
|
||||||
|
// end-of-step delimiter (mirrors the execute-phase.md gap-closure-artifacts
|
||||||
|
// pattern above) instead of bleeding to end-of-file.
|
||||||
|
const section = sectionBetween(content, '**Step 6.25: Code review (auto)**', '<!-- gsd:section id="quick-verification"');
|
||||||
|
|
||||||
assert.ok(section.includes('loop render-hooks execute:post'));
|
assert.ok(section.includes('loop render-hooks execute:post'));
|
||||||
assert.ok(section.includes('gsd-code-reviewer'));
|
assert.ok(section.includes('gsd-code-reviewer'));
|
||||||
|
|||||||
@@ -1161,12 +1161,22 @@ describe('feat-22-surfacing-docs', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('new-project workflow has Drift Guard default-yes option', () => {
|
test('new-project workflow has Drift Guard default-yes option', () => {
|
||||||
const content = fs.readFileSync(NEW_PROJECT_PATH, 'utf-8');
|
// #2994 fragmentization moved the Step 2a auto-mode AI/Drift-Guard
|
||||||
|
// AskUserQuestion block (the sole holder of this exact phrasing, even
|
||||||
|
// pre-fragmentization — Step 5's interactive block uses different wording)
|
||||||
|
// out of new-project.md into
|
||||||
|
// gsd-core/workflows/new-project/steps/auto-mode-config.md. Read host +
|
||||||
|
// step combined so this count is preserved across the split.
|
||||||
|
const content = fs.readFileSync(NEW_PROJECT_PATH, 'utf-8') +
|
||||||
|
'\n' + fs.readFileSync(
|
||||||
|
path.join(ROOT, 'gsd-core', 'workflows', 'new-project', 'steps', 'auto-mode-config.md'),
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
// Both question blocks (auto and interactive) should have the yes option
|
// Both question blocks (auto and interactive) should have the yes option
|
||||||
const count = (content.match(/Yes \(Recommended\).*catches hallucinated names/g) || []).length;
|
const count = (content.match(/Yes \(Recommended\).*catches hallucinated names/g) || []).length;
|
||||||
assert.ok(
|
assert.ok(
|
||||||
count >= 1,
|
count >= 1,
|
||||||
'new-project.md must have at least one Drift Guard "Yes (Recommended)" option'
|
'new-project.md (host + new-project/steps/*.md) must have at least one Drift Guard "Yes (Recommended)" option'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,25 @@ const assert = require('node:assert/strict');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
|
// #2994 fragmentization moved the automated_ui_verification step out of
|
||||||
|
// verify-work.md into gsd-core/workflows/verify-work/steps/automated-ui-verification.md
|
||||||
|
// behind a section marker (`state:ui-phase-active`). The host no longer
|
||||||
|
// contains any "playwright" mention at all — reading the host alone now
|
||||||
|
// only passes these two assertions via unrelated substring coincidences
|
||||||
|
// ("automated"/"UI" from the section-marker id and an unrelated "User-facing
|
||||||
|
// changes - UI" bullet; "fall back" from an unrelated subagent-dispatch
|
||||||
|
// line), which is vacuous. Read the step file directly — it is the sole
|
||||||
|
// remaining source of the real Playwright content.
|
||||||
|
const AUTOMATED_UI_VERIFICATION_STEP_PATH = path.join(
|
||||||
|
__dirname, '..', 'gsd-core', 'workflows', 'verify-work', 'steps', 'automated-ui-verification.md'
|
||||||
|
);
|
||||||
|
|
||||||
describe('Playwright-MCP UI verification integration', () => {
|
describe('Playwright-MCP UI verification integration', () => {
|
||||||
test('verify-work.md mentions automated UI verification', () => {
|
test('verify-work.md mentions automated UI verification', () => {
|
||||||
const content = fs.readFileSync(
|
const content = fs.readFileSync(AUTOMATED_UI_VERIFICATION_STEP_PATH, 'utf-8');
|
||||||
path.join(__dirname, '..', 'gsd-core', 'workflows', 'verify-work.md'), 'utf-8'
|
|
||||||
);
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
content.toLowerCase().includes('playwright') || content.includes('automated') && content.includes('UI'),
|
content.toLowerCase().includes('playwright') || content.includes('automated') && content.includes('UI'),
|
||||||
'verify-work.md should mention automated UI verification option'
|
'verify-work.md (or its extracted verify-work/steps/automated-ui-verification.md) should mention automated UI verification option'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -39,9 +50,7 @@ describe('Playwright-MCP UI verification integration', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('automated verification is optional/conditional (falls back to manual)', () => {
|
test('automated verification is optional/conditional (falls back to manual)', () => {
|
||||||
const verifyContent = fs.readFileSync(
|
const verifyContent = fs.readFileSync(AUTOMATED_UI_VERIFICATION_STEP_PATH, 'utf-8');
|
||||||
path.join(__dirname, '..', 'gsd-core', 'workflows', 'verify-work.md'), 'utf-8'
|
|
||||||
);
|
|
||||||
// Must include a fallback / "if available" conditional
|
// Must include a fallback / "if available" conditional
|
||||||
const hasConditional =
|
const hasConditional =
|
||||||
verifyContent.includes('if available') ||
|
verifyContent.includes('if available') ||
|
||||||
|
|||||||
@@ -14,6 +14,16 @@ const assert = require('node:assert/strict');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
|
// #2994 fragmentization moved the --forensic-gated forensic_audit step out of
|
||||||
|
// progress.md into gsd-core/workflows/progress/steps/forensic-audit.md behind
|
||||||
|
// a section marker. Tests that need the step BODY read that step file
|
||||||
|
// directly (it is the sole remaining source of the step's content); the
|
||||||
|
// step-presence check below is the only one that must also confirm the host
|
||||||
|
// still wires up the marker that reads it.
|
||||||
|
const FORENSIC_AUDIT_STEP_PATH = path.join(
|
||||||
|
__dirname, '..', 'gsd-core', 'workflows', 'progress', 'steps', 'forensic-audit.md'
|
||||||
|
);
|
||||||
|
|
||||||
describe('#2189: progress --forensic flag', () => {
|
describe('#2189: progress --forensic flag', () => {
|
||||||
test('progress command argument-hint includes --forensic', () => {
|
test('progress command argument-hint includes --forensic', () => {
|
||||||
const command = fs.readFileSync(
|
const command = fs.readFileSync(
|
||||||
@@ -26,20 +36,19 @@ describe('#2189: progress --forensic flag', () => {
|
|||||||
const workflow = fs.readFileSync(
|
const workflow = fs.readFileSync(
|
||||||
path.join(__dirname, '..', 'gsd-core', 'workflows', 'progress.md'), 'utf8'
|
path.join(__dirname, '..', 'gsd-core', 'workflows', 'progress.md'), 'utf8'
|
||||||
);
|
);
|
||||||
|
const step = fs.readFileSync(FORENSIC_AUDIT_STEP_PATH, 'utf8');
|
||||||
assert.ok(
|
assert.ok(
|
||||||
workflow.includes('<step name="forensic_audit">'),
|
workflow.includes('id="forensic-audit"'),
|
||||||
'workflow should have a forensic_audit step'
|
'progress.md must wire up the forensic-audit section marker'
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
step.includes('<step name="forensic_audit">'),
|
||||||
|
'progress/steps/forensic-audit.md should have a forensic_audit step'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('forensic_audit step is only triggered when --forensic is present', () => {
|
test('forensic_audit step is only triggered when --forensic is present', () => {
|
||||||
const workflow = fs.readFileSync(
|
const forensicStep = fs.readFileSync(FORENSIC_AUDIT_STEP_PATH, 'utf8');
|
||||||
path.join(__dirname, '..', 'gsd-core', 'workflows', 'progress.md'), 'utf8'
|
|
||||||
);
|
|
||||||
const forensicStep = workflow.slice(
|
|
||||||
workflow.indexOf('<step name="forensic_audit">'),
|
|
||||||
workflow.indexOf('</step>', workflow.indexOf('<step name="forensic_audit">'))
|
|
||||||
);
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
forensicStep.includes('--forensic'),
|
forensicStep.includes('--forensic'),
|
||||||
'forensic_audit step should be gated on --forensic flag'
|
'forensic_audit step should be gated on --forensic flag'
|
||||||
@@ -51,13 +60,7 @@ describe('#2189: progress --forensic flag', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('forensic_audit step includes all 6 checks', () => {
|
test('forensic_audit step includes all 6 checks', () => {
|
||||||
const workflow = fs.readFileSync(
|
const forensicStep = fs.readFileSync(FORENSIC_AUDIT_STEP_PATH, 'utf8');
|
||||||
path.join(__dirname, '..', 'gsd-core', 'workflows', 'progress.md'), 'utf8'
|
|
||||||
);
|
|
||||||
const forensicStep = workflow.slice(
|
|
||||||
workflow.indexOf('<step name="forensic_audit">'),
|
|
||||||
workflow.indexOf('</step>', workflow.indexOf('<step name="forensic_audit">'))
|
|
||||||
);
|
|
||||||
// Check 1: STATE vs artifact consistency
|
// Check 1: STATE vs artifact consistency
|
||||||
assert.ok(
|
assert.ok(
|
||||||
forensicStep.includes('STATE') && (forensicStep.includes('artifact') || forensicStep.includes('consistent')),
|
forensicStep.includes('STATE') && (forensicStep.includes('artifact') || forensicStep.includes('consistent')),
|
||||||
@@ -91,13 +94,7 @@ describe('#2189: progress --forensic flag', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('forensic_audit step produces a CLEAN or INTEGRITY ISSUE(S) FOUND verdict', () => {
|
test('forensic_audit step produces a CLEAN or INTEGRITY ISSUE(S) FOUND verdict', () => {
|
||||||
const workflow = fs.readFileSync(
|
const forensicStep = fs.readFileSync(FORENSIC_AUDIT_STEP_PATH, 'utf8');
|
||||||
path.join(__dirname, '..', 'gsd-core', 'workflows', 'progress.md'), 'utf8'
|
|
||||||
);
|
|
||||||
const forensicStep = workflow.slice(
|
|
||||||
workflow.indexOf('<step name="forensic_audit">'),
|
|
||||||
workflow.indexOf('</step>', workflow.indexOf('<step name="forensic_audit">'))
|
|
||||||
);
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
forensicStep.includes('CLEAN'),
|
forensicStep.includes('CLEAN'),
|
||||||
'forensic step should produce a CLEAN verdict when all checks pass'
|
'forensic step should produce a CLEAN verdict when all checks pass'
|
||||||
@@ -109,14 +106,8 @@ describe('#2189: progress --forensic flag', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('forensic_audit step does not change default progress behavior', () => {
|
test('forensic_audit step does not change default progress behavior', () => {
|
||||||
const workflow = fs.readFileSync(
|
|
||||||
path.join(__dirname, '..', 'gsd-core', 'workflows', 'progress.md'), 'utf8'
|
|
||||||
);
|
|
||||||
// The forensic step must explicitly say default behavior is unchanged
|
// The forensic step must explicitly say default behavior is unchanged
|
||||||
const forensicStep = workflow.slice(
|
const forensicStep = fs.readFileSync(FORENSIC_AUDIT_STEP_PATH, 'utf8');
|
||||||
workflow.indexOf('<step name="forensic_audit">'),
|
|
||||||
workflow.indexOf('</step>', workflow.indexOf('<step name="forensic_audit">'))
|
|
||||||
);
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
forensicStep.includes('unchanged') || forensicStep.includes('standard report'),
|
forensicStep.includes('unchanged') || forensicStep.includes('standard report'),
|
||||||
'forensic step should clarify that default behavior is unchanged'
|
'forensic step should clarify that default behavior is unchanged'
|
||||||
|
|||||||
@@ -7,6 +7,27 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const WORKFLOW = path.join(__dirname, '..', 'gsd-core', 'workflows', 'progress.md');
|
const WORKFLOW = path.join(__dirname, '..', 'gsd-core', 'workflows', 'progress.md');
|
||||||
|
const PROGRESS_STEPS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows', 'progress', 'steps');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* progress.md was fragmented (#2994) into gsd-core/workflows/progress/steps/*.md.
|
||||||
|
* The mvp_display step (and its MVP-mode display contract) now lives in
|
||||||
|
* progress/steps/mvp-display.md, only read at all when state:phase-mvp-mode is
|
||||||
|
* true. Read host + every step file combined so this contract guard keeps
|
||||||
|
* seeing the full picture regardless of which file the content physically
|
||||||
|
* lives in.
|
||||||
|
*/
|
||||||
|
function readProgressCombined() {
|
||||||
|
let combined = fs.readFileSync(WORKFLOW, 'utf8');
|
||||||
|
if (fs.existsSync(PROGRESS_STEPS_DIR)) {
|
||||||
|
for (const entry of fs.readdirSync(PROGRESS_STEPS_DIR).sort()) {
|
||||||
|
if (entry.endsWith('.md')) {
|
||||||
|
combined += '\n' + fs.readFileSync(path.join(PROGRESS_STEPS_DIR, entry), 'utf8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
|
||||||
function parseProgressContract(content) {
|
function parseProgressContract(content) {
|
||||||
const lines = content.split(/\r?\n/);
|
const lines = content.split(/\r?\n/);
|
||||||
@@ -24,7 +45,7 @@ function parseProgressContract(content) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('progress — MVP mode display', () => {
|
describe('progress — MVP mode display', () => {
|
||||||
const contract = parseProgressContract(fs.readFileSync(WORKFLOW, 'utf-8'));
|
const contract = parseProgressContract(readProgressCombined());
|
||||||
|
|
||||||
test('workflow declares MVP_MODE branch', () => {
|
test('workflow declares MVP_MODE branch', () => {
|
||||||
assert.ok(contract.hasMvpModeVariable, 'must declare MVP_MODE');
|
assert.ok(contract.hasMvpModeVariable, 'must declare MVP_MODE');
|
||||||
|
|||||||
@@ -77,13 +77,39 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const QUICK_MD = path.join(__dirname, '..', 'gsd-core', 'workflows', 'quick.md');
|
const QUICK_MD = path.join(__dirname, '..', 'gsd-core', 'workflows', 'quick.md');
|
||||||
|
const REPO_ROOT_FOR_SECTIONS = path.join(__dirname, '..');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #2994 fragmentization moved Steps 4.5/4.75/5.5/5.6/6.5 out of quick.md into
|
||||||
|
* gsd-core/workflows/quick/steps/*.md behind section markers. A plain
|
||||||
|
* `content.indexOf('Step 5.6')` against the host now matches an unrelated
|
||||||
|
* later CROSS-REFERENCE to "Step 5.6" (a mention inside the worktree-branch
|
||||||
|
* guard prose), not the real heading — silently vacuous, since that
|
||||||
|
* mis-anchored slice still happens to contain USE_WORKTREES/commit_docs/
|
||||||
|
* PLAN.md/git add/git commit substrings from unrelated surrounding text.
|
||||||
|
* Expand each `<!-- gsd:section id="X" ... -->...<!-- /gsd:section -->`
|
||||||
|
* marker IN PLACE with its step file's content so Step 5.5/5.6/6/6.5 land
|
||||||
|
* back at their original logical positions and ordering/slicing assertions
|
||||||
|
* see the real step bodies again.
|
||||||
|
*/
|
||||||
|
function expandWorkflowSections(workflowPath) {
|
||||||
|
const raw = fs.readFileSync(workflowPath, 'utf8');
|
||||||
|
const markerRe = /<!-- gsd:section id="[\w-]+" when="[^"]*" -->[\s\S]*?<!-- \/gsd:section -->/g;
|
||||||
|
return raw.replace(markerRe, (block) => {
|
||||||
|
const pathMatch = block.match(/`([^`]+\.md)`/);
|
||||||
|
if (!pathMatch) return block;
|
||||||
|
const stepPath = path.join(REPO_ROOT_FOR_SECTIONS, pathMatch[1]);
|
||||||
|
if (!fs.existsSync(stepPath)) return block;
|
||||||
|
return fs.readFileSync(stepPath, 'utf8');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
describe('quick.md pre-dispatch PLAN.md commit (#2432)', () => {
|
describe('quick.md pre-dispatch PLAN.md commit (#2432)', () => {
|
||||||
let content;
|
let content;
|
||||||
|
|
||||||
test('quick.md exists', () => {
|
test('quick.md exists', () => {
|
||||||
assert.ok(fs.existsSync(QUICK_MD), 'gsd-core/workflows/quick.md must exist');
|
assert.ok(fs.existsSync(QUICK_MD), 'gsd-core/workflows/quick.md must exist');
|
||||||
content = fs.readFileSync(QUICK_MD, 'utf-8');
|
content = expandWorkflowSections(QUICK_MD);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Step 5.6 exists between Step 5.5 and Step 6', () => {
|
test('Step 5.6 exists between Step 5.5 and Step 6', () => {
|
||||||
|
|||||||
@@ -16,6 +16,27 @@ const { runGsdTools, createTempProject, cleanup, absPlanningPath } = require('./
|
|||||||
|
|
||||||
const COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'gsd');
|
const COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'gsd');
|
||||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||||
|
const REPO_ROOT_FOR_SECTIONS = path.join(__dirname, '..');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #2994 fragmentization moved Steps 4.5/4.75/5.5/5.6/6.5 out of quick.md into
|
||||||
|
* gsd-core/workflows/quick/steps/*.md behind section markers. Position-based
|
||||||
|
* slicing here (`indexOf('Step 4.75')` .. `indexOf('Step 5:')`, etc.) needs
|
||||||
|
* those step headings back at their original logical position, not appended
|
||||||
|
* at the end — so expand each `<!-- gsd:section id="X" ... -->...
|
||||||
|
* <!-- /gsd:section -->` marker IN PLACE with its step file's content.
|
||||||
|
*/
|
||||||
|
function expandWorkflowSections(workflowPath) {
|
||||||
|
const raw = fs.readFileSync(workflowPath, 'utf8');
|
||||||
|
const markerRe = /<!-- gsd:section id="[\w-]+" when="[^"]*" -->[\s\S]*?<!-- \/gsd:section -->/g;
|
||||||
|
return raw.replace(markerRe, (block) => {
|
||||||
|
const pathMatch = block.match(/`([^`]+\.md)`/);
|
||||||
|
if (!pathMatch) return block;
|
||||||
|
const stepPath = path.join(REPO_ROOT_FOR_SECTIONS, pathMatch[1]);
|
||||||
|
if (!fs.existsSync(stepPath)) return block;
|
||||||
|
return fs.readFileSync(stepPath, 'utf8');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// Command frontmatter: --research flag advertised
|
// Command frontmatter: --research flag advertised
|
||||||
@@ -67,11 +88,11 @@ describe('quick workflow: research step', () => {
|
|||||||
|
|
||||||
test('workflow file exists', () => {
|
test('workflow file exists', () => {
|
||||||
assert.ok(fs.existsSync(workflowPath), 'workflows/quick.md should exist');
|
assert.ok(fs.existsSync(workflowPath), 'workflows/quick.md should exist');
|
||||||
content = fs.readFileSync(workflowPath, 'utf-8');
|
content = expandWorkflowSections(workflowPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('purpose mentions --research flag', () => {
|
test('purpose mentions --research flag', () => {
|
||||||
content = fs.readFileSync(workflowPath, 'utf-8');
|
content = expandWorkflowSections(workflowPath);
|
||||||
const purposeMatch = content.match(/<purpose>([\s\S]*?)<\/purpose>/);
|
const purposeMatch = content.match(/<purpose>([\s\S]*?)<\/purpose>/);
|
||||||
assert.ok(purposeMatch, 'should have <purpose> section');
|
assert.ok(purposeMatch, 'should have <purpose> section');
|
||||||
assert.ok(
|
assert.ok(
|
||||||
@@ -81,7 +102,7 @@ describe('quick workflow: research step', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('step 1 parses --research flag', () => {
|
test('step 1 parses --research flag', () => {
|
||||||
content = fs.readFileSync(workflowPath, 'utf-8');
|
content = expandWorkflowSections(workflowPath);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
content.includes('$RESEARCH_MODE'),
|
content.includes('$RESEARCH_MODE'),
|
||||||
'workflow should reference $RESEARCH_MODE variable'
|
'workflow should reference $RESEARCH_MODE variable'
|
||||||
@@ -89,7 +110,7 @@ describe('quick workflow: research step', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('step 4.75 research phase exists', () => {
|
test('step 4.75 research phase exists', () => {
|
||||||
content = fs.readFileSync(workflowPath, 'utf-8');
|
content = expandWorkflowSections(workflowPath);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
content.includes('Step 4.75'),
|
content.includes('Step 4.75'),
|
||||||
'workflow should contain Step 4.75 (research phase)'
|
'workflow should contain Step 4.75 (research phase)'
|
||||||
@@ -97,7 +118,7 @@ describe('quick workflow: research step', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('research step spawns gsd-phase-researcher', () => {
|
test('research step spawns gsd-phase-researcher', () => {
|
||||||
content = fs.readFileSync(workflowPath, 'utf-8');
|
content = expandWorkflowSections(workflowPath);
|
||||||
const researchSection = content.substring(
|
const researchSection = content.substring(
|
||||||
content.indexOf('Step 4.75'),
|
content.indexOf('Step 4.75'),
|
||||||
content.indexOf('Step 5:')
|
content.indexOf('Step 5:')
|
||||||
@@ -109,7 +130,7 @@ describe('quick workflow: research step', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('research step writes RESEARCH.md', () => {
|
test('research step writes RESEARCH.md', () => {
|
||||||
content = fs.readFileSync(workflowPath, 'utf-8');
|
content = expandWorkflowSections(workflowPath);
|
||||||
const researchSection = content.substring(
|
const researchSection = content.substring(
|
||||||
content.indexOf('Step 4.75'),
|
content.indexOf('Step 4.75'),
|
||||||
content.indexOf('Step 5:')
|
content.indexOf('Step 5:')
|
||||||
@@ -121,7 +142,7 @@ describe('quick workflow: research step', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('planner context includes RESEARCH.md when research mode', () => {
|
test('planner context includes RESEARCH.md when research mode', () => {
|
||||||
content = fs.readFileSync(workflowPath, 'utf-8');
|
content = expandWorkflowSections(workflowPath);
|
||||||
const plannerSection = content.substring(
|
const plannerSection = content.substring(
|
||||||
content.indexOf('Step 5: Spawn planner'),
|
content.indexOf('Step 5: Spawn planner'),
|
||||||
content.indexOf('Step 5.5')
|
content.indexOf('Step 5.5')
|
||||||
@@ -133,7 +154,7 @@ describe('quick workflow: research step', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('file commit list includes RESEARCH.md', () => {
|
test('file commit list includes RESEARCH.md', () => {
|
||||||
content = fs.readFileSync(workflowPath, 'utf-8');
|
content = expandWorkflowSections(workflowPath);
|
||||||
const commitSection = content.substring(
|
const commitSection = content.substring(
|
||||||
content.indexOf('Step 8:'),
|
content.indexOf('Step 8:'),
|
||||||
content.indexOf('</process>')
|
content.indexOf('</process>')
|
||||||
@@ -145,7 +166,7 @@ describe('quick workflow: research step', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('success criteria includes research items', () => {
|
test('success criteria includes research items', () => {
|
||||||
content = fs.readFileSync(workflowPath, 'utf-8');
|
content = expandWorkflowSections(workflowPath);
|
||||||
const criteriaMatch = content.match(/<success_criteria>([\s\S]*?)<\/success_criteria>/);
|
const criteriaMatch = content.match(/<success_criteria>([\s\S]*?)<\/success_criteria>/);
|
||||||
assert.ok(criteriaMatch, 'should have <success_criteria> section');
|
assert.ok(criteriaMatch, 'should have <success_criteria> section');
|
||||||
assert.ok(
|
assert.ok(
|
||||||
|
|||||||
163
tests/resolver-hoist-guard.test.cjs
Normal file
163
tests/resolver-hoist-guard.test.cjs
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
// allow-test-rule: source-text-is-the-product see #2994 — G5 below asserts that an
|
||||||
|
// extracted step file's raw text does not contain the literal resolver call
|
||||||
|
// for the SAME fact that gates its own admission (a circular, self-disabling
|
||||||
|
// section). There is no typed/structural representation of "this step body
|
||||||
|
// re-derives its own gate" other than the text itself; G2 asserts a specific
|
||||||
|
// resolver variable's source line is retained verbatim because a STILL-LIVE
|
||||||
|
// consumer elsewhere in the same host file reads it literally.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolver-hoist guard — #2994 (epic #1671 Phase 6.3) matrix §G
|
||||||
|
* (`.gsd/phase/chore-2994-fragment-model-workflows/50-test-matrix.md`):
|
||||||
|
* "did the hoist break the ungated path?" and "did the hoist actually
|
||||||
|
* happen?" — a gated section whose own extracted body STILL resolves its own
|
||||||
|
* gating fact is a circular, self-disabling section (the `plan-phase.md:125-158`
|
||||||
|
* precedent named in `40-design.md`'s negative space).
|
||||||
|
*
|
||||||
|
* G5 generalizes G1-G4 into ONE data-driven assertion over the SHIPPED
|
||||||
|
* `gsd-core/workflows/section-manifest.json`, so a FUTURE extraction cannot
|
||||||
|
* reintroduce the class without this test catching it: for every gated
|
||||||
|
* section whose `when=` atom has a documented, config-key-backed (or
|
||||||
|
* detector-backed) resolution — per `src/section-manifest.cts`'s
|
||||||
|
* `InvocationFacts` JSDoc and `src/init.cts`'s `detect*`/`buildSectionManifestField`
|
||||||
|
* comments, both read directly rather than re-guessed — its own step file
|
||||||
|
* must not contain the literal resolver call for that SAME fact. Where no
|
||||||
|
* generic rule is derivable (e.g. `state:workstream-active`'s env/pointer
|
||||||
|
* resolution, `state:is-monorepo`'s glob-detector, `state:next-channel`'s
|
||||||
|
* flag-only resolution), the atom is simply absent from the map below and
|
||||||
|
* skipped — per the matrix's own "where a generic rule is impossible, encode
|
||||||
|
* the specific known pairs" instruction.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { describe, test } = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const ROOT = path.join(__dirname, '..');
|
||||||
|
const SHIPPED_MANIFEST_PATH = path.join(ROOT, 'gsd-core', 'workflows', 'section-manifest.json');
|
||||||
|
|
||||||
|
function readShippedManifest() {
|
||||||
|
return JSON.parse(fs.readFileSync(SHIPPED_MANIFEST_PATH, 'utf8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenated content of every ```-fenced code block in `content` (fence
|
||||||
|
* markers themselves excluded). A LIVE resolver call (an actual `gsd_run
|
||||||
|
* query ...`/`gsd_run loop ...` invocation the runtime would execute) only
|
||||||
|
* ever appears inside a fenced bash block; a PROSE mention of the same
|
||||||
|
* config key or command name (e.g. documenting why a resolver was
|
||||||
|
* deliberately removed, or naming a config key in a sentence) appears in
|
||||||
|
* single-backtick inline code within ordinary prose, never inside a fence.
|
||||||
|
* Restricting the forbidden-substring search below to fenced content is what
|
||||||
|
* lets G5 tell "this step file still executes the resolver" apart from
|
||||||
|
* "this step file's prose merely talks about the resolver" — both
|
||||||
|
* `mvp-display.md` and `reviewer-instances-note-1.md` do the latter
|
||||||
|
* (explaining, in prose, why they do NOT re-resolve), which a plain
|
||||||
|
* whole-file substring search cannot distinguish from the former.
|
||||||
|
*/
|
||||||
|
function fencedCodeOnly(content) {
|
||||||
|
const parts = content.split('```');
|
||||||
|
// parts[0] is text before the first fence, parts[1] is inside the first
|
||||||
|
// fence, parts[2] is between fence 1's close and fence 2's open, etc. —
|
||||||
|
// odd indices are fence interiors (matches this repo's other fence-aware
|
||||||
|
// helpers' `%2===1` convention).
|
||||||
|
return parts.filter((_, i) => i % 2 === 1).join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── G5: no gated section's own step file resolves its own gating fact ────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Known {when atom -> forbidden literal substring(s)} pairs: the exact
|
||||||
|
* resolver invocation each atom's fact is documented as being hoisted FROM
|
||||||
|
* (`src/init.cts`'s `detectFallowConfig`/`detectGitCreateTag`/`readConfigJsonBoolean`
|
||||||
|
* call sites, and `src/section-manifest.cts`'s matching `InvocationFacts`
|
||||||
|
* JSDoc for each field). A step file gated on the atom must never contain
|
||||||
|
* that substring — if it does, the section re-derives the same fact its own
|
||||||
|
* marker already gates on, instead of consuming the init-resolved value.
|
||||||
|
*/
|
||||||
|
const ATOM_FORBIDDEN_RESOLVER_SUBSTRINGS = Object.freeze({
|
||||||
|
'state:fallow-enabled': ['code_quality.fallow.enabled'],
|
||||||
|
'state:git-create-tag': ['git.create_tag'],
|
||||||
|
'state:chunked-mode': ['workflow.plan_chunked'],
|
||||||
|
'state:worktrees-enabled': ['workflow.use_worktrees'],
|
||||||
|
'state:reviewer-instances-configured': ['review.reviewer_instances'],
|
||||||
|
'state:phase-mvp-mode': ['phase.mvp-mode'],
|
||||||
|
// #2994: detectUiPhaseActive (src/init.cts) resolves this fact via
|
||||||
|
// `resolveLoopHooks({point:'plan:pre', ...})` — the CLI-facing equivalent
|
||||||
|
// of that SAME resolution is `gsd_run loop render-hooks plan:pre`.
|
||||||
|
'state:ui-phase-active': ['render-hooks plan:pre'],
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('no gated section resolves its own gating fact (matrix row G5, data-driven over the shipped manifest)', () => {
|
||||||
|
const manifest = readShippedManifest();
|
||||||
|
|
||||||
|
for (const [workflow, sections] of Object.entries(manifest.workflows)) {
|
||||||
|
for (const section of sections) {
|
||||||
|
const forbidden = ATOM_FORBIDDEN_RESOLVER_SUBSTRINGS[section.when];
|
||||||
|
if (!forbidden) continue; // no generic rule for this atom — see module doc comment.
|
||||||
|
|
||||||
|
test(`${workflow}.md § "${section.id}" (when="${section.when}") does not re-resolve its own gate`, () => {
|
||||||
|
const stepFilePath = path.join(ROOT, section.read);
|
||||||
|
const stepContent = fencedCodeOnly(fs.readFileSync(stepFilePath, 'utf8'));
|
||||||
|
for (const token of forbidden) {
|
||||||
|
assert.ok(
|
||||||
|
!stepContent.includes(token),
|
||||||
|
`${section.read} (gated on "${section.when}") must not itself call the resolver for that fact ` +
|
||||||
|
`(found forbidden substring "${token}") — this is a circular, self-disabling section: the init-resolved ` +
|
||||||
|
`fact already gates this file's own inclusion, so re-deriving it inside the file is dead weight at best ` +
|
||||||
|
`and a divergent second resolver at worst`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test('the forbidden-substring map itself covers at least one atom actually present in the shipped manifest (anti-vacuity)', () => {
|
||||||
|
const usedAtoms = new Set();
|
||||||
|
for (const sections of Object.values(manifest.workflows)) {
|
||||||
|
for (const section of sections) usedAtoms.add(section.when);
|
||||||
|
}
|
||||||
|
const coveredAndUsed = Object.keys(ATOM_FORBIDDEN_RESOLVER_SUBSTRINGS).filter((atom) => usedAtoms.has(atom));
|
||||||
|
assert.ok(coveredAndUsed.length > 0, 'the forbidden-substring map must exercise at least one atom the shipped manifest actually gates a section on');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── G2: autonomous.md's $PLAN_STRATEGY resolver survives the converge hoist
|
||||||
|
// because ungated "local" bullets still consume it literally ─────────────
|
||||||
|
|
||||||
|
describe('autonomous.md retains its $PLAN_STRATEGY resolver — still consumed by ungated content (matrix row G2)', () => {
|
||||||
|
const filePath = path.join(ROOT, 'gsd-core', 'workflows', 'autonomous.md');
|
||||||
|
const content = fs.readFileSync(filePath, 'utf8');
|
||||||
|
|
||||||
|
test('the PLAN_STRATEGY resolver (local/converge assignment) is present verbatim', () => {
|
||||||
|
assert.ok(content.includes('PLAN_STRATEGY="local"'), 'the default-local assignment must survive the converge-hoist');
|
||||||
|
assert.ok(content.includes('PLAN_STRATEGY="converge"'), 'the converge-branch assignment must survive the converge-hoist');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('an ungated "PLAN_STRATEGY=local" consumer exists OUTSIDE any converge-* gated section', () => {
|
||||||
|
const { parseWorkflowSections } = require('../gsd-core/bin/lib/workflow-fragments.cjs');
|
||||||
|
const sections = parseWorkflowSections(content, 'gsd-core/workflows/autonomous.md');
|
||||||
|
|
||||||
|
const CONSUMER_TEXT = 'If `PLAN_STRATEGY=local`, run the regular planner:';
|
||||||
|
assert.ok(content.includes(CONSUMER_TEXT), 'the ungated local-strategy consumer text must exist in the host file');
|
||||||
|
|
||||||
|
const gatedConvergeSections = sections.filter((s) => s.explicit && s.when === 'state:plan-strategy-converge');
|
||||||
|
assert.ok(gatedConvergeSections.length > 0, 'sanity: autonomous.md must have at least one state:plan-strategy-converge gated section');
|
||||||
|
for (const gated of gatedConvergeSections) {
|
||||||
|
assert.ok(
|
||||||
|
!gated.body.includes(CONSUMER_TEXT),
|
||||||
|
`the ungated PLAN_STRATEGY=local consumer must not live inside gated section "${gated.id}" — deleting that section must never remove the local-planning path`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deleting the resolver would break this ungated consumer — assert the
|
||||||
|
// resolver assignment and the consumer are BOTH present outside every
|
||||||
|
// converge-gated section (i.e. in the always-composed remainder).
|
||||||
|
const gapText = sections.filter((s) => !s.explicit).map((s) => s.body).join('');
|
||||||
|
assert.ok(gapText.includes('PLAN_STRATEGY="local"'));
|
||||||
|
assert.ok(gapText.includes(CONSUMER_TEXT));
|
||||||
|
});
|
||||||
|
});
|
||||||
310
tests/section-manifest-init-facts.test.cjs
Normal file
310
tests/section-manifest-init-facts.test.cjs
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prod-shape integration tests for `src/init.cts`'s `buildSectionManifestField`
|
||||||
|
* seam (compiled into `gsd-core/bin/lib/init.cjs`) — #2994 (epic #1671 Phase
|
||||||
|
* 6.3), `.gsd/phase/chore-2994-fragment-model-workflows/50-test-matrix.md`
|
||||||
|
* sections B (compound-fact folding), D (flag value shapes), and E (the
|
||||||
|
* `null` vs `[]` distinction).
|
||||||
|
*
|
||||||
|
* Every test drives the REAL CLI (`runGsdTools`, spawns `gsd-tools.cjs`) —
|
||||||
|
* never a direct `require()` of `cmdInit*`, since `buildSectionManifestField`
|
||||||
|
* itself is not exported and the whole point of this suite is proving the
|
||||||
|
* flag/config plumbing that only exists at the CLI seam (`init-command-router.cjs`'s
|
||||||
|
* `parseNamedArgs` calls) actually reaches `section_manifest`.
|
||||||
|
*
|
||||||
|
* Group E uses the `GSD_SECTION_MANIFEST` env override
|
||||||
|
* (`gsd-core/bin/lib/init.cjs`'s `_sectionManifestCandidatePath`) to point the
|
||||||
|
* CLI at a temp fixture manifest instead of the shipped
|
||||||
|
* `gsd-core/workflows/section-manifest.json` — this is the ONLY way to
|
||||||
|
* exercise "workflow absent from the manifest" today, because every
|
||||||
|
* `cmdInit*` this repo currently wires (#2994's own completion) already has
|
||||||
|
* an explicit key in the shipped artifact; Groups B and D deliberately do NOT
|
||||||
|
* override it, driving the shipped manifest directly per the matrix's own
|
||||||
|
* "drive from the shipped artifact wherever possible" rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { describe, test, beforeEach, afterEach } = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const { runGsdTools, cleanup, createTempDir, createTempProject } = require('./helpers.cjs');
|
||||||
|
const { seedPhase } = require('./fixtures/index.cjs');
|
||||||
|
|
||||||
|
function writeConfig(tmpDir, config) {
|
||||||
|
fs.mkdirSync(path.join(tmpDir, '.planning'), { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(tmpDir, '.planning', 'config.json'), JSON.stringify(config, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
function runInitJson(argv, cwd, env = {}) {
|
||||||
|
const result = runGsdTools(argv, cwd, env);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
return JSON.parse(result.output);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Group E: the null vs [] distinction (matrix rows E1/E2/E3/E5/E6) ──────
|
||||||
|
|
||||||
|
describe('section_manifest: null (degraded) vs [] (computed, nothing applicable) — matrix §E', () => {
|
||||||
|
let tmpDir;
|
||||||
|
let manifestDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject('section-manifest-e-');
|
||||||
|
manifestDir = createTempDir('section-manifest-e-manifest-');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
cleanup(manifestDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a manifest with no "quick" key degrades section_manifest to null (row E1)', () => {
|
||||||
|
const manifestPath = path.join(manifestDir, 'manifest.json');
|
||||||
|
fs.writeFileSync(manifestPath, JSON.stringify({ workflows: {} }));
|
||||||
|
|
||||||
|
const output = runInitJson(['init', 'quick', 'desc'], tmpDir, { GSD_SECTION_MANIFEST: manifestPath });
|
||||||
|
assert.equal(output.section_manifest, null, 'an absent workflow key must degrade to null, never []');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a manifest with an explicit empty "quick" section list yields a computed [] (row E2)', () => {
|
||||||
|
const manifestPath = path.join(manifestDir, 'manifest.json');
|
||||||
|
fs.writeFileSync(manifestPath, JSON.stringify({ workflows: { quick: [] } }));
|
||||||
|
|
||||||
|
const output = runInitJson(['init', 'quick', 'desc'], tmpDir, { GSD_SECTION_MANIFEST: manifestPath });
|
||||||
|
assert.notEqual(output.section_manifest, null, 'a present (even empty) workflow key must NOT degrade to null');
|
||||||
|
assert.deepEqual(output.section_manifest.included, []);
|
||||||
|
assert.deepEqual(output.section_manifest.excluded, []);
|
||||||
|
assert.deepEqual(output.section_manifest.read, []);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('null and the computed [] shape are DIFFERENT values, not interchangeable (row E3)', () => {
|
||||||
|
const absentKeyManifest = path.join(manifestDir, 'absent.json');
|
||||||
|
fs.writeFileSync(absentKeyManifest, JSON.stringify({ workflows: {} }));
|
||||||
|
const emptyListManifest = path.join(manifestDir, 'empty.json');
|
||||||
|
fs.writeFileSync(emptyListManifest, JSON.stringify({ workflows: { quick: [] } }));
|
||||||
|
|
||||||
|
const degraded = runInitJson(['init', 'quick', 'desc'], tmpDir, { GSD_SECTION_MANIFEST: absentKeyManifest });
|
||||||
|
const computed = runInitJson(['init', 'quick', 'desc'], tmpDir, { GSD_SECTION_MANIFEST: emptyListManifest });
|
||||||
|
|
||||||
|
assert.equal(degraded.section_manifest, null);
|
||||||
|
assert.notEqual(computed.section_manifest, null);
|
||||||
|
assert.notDeepEqual(degraded.section_manifest, computed.section_manifest, 'null must never compare equal to the computed {included:[],excluded:[],...} shape');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a section list where every when= evaluates false is still the computed [] shape, not null (row E2, second case)', () => {
|
||||||
|
// "Applicable" is a FACTS question, not a shape question: a real section
|
||||||
|
// whose gating flag was simply never passed on this invocation must still
|
||||||
|
// be the computed {included:[],...} object, never collapse to null.
|
||||||
|
const manifestPath = path.join(manifestDir, 'manifest.json');
|
||||||
|
fs.writeFileSync(manifestPath, JSON.stringify({
|
||||||
|
workflows: {
|
||||||
|
quick: [{ id: 'discussion-phase', when: 'flag:--discuss', read: 'gsd-core/workflows/quick/steps/discussion-phase.md' }],
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const output = runInitJson(['init', 'quick', 'desc'], tmpDir, { GSD_SECTION_MANIFEST: manifestPath });
|
||||||
|
assert.notEqual(output.section_manifest, null);
|
||||||
|
assert.deepEqual(output.section_manifest.included, []);
|
||||||
|
assert.deepEqual(output.section_manifest.excluded, ['discussion-phase']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a missing manifest file degrades to null without throwing (row E5)', () => {
|
||||||
|
const neverCreatedPath = path.join(manifestDir, 'does-not-exist.json');
|
||||||
|
assert.equal(fs.existsSync(neverCreatedPath), false, 'sanity: file must not exist');
|
||||||
|
|
||||||
|
const result = runGsdTools(['init', 'quick', 'desc'], tmpDir, { GSD_SECTION_MANIFEST: neverCreatedPath });
|
||||||
|
assert.ok(result.success, `a missing manifest file must not crash the command: ${result.error}`);
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.equal(output.section_manifest, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a malformed-JSON manifest file degrades to null without throwing (row E6)', () => {
|
||||||
|
const manifestPath = path.join(manifestDir, 'malformed.json');
|
||||||
|
fs.writeFileSync(manifestPath, '{ this is not valid JSON');
|
||||||
|
|
||||||
|
const result = runGsdTools(['init', 'quick', 'desc'], tmpDir, { GSD_SECTION_MANIFEST: manifestPath });
|
||||||
|
assert.ok(result.success, `a malformed manifest file must not crash the command: ${result.error}`);
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.equal(output.section_manifest, null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Group B: compound-fact folding lives in init.cts, never the grammar ──
|
||||||
|
|
||||||
|
describe('compound-fact folding at the init seam — matrix §B', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject('section-manifest-b-');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
// B1-B4: cmdInitQuick folds --full into --discuss/--research/--validate BEFORE
|
||||||
|
// buildSectionManifestField evaluates flag:--discuss/flag:--research/flag:--validate.
|
||||||
|
describe('cmdInitQuick folds --full into each mode fact (rows B1-B4)', () => {
|
||||||
|
test('--discuss alone includes discussion-phase (row B1, baseline)', () => {
|
||||||
|
const output = runInitJson(['init', 'quick', 'desc', '--discuss'], tmpDir);
|
||||||
|
assert.ok(output.section_manifest.included.includes('discussion-phase'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('--full alone (no --discuss) STILL includes discussion-phase — the fold this PR exists to prove (row B2)', () => {
|
||||||
|
const output = runInitJson(['init', 'quick', 'desc', '--full'], tmpDir);
|
||||||
|
assert.ok(
|
||||||
|
output.section_manifest.included.includes('discussion-phase'),
|
||||||
|
'--full must imply --discuss for section-gating purposes, folded in cmdInitQuick before evaluation',
|
||||||
|
);
|
||||||
|
assert.ok(output.section_manifest.included.includes('research-phase'), '--full must also imply --research');
|
||||||
|
assert.ok(output.section_manifest.included.includes('plan-checker-loop'), '--full must also imply --validate (plan-checker-loop)');
|
||||||
|
assert.ok(output.section_manifest.included.includes('quick-verification'), '--full must also imply --validate (quick-verification)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('neither --discuss nor --full excludes discussion-phase (row B3)', () => {
|
||||||
|
const output = runInitJson(['init', 'quick', 'desc'], tmpDir);
|
||||||
|
assert.ok(output.section_manifest.excluded.includes('discussion-phase'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('--full AND --discuss together still include discussion-phase exactly once, no double-counting (row B4)', () => {
|
||||||
|
const output = runInitJson(['init', 'quick', 'desc', '--full', '--discuss'], tmpDir);
|
||||||
|
const occurrences = output.section_manifest.included.filter((id) => id === 'discussion-phase');
|
||||||
|
assert.equal(occurrences.length, 1, 'discussion-phase must appear exactly once in included, never duplicated by the fold');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// B5-B6: cmdInitAutonomous folds --converge OR --cross-ai into one plan_strategy_converge fact.
|
||||||
|
describe('cmdInitAutonomous folds --converge OR --cross-ai (rows B5-B6)', () => {
|
||||||
|
test('--converge alone drives state:plan-strategy-converge sections into included (row B5)', () => {
|
||||||
|
const output = runInitJson(['init', 'autonomous', '--converge'], tmpDir);
|
||||||
|
assert.equal(output.plan_strategy_converge, true);
|
||||||
|
assert.ok(output.section_manifest.included.includes('converge-fail-fast'));
|
||||||
|
assert.ok(output.section_manifest.included.includes('converge-banner'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('--cross-ai alone (the documented alias, no --converge) ALSO drives inclusion (row B6)', () => {
|
||||||
|
const output = runInitJson(['init', 'autonomous', '--cross-ai'], tmpDir);
|
||||||
|
assert.equal(output.plan_strategy_converge, true);
|
||||||
|
assert.ok(output.section_manifest.included.includes('converge-fail-fast'));
|
||||||
|
assert.ok(output.section_manifest.included.includes('converge-loop'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('neither flag excludes every converge-* section', () => {
|
||||||
|
const output = runInitJson(['init', 'autonomous'], tmpDir);
|
||||||
|
assert.equal(output.plan_strategy_converge, false);
|
||||||
|
for (const id of ['converge-fail-fast', 'converge-banner', 'converge-dispatch-bg', 'converge-dispatch-inline', 'converge-loop']) {
|
||||||
|
assert.ok(output.section_manifest.excluded.includes(id), `expected "${id}" excluded when neither --converge nor --cross-ai is set`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// B7: cmdInitUpdate folds --next OR --rc into one next_channel fact.
|
||||||
|
describe('cmdInitUpdate folds --next OR --rc (row B7)', () => {
|
||||||
|
test('--rc alone (not --next) drives channel-banner into included', () => {
|
||||||
|
const output = runInitJson(['init', 'update', '--rc'], tmpDir);
|
||||||
|
assert.equal(output.next_channel, true);
|
||||||
|
assert.ok(output.section_manifest.included.includes('channel-banner'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('--next alone also drives channel-banner into included (the other half of the OR)', () => {
|
||||||
|
const output = runInitJson(['init', 'update', '--next'], tmpDir);
|
||||||
|
assert.equal(output.next_channel, true);
|
||||||
|
assert.ok(output.section_manifest.included.includes('channel-banner'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('neither flag excludes channel-banner', () => {
|
||||||
|
const output = runInitJson(['init', 'update'], tmpDir);
|
||||||
|
assert.equal(output.next_channel, false);
|
||||||
|
assert.ok(output.section_manifest.excluded.includes('channel-banner'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// B8: cmdInitDiscussPhaseAssumptions folds --auto OR consolidated auto-mode config.
|
||||||
|
describe('cmdInitDiscussPhaseAssumptions folds --auto flag OR config auto-mode (row B8)', () => {
|
||||||
|
test('no --auto flag, but config workflow._auto_chain_active=true, still includes auto-advance-dispatch', () => {
|
||||||
|
writeConfig(tmpDir, { workflow: { _auto_chain_active: true } });
|
||||||
|
const output = runInitJson(['init', 'discuss-phase-assumptions', '99'], tmpDir);
|
||||||
|
assert.ok(output.section_manifest.included.includes('auto-advance-dispatch'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('no --auto flag, but config workflow.auto_advance=true, still includes auto-advance-dispatch (the other config source)', () => {
|
||||||
|
writeConfig(tmpDir, { workflow: { auto_advance: true } });
|
||||||
|
const output = runInitJson(['init', 'discuss-phase-assumptions', '99'], tmpDir);
|
||||||
|
assert.ok(output.section_manifest.included.includes('auto-advance-dispatch'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('--auto flag present with no config also includes auto-advance-dispatch', () => {
|
||||||
|
const output = runInitJson(['init', 'discuss-phase-assumptions', '99', '--auto'], tmpDir);
|
||||||
|
assert.ok(output.section_manifest.included.includes('auto-advance-dispatch'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('neither --auto nor either config key excludes auto-advance-dispatch', () => {
|
||||||
|
const output = runInitJson(['init', 'discuss-phase-assumptions', '99'], tmpDir);
|
||||||
|
assert.ok(output.section_manifest.excluded.includes('auto-advance-dispatch'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Group D: flag value shapes — token presence, not value truthiness ────
|
||||||
|
|
||||||
|
describe('flag value shapes drive section_manifest by truthiness, not semantic value — matrix §D', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject('section-manifest-d-');
|
||||||
|
seedPhase(tmpDir, '01-sample', { '01-CONTEXT.md': '# Phase Context\n' });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('absent boolean flag (--reviews never passed) does not mark reviews-prerequisite present (row D1)', () => {
|
||||||
|
const output = runInitJson(['init', 'plan-phase', '1'], tmpDir);
|
||||||
|
assert.ok(output.section_manifest.excluded.includes('reviews-prerequisite'));
|
||||||
|
assert.ok(!output.section_manifest.included.includes('reviews-prerequisite'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('empty-string value flag (--prd "") excludes prd-express-gate — an empty value is not a branch selection (row D2)', () => {
|
||||||
|
const output = runInitJson(['init', 'plan-phase', '1', '--prd', ''], tmpDir);
|
||||||
|
assert.ok(output.section_manifest.excluded.includes('prd-express-gate'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('string-"0" value flag (--prd "0") INCLUDES prd-express-gate — token-presence semantics, not value truthiness (row D3)', () => {
|
||||||
|
const output = runInitJson(['init', 'plan-phase', '1', '--prd', '0'], tmpDir);
|
||||||
|
assert.ok(output.section_manifest.included.includes('prd-express-gate'), '--prd "0" is a non-empty string and must be treated as present');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('whitespace-only value flag (--prd " ") includes prd-express-gate, asserted deliberately (row D4)', () => {
|
||||||
|
const output = runInitJson(['init', 'plan-phase', '1', '--prd', ' '], tmpDir);
|
||||||
|
assert.ok(output.section_manifest.included.includes('prd-express-gate'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('duplicate flag tokens (--prd a --prd b) resolve to a single membership, no crash (row D5)', () => {
|
||||||
|
const result = runGsdTools(['init', 'plan-phase', '1', '--prd', 'a', '--prd', 'b'], tmpDir);
|
||||||
|
assert.ok(result.success, `duplicate --prd tokens must not crash the command: ${result.error}`);
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.ok(output.section_manifest.included.includes('prd-express-gate'));
|
||||||
|
const occurrences = output.section_manifest.included.filter((id) => id === 'prd-express-gate');
|
||||||
|
assert.equal(occurrences.length, 1, 'must be a single membership, not one entry per duplicate token');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('flag-shaped value (--prd --weird) does not crash and deterministically excludes prd-express-gate (row D6)', () => {
|
||||||
|
// parseNamedArgs treats a token starting with "--" as the NEXT flag, never
|
||||||
|
// as this flag's value — so --prd here resolves to null (absent), not the
|
||||||
|
// literal string "--weird".
|
||||||
|
const result = runGsdTools(['init', 'plan-phase', '1', '--prd', '--weird'], tmpDir);
|
||||||
|
assert.ok(result.success, `a flag-shaped value must not crash the command: ${result.error}`);
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.ok(output.section_manifest.excluded.includes('prd-express-gate'), '--prd immediately followed by another --flag token must resolve to absent, per parseNamedArgs');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unicode / very long value does not crash and is included as present (row D7)', () => {
|
||||||
|
const longUnicodeValue = `产品需求文档-🚀-${'x'.repeat(2000)}`;
|
||||||
|
const result = runGsdTools(['init', 'plan-phase', '1', '--prd', longUnicodeValue], tmpDir);
|
||||||
|
assert.ok(result.success, `a unicode/very-long --prd value must not crash the command: ${result.error}`);
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.ok(output.section_manifest.included.includes('prd-express-gate'));
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -86,6 +86,24 @@ const factsArb = fc.record({
|
|||||||
// InvocationFacts — must also participate in the partition/totality
|
// InvocationFacts — must also participate in the partition/totality
|
||||||
// properties below, not just the six pre-existing fact keys.
|
// properties below, not just the six pre-existing fact keys.
|
||||||
chunkedMode: fc.boolean(),
|
chunkedMode: fc.boolean(),
|
||||||
|
// #2994 (epic #1671 Phase 6.3, matrix §K gap-fill): ten further boolean
|
||||||
|
// facts shipped alongside the widened WHEN_VOCABULARY (30 atoms total).
|
||||||
|
// Without these, `whenArb` (drawn from the FULL WHEN_VOCABULARY) can still
|
||||||
|
// generate sections gated on e.g. `state:plan-strategy-converge`, but every
|
||||||
|
// generated `facts` value left that field `undefined` — falsy under
|
||||||
|
// totality — so those sections were NEVER driven into `included` by this
|
||||||
|
// property, silently narrowing the partition/order-preservation coverage
|
||||||
|
// to the pre-#2994 atom set even though `whenArb` claimed full coverage.
|
||||||
|
uiPhaseActive: fc.boolean(),
|
||||||
|
fallowEnabled: fc.boolean(),
|
||||||
|
gitCreateTag: fc.boolean(),
|
||||||
|
planStrategyConverge: fc.boolean(),
|
||||||
|
reviewerInstancesConfigured: fc.boolean(),
|
||||||
|
autoAdvanceActive: fc.boolean(),
|
||||||
|
isMonorepo: fc.boolean(),
|
||||||
|
nextChannel: fc.boolean(),
|
||||||
|
workstreamActive: fc.boolean(),
|
||||||
|
flatMode: fc.boolean(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// ─── Row 25: exact partition ────────────────────────────────────────────────
|
// ─── Row 25: exact partition ────────────────────────────────────────────────
|
||||||
@@ -136,8 +154,12 @@ describe('property: never throws for vocabulary-valid when and arbitrary facts',
|
|||||||
|
|
||||||
test('neverThrowsWhenFactsAreMissingKeysEntirely', () => {
|
test('neverThrowsWhenFactsAreMissingKeysEntirely', () => {
|
||||||
// Totality also over PARTIAL facts objects (row 19's property-level
|
// Totality also over PARTIAL facts objects (row 19's property-level
|
||||||
// twin): dropping zero or more of the seven fact keys must never throw.
|
// twin): dropping zero or more of the fact keys must never throw.
|
||||||
const factKeys = ['flags', 'phaseNumber', 'hasPriorPhases', 'needsCodebaseMap', 'phaseMvpMode', 'worktreesEnabled', 'chunkedMode'];
|
const factKeys = [
|
||||||
|
'flags', 'phaseNumber', 'hasPriorPhases', 'needsCodebaseMap', 'phaseMvpMode', 'worktreesEnabled', 'chunkedMode',
|
||||||
|
'uiPhaseActive', 'fallowEnabled', 'gitCreateTag', 'planStrategyConverge', 'reviewerInstancesConfigured',
|
||||||
|
'autoAdvanceActive', 'isMonorepo', 'nextChannel', 'workstreamActive', 'flatMode',
|
||||||
|
];
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(sectionsArb, factsArb, fc.subarray(factKeys), (sections, facts, keysToKeep) => {
|
fc.property(sectionsArb, factsArb, fc.subarray(factKeys), (sections, facts, keysToKeep) => {
|
||||||
const partialFacts = {};
|
const partialFacts = {};
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
const { describe, test } = require('node:test');
|
const { describe, test } = require('node:test');
|
||||||
const assert = require('node:assert/strict');
|
const assert = require('node:assert/strict');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
|
||||||
const { selectSections, WHEN_PREDICATES, REASON } = require('../gsd-core/bin/lib/section-manifest.cjs');
|
const { selectSections, WHEN_PREDICATES, REASON } = require('../gsd-core/bin/lib/section-manifest.cjs');
|
||||||
const { WHEN_VOCABULARY } = require('../gsd-core/bin/lib/workflow-fragments.cjs');
|
const { WHEN_VOCABULARY } = require('../gsd-core/bin/lib/workflow-fragments.cjs');
|
||||||
@@ -262,7 +264,7 @@ describe('flags set cardinality (#2992 row B14)', () => {
|
|||||||
const sections = Object.freeze([
|
const sections = Object.freeze([
|
||||||
{ id: 'a', when: 'flag:--auto' },
|
{ id: 'a', when: 'flag:--auto' },
|
||||||
{ id: 'b', when: 'flag:--discuss' },
|
{ id: 'b', when: 'flag:--discuss' },
|
||||||
{ id: 'c', when: 'flag:--full' },
|
{ id: 'c', when: 'flag:--fix' },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
test('zeroFlagsExcludesEveryFlagSection', () => {
|
test('zeroFlagsExcludesEveryFlagSection', () => {
|
||||||
@@ -276,7 +278,7 @@ describe('flags set cardinality (#2992 row B14)', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('manyFlagsIncludeEveryMatchingSection', () => {
|
test('manyFlagsIncludeEveryMatchingSection', () => {
|
||||||
const result = selectSections(sections, facts({ flags: new Set(['--auto', '--discuss', '--full', '--irrelevant']) }));
|
const result = selectSections(sections, facts({ flags: new Set(['--auto', '--discuss', '--fix', '--irrelevant']) }));
|
||||||
assert.deepEqual(result, { included: ['a', 'b', 'c'], excluded: [] });
|
assert.deepEqual(result, { included: ['a', 'b', 'c'], excluded: [] });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -450,6 +452,161 @@ describe('state:chunked-mode predicate (row A8)', () => {
|
|||||||
// they were predicates, silently including the section or throwing an
|
// they were predicates, silently including the section or throwing an
|
||||||
// untyped error instead of failing closed with REASON.UNKNOWN_WHEN.
|
// untyped error instead of failing closed with REASON.UNKNOWN_WHEN.
|
||||||
|
|
||||||
|
// ─── #2994 (epic #1671 Phase 6.3) matrix §A: atom admission — the gate-2 hole ──
|
||||||
|
//
|
||||||
|
// The two load-time parity guards above (rows 21-23) prove WHEN_VOCABULARY <->
|
||||||
|
// WHEN_PREDICATES symmetry — every atom has SOME predicate function. Neither
|
||||||
|
// proves the predicate's underlying FACT is ever computed by a real cmdInit*
|
||||||
|
// caller: an atom whose fact no caller assembles evaluates false forever,
|
||||||
|
// which is the silent-wrong-answer class this PR exists to prevent (matrix
|
||||||
|
// row A/#8). This block closes that hole by driving every one of the 30
|
||||||
|
// frozen atoms to both a TRUE and a FALSE outcome via a constructed
|
||||||
|
// InvocationFacts, entirely at this module's own boundary (never reaching
|
||||||
|
// into src/init.cts, which is covered separately in
|
||||||
|
// tests/section-manifest-init-facts.test.cjs).
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-atom recipe: how to build an `InvocationFacts` fragment that drives
|
||||||
|
* `WHEN_PREDICATES[atom]` to `true`, and a second fragment that drives it to
|
||||||
|
* `false`. `flag` atoms derive their token mechanically (`atom.slice('flag:'.length)`,
|
||||||
|
* the same derivation B11 above already established as test-only-legal); every
|
||||||
|
* `state:` atom's backing fact FIELD is a hand-written literal here — same
|
||||||
|
* discipline as `WHEN_PREDICATES` itself (module doc comment "The evaluator is
|
||||||
|
* a LOOKUP, not a parser"): there is no mechanical way to derive
|
||||||
|
* `chunkedMode` from `"state:chunked-mode"` without inventing a tokenizer.
|
||||||
|
* `'always'` is the one structural exception: its predicate is `() => true`
|
||||||
|
* unconditionally (it backs every synthesized gap fragment), so no `facts`
|
||||||
|
* value can ever drive it to `false` — handled as its own branch below, not
|
||||||
|
* silently dropped from the 30-atom set A5 checks against.
|
||||||
|
*/
|
||||||
|
const STATE_ATOM_FACT_FIELD = Object.freeze({
|
||||||
|
'state:has-prior-phases': 'hasPriorPhases',
|
||||||
|
'state:auto-advance-active': 'autoAdvanceActive',
|
||||||
|
'state:chunked-mode': 'chunkedMode',
|
||||||
|
'state:fallow-enabled': 'fallowEnabled',
|
||||||
|
'state:git-create-tag': 'gitCreateTag',
|
||||||
|
'state:needs-codebase-map': 'needsCodebaseMap',
|
||||||
|
'state:phase-mvp-mode': 'phaseMvpMode',
|
||||||
|
'state:plan-strategy-converge': 'planStrategyConverge',
|
||||||
|
'state:reviewer-instances-configured': 'reviewerInstancesConfigured',
|
||||||
|
'state:ui-phase-active': 'uiPhaseActive',
|
||||||
|
'state:worktrees-enabled': 'worktreesEnabled',
|
||||||
|
'state:is-monorepo': 'isMonorepo',
|
||||||
|
'state:next-channel': 'nextChannel',
|
||||||
|
'state:workstream-active': 'workstreamActive',
|
||||||
|
'state:flat-mode': 'flatMode',
|
||||||
|
});
|
||||||
|
|
||||||
|
/** `state:gap-closure-phase` is the one atom keyed off `phaseNumber`, not a plain boolean field. */
|
||||||
|
const GAP_CLOSURE_ATOM = 'state:gap-closure-phase';
|
||||||
|
|
||||||
|
/** @returns {{truthy: object, falsy: object | null}} fact FRAGMENTS (merged onto `facts({})` by the caller) for `atom`; `falsy: null` marks the `'always'` exception. */
|
||||||
|
function factFragmentsFor(atom) {
|
||||||
|
if (atom === 'always') {
|
||||||
|
return { truthy: {}, falsy: null };
|
||||||
|
}
|
||||||
|
if (atom === GAP_CLOSURE_ATOM) {
|
||||||
|
return { truthy: { phaseNumber: '3.1' }, falsy: { phaseNumber: '3' } };
|
||||||
|
}
|
||||||
|
if (atom.startsWith('flag:--')) {
|
||||||
|
const token = atom.slice('flag:'.length);
|
||||||
|
return { truthy: { flags: new Set([token]) }, falsy: { flags: new Set() } };
|
||||||
|
}
|
||||||
|
const field = STATE_ATOM_FACT_FIELD[atom];
|
||||||
|
if (!field) {
|
||||||
|
throw new Error(`factFragmentsFor: atom "${atom}" has no fact-field mapping — update STATE_ATOM_FACT_FIELD`);
|
||||||
|
}
|
||||||
|
return { truthy: { [field]: true }, falsy: { [field]: false } };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('every when= atom is satisfiable by some InvocationFacts (#2994 matrix rows A1/A2/A5)', () => {
|
||||||
|
// A5 anti-vacuity: the atom set this describe block iterates must be
|
||||||
|
// non-empty AND exactly equal to WHEN_VOCABULARY's full 29-entry set — an
|
||||||
|
// A1/A2 loop over an empty or partial collection would be a green test
|
||||||
|
// proving nothing (ADR-1671 eval-gate rot, named explicitly in the design).
|
||||||
|
test('atom satisfiability set is non-empty and equals the full frozen WHEN_VOCABULARY (row A5)', () => {
|
||||||
|
assert.ok(WHEN_VOCABULARY.length > 0, 'WHEN_VOCABULARY must not be empty');
|
||||||
|
const coveredAtoms = [...WHEN_VOCABULARY].sort();
|
||||||
|
// Every atom must resolve via factFragmentsFor without throwing — this is
|
||||||
|
// the completeness check on STATE_ATOM_FACT_FIELD itself: an atom added
|
||||||
|
// to WHEN_VOCABULARY without a corresponding entry here throws loudly
|
||||||
|
// instead of silently narrowing the set A1/A2 below actually cover.
|
||||||
|
for (const atom of WHEN_VOCABULARY) {
|
||||||
|
assert.doesNotThrow(() => factFragmentsFor(atom), `factFragmentsFor must resolve "${atom}"`);
|
||||||
|
}
|
||||||
|
assert.deepEqual(coveredAtoms, [...WHEN_VOCABULARY].sort(), 'the covered atom set must be exactly WHEN_VOCABULARY');
|
||||||
|
assert.equal(WHEN_VOCABULARY.length, 29, 'sanity: the frozen vocabulary is expected at 29 entries for #2994 — update this literal alongside a deliberate vocabulary widening');
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const atom of WHEN_VOCABULARY) {
|
||||||
|
test(`atom "${atom}" is included when its fact is true, excluded when false (rows A1/A2)`, () => {
|
||||||
|
const { truthy, falsy } = factFragmentsFor(atom);
|
||||||
|
const sections = [{ id: 'target', when: atom }];
|
||||||
|
|
||||||
|
// A1: some constructed InvocationFacts drives this section into `included`.
|
||||||
|
const includedResult = selectSections(sections, facts(truthy));
|
||||||
|
assert.deepEqual(includedResult, { included: ['target'], excluded: [] }, `expected "${atom}" included under its truthy facts`);
|
||||||
|
|
||||||
|
if (falsy === null) {
|
||||||
|
// The 'always' exception: no facts value can ever exclude it — assert
|
||||||
|
// that deliberately, rather than silently skipping the atom (which
|
||||||
|
// would violate A5's "set is non-empty" guarantee for THIS assertion).
|
||||||
|
const stillIncluded = selectSections(sections, facts({}));
|
||||||
|
assert.deepEqual(stillIncluded, { included: ['target'], excluded: [] }, '"always" must remain included under every facts value — it has no false branch');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A2: some constructed InvocationFacts drives the SAME section into `excluded`.
|
||||||
|
const excludedResult = selectSections(sections, facts(falsy));
|
||||||
|
assert.deepEqual(excludedResult, { included: [], excluded: ['target'] }, `expected "${atom}" excluded under its falsy facts`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── #2994 matrix row A3: no admitted atom lacks a consuming section ───────
|
||||||
|
//
|
||||||
|
// An atom present in WHEN_VOCABULARY with zero consuming `when=` markers
|
||||||
|
// anywhere in the SHIPPED gsd-core/workflows/section-manifest.json is dead
|
||||||
|
// vocabulary: cardinality without a section, the exact rot the frozen list's
|
||||||
|
// "no atom is admitted without a consumer, in the same commit" discipline
|
||||||
|
// exists to prevent (40-design.md "Vocabulary growth"). Driven from the real
|
||||||
|
// installed-shape artifact — never a hand-built fixture — per the matrix's
|
||||||
|
// prime directive ("a hand-built manifest proves a property no real caller
|
||||||
|
// exercises").
|
||||||
|
|
||||||
|
describe('no admitted atom lacks a consuming section in the shipped manifest (#2994 matrix row A3)', () => {
|
||||||
|
const SHIPPED_MANIFEST_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'section-manifest.json');
|
||||||
|
|
||||||
|
test('every WHEN_VOCABULARY atom is used as a when= value by at least one shipped section', () => {
|
||||||
|
const manifest = JSON.parse(fs.readFileSync(SHIPPED_MANIFEST_PATH, 'utf8'));
|
||||||
|
const usedAtoms = new Set();
|
||||||
|
for (const sections of Object.values(manifest.workflows)) {
|
||||||
|
for (const section of sections) {
|
||||||
|
usedAtoms.add(section.when);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `always` is a NAMED, single exemption — not a blanket skip. It is the
|
||||||
|
// implicit gap-fragment default (a section with no `when=` marker at all
|
||||||
|
// is treated as unconditionally included) and is structural, not a
|
||||||
|
// marker atom: `gen-section-manifest.cjs`'s `explicitSections` filter
|
||||||
|
// (`s.explicit`) deliberately EXCLUDES it from the generated
|
||||||
|
// section-manifest.json, so no shipped section's `when=` value can ever
|
||||||
|
// literally read `"always"` — the atom is real (WHEN_PREDICATES and
|
||||||
|
// section-marker-parsing both accept it) but by design has zero
|
||||||
|
// consuming JSON entries.
|
||||||
|
const unusedAtoms = WHEN_VOCABULARY.filter((atom) => atom !== 'always' && !usedAtoms.has(atom));
|
||||||
|
|
||||||
|
// Deliberately UNWEAKENED per 50-test-matrix.md row A3's own instruction:
|
||||||
|
// "if any atom legitimately has no consumer today, do NOT weaken this
|
||||||
|
// test — report it to me instead, it is a real finding." No exclusion
|
||||||
|
// list beyond the single named `always` exemption above is applied here
|
||||||
|
// — if this assertion is red, that redness IS the finding, not a defect
|
||||||
|
// in the test.
|
||||||
|
assert.deepEqual(unusedAtoms, [], `dead vocabulary: these WHEN_VOCABULARY atoms have no consuming section anywhere in ${SHIPPED_MANIFEST_PATH}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Object.prototype-shaped when= values fail closed (REASON.UNKNOWN_WHEN)', () => {
|
describe('Object.prototype-shaped when= values fail closed (REASON.UNKNOWN_WHEN)', () => {
|
||||||
const HOSTILE_WHEN_VALUES = Object.freeze([
|
const HOSTILE_WHEN_VALUES = Object.freeze([
|
||||||
'constructor',
|
'constructor',
|
||||||
|
|||||||
151
tests/section-marker-inversion-guard.test.cjs
Normal file
151
tests/section-marker-inversion-guard.test.cjs
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
// allow-test-rule: source-text-is-the-product see #2994 — this file asserts that
|
||||||
|
// specific prose/resolver LINES remain physically outside a `<!-- gsd:section
|
||||||
|
// -->` marker pair in the HOST workflow .md files. The deployed contract IS
|
||||||
|
// the exact text and its position relative to the marker boundary (a runtime
|
||||||
|
// that reads only the gated fragment must still see the flag-absent
|
||||||
|
// fallback), so asserting on rendered text is the only meaningful check here
|
||||||
|
// — a typed/structural assertion on `parseWorkflowSections` output could not
|
||||||
|
// distinguish "the fallback text survived, in the right gap" from "the
|
||||||
|
// fallback text was deleted entirely along with the surrounding gap".
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inversion-guard tests — #2994 (epic #1671 Phase 6.3) matrix §F, the
|
||||||
|
* highest-risk edit in the PR (`.gsd/phase/chore-2994-fragment-model-workflows/
|
||||||
|
* 50-test-matrix.md`): gating `discuss-phase-assumptions.md`'s `auto_advance`
|
||||||
|
* step WHOLE would delete the flag-absent fallback ("End here — confirm_creation
|
||||||
|
* already ran; do not route back to it."), removing text needed EXACTLY when
|
||||||
|
* `--auto` is absent — the inverse of what a `when=` gate is supposed to do.
|
||||||
|
* The design's split is at `:651/652`: only `auto-advance-dispatch` itself is
|
||||||
|
* gated (`state:auto-advance-active`); the resolvers above it and the
|
||||||
|
* flag-absent fallback below it stay outside the marker pair, in the host
|
||||||
|
* file, unconditionally composed regardless of `section_manifest`.
|
||||||
|
*
|
||||||
|
* The same class — "an always-relevant note living immediately adjacent to a
|
||||||
|
* gated section must not itself be swallowed by the marker" — applies to
|
||||||
|
* `verify-work.md`'s MVP false-branch note (row "Same class applies").
|
||||||
|
*
|
||||||
|
* Parses each host file with the REAL `parseWorkflowSections` (never a
|
||||||
|
* second ad-hoc marker scan) to get the marker's exact byte boundaries, then
|
||||||
|
* asserts the fallback/resolver text is NOT inside the gated section's body
|
||||||
|
* and IS present in the surrounding (always-composed) gap text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { describe, test } = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const { parseWorkflowSections } = require('../gsd-core/bin/lib/workflow-fragments.cjs');
|
||||||
|
|
||||||
|
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||||
|
|
||||||
|
function readWorkflow(name) {
|
||||||
|
const filePath = path.join(WORKFLOWS_DIR, `${name}.md`);
|
||||||
|
return { filePath, content: fs.readFileSync(filePath, 'utf8') };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The gated section's own body, by id — must equal exactly one parsed section. */
|
||||||
|
function bodyOf(sections, id) {
|
||||||
|
const match = sections.filter((s) => s.id === id);
|
||||||
|
assert.equal(match.length, 1, `expected exactly one parsed section with id "${id}"`);
|
||||||
|
return match[0].body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Every gap-section body concatenated (i.e., everything OUTSIDE any explicit marker), preserving the "never inside a gate" question. */
|
||||||
|
function outsideGatedText(sections) {
|
||||||
|
return sections.filter((s) => !s.explicit).map((s) => s.body).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── F1/F2/F3: discuss-phase-assumptions.md's auto-advance-dispatch split ──
|
||||||
|
|
||||||
|
describe('discuss-phase-assumptions.md: the flag-absent fallback survives exclusion (matrix §F, highest-risk edit)', () => {
|
||||||
|
const { content } = readWorkflow('discuss-phase-assumptions');
|
||||||
|
const sections = parseWorkflowSections(content, 'gsd-core/workflows/discuss-phase-assumptions.md');
|
||||||
|
|
||||||
|
const GATED_ID = 'auto-advance-dispatch';
|
||||||
|
const FALLBACK_TEXT = 'End here — `confirm_creation` already ran; do not route back to it.';
|
||||||
|
const RESOLVER_MARKERS = [
|
||||||
|
'Parse `--auto` flag from $ARGUMENTS',
|
||||||
|
'gsd_run query config-set workflow._auto_chain_active',
|
||||||
|
'AUTO_MODE=$(gsd_run query check auto-mode --pick active',
|
||||||
|
];
|
||||||
|
|
||||||
|
test('the gated section exists and is when="state:auto-advance-active" (sanity)', () => {
|
||||||
|
const gated = sections.find((s) => s.id === GATED_ID);
|
||||||
|
assert.ok(gated, `expected a "${GATED_ID}" section to exist`);
|
||||||
|
assert.equal(gated.when, 'state:auto-advance-active');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the flag-absent "End here" fallback is present in the composed HOST output (row F1 — the inversion)', () => {
|
||||||
|
assert.ok(content.includes(FALLBACK_TEXT), 'the fallback text must exist verbatim in the host file at all');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the flag-absent fallback is NOT inside the gated section\'s own body (row F1/F3)', () => {
|
||||||
|
const gatedBody = bodyOf(sections, GATED_ID);
|
||||||
|
assert.ok(
|
||||||
|
!gatedBody.includes(FALLBACK_TEXT),
|
||||||
|
'the fallback must live OUTSIDE the gated marker — gating it away would delete the text needed exactly when --auto is absent',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the flag-absent fallback IS present in the unconditionally-composed gap text (row F2 — dispatch body included path also composes the surrounding text unconditionally)', () => {
|
||||||
|
const gapText = outsideGatedText(sections);
|
||||||
|
assert.ok(gapText.includes(FALLBACK_TEXT), 'the fallback must be part of an always-composed gap section, not lost between gaps');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('every resolver line (--auto parse, config-set, AUTO_MODE read) lies OUTSIDE the gated marker (row F3)', () => {
|
||||||
|
const gatedBody = bodyOf(sections, GATED_ID);
|
||||||
|
for (const marker of RESOLVER_MARKERS) {
|
||||||
|
assert.ok(!gatedBody.includes(marker), `resolver text "${marker}" must not live inside the gated "${GATED_ID}" section`);
|
||||||
|
}
|
||||||
|
const gapText = outsideGatedText(sections);
|
||||||
|
for (const marker of RESOLVER_MARKERS) {
|
||||||
|
assert.ok(gapText.includes(marker), `resolver text "${marker}" must be present in the unconditionally-composed surrounding text`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('composing the document with every section INCLUDED still contains the fallback (row F2 sanity — inclusion never deletes it either)', () => {
|
||||||
|
// The stub instructs conditional reading of steps/auto-advance-dispatch.md,
|
||||||
|
// but the fallback sentence itself is literal HOST text either way — this
|
||||||
|
// asserts the host's raw byte content carries it once, regardless of
|
||||||
|
// section_manifest, which composeWorkflow (verbatim strategy, #2930)
|
||||||
|
// preserves unconditionally.
|
||||||
|
const occurrences = content.split(FALLBACK_TEXT).length - 1;
|
||||||
|
assert.equal(occurrences, 1, 'the fallback text must appear exactly once in the host file');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Same class: verify-work.md's MVP false-branch note ────────────────────
|
||||||
|
|
||||||
|
describe('verify-work.md: the MVP false-branch note survives exclusion of mvp-uat-framing (same class as §F)', () => {
|
||||||
|
const { content } = readWorkflow('verify-work');
|
||||||
|
const sections = parseWorkflowSections(content, 'gsd-core/workflows/verify-work.md');
|
||||||
|
|
||||||
|
const GATED_ID = 'mvp-uat-framing';
|
||||||
|
const FALSE_BRANCH_NOTE = 'When `MVP_MODE=false` (mode is null, absent, or the phase has no `**Mode:**` line in ROADMAP.md), fall back to the standard UAT generation path — no behavioral change.';
|
||||||
|
|
||||||
|
test('the gated section exists and is when="state:phase-mvp-mode" (sanity)', () => {
|
||||||
|
const gated = sections.find((s) => s.id === GATED_ID);
|
||||||
|
assert.ok(gated, `expected a "${GATED_ID}" section to exist`);
|
||||||
|
assert.equal(gated.when, 'state:phase-mvp-mode');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the MVP=false fallback note is present in the host file at all', () => {
|
||||||
|
assert.ok(content.includes(FALSE_BRANCH_NOTE));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the MVP=false fallback note is NOT inside the gated mvp-uat-framing section body', () => {
|
||||||
|
const gatedBody = bodyOf(sections, GATED_ID);
|
||||||
|
assert.ok(
|
||||||
|
!gatedBody.includes(FALSE_BRANCH_NOTE),
|
||||||
|
'gating mvp-uat-framing away (MVP_MODE=false) must not delete the note that documents exactly that fallback',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the MVP=false fallback note IS present in the unconditionally-composed gap text', () => {
|
||||||
|
const gapText = outsideGatedText(sections);
|
||||||
|
assert.ok(gapText.includes(FALSE_BRANCH_NOTE));
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -26,6 +26,7 @@ const { test, describe } = require('node:test');
|
|||||||
const assert = require('node:assert/strict');
|
const assert = require('node:assert/strict');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const { readWorkflowCombined } = require('./helpers.cjs');
|
||||||
|
|
||||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||||
const LIVENESS_PHRASE = 'runs in a subagent';
|
const LIVENESS_PHRASE = 'runs in a subagent';
|
||||||
@@ -84,11 +85,24 @@ describe('spawn-liveness-banner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('every workflow that dispatches a subagent contains the liveness phrase somewhere', () => {
|
test('every workflow that dispatches a subagent contains the liveness phrase somewhere', () => {
|
||||||
const mdFiles = findMdFiles(WORKFLOWS_DIR);
|
// #2994: scoped to HOST workflow files only (top-level gsd-core/workflows/*.md),
|
||||||
|
// each read via readWorkflowCombined (host + its workflows/<wf>/steps/*.md
|
||||||
|
// fragments). The fragment model can move a subagent dispatch site into a step
|
||||||
|
// file while the host still carries the liveness phrase elsewhere for its other
|
||||||
|
// dispatches (e.g. docs-update.md). A raw recursive `findMdFiles` walk treats
|
||||||
|
// that step fragment as an independent "workflow" and reports a false positive
|
||||||
|
// — it is not a standalone workflow, it is part of its host's contract. The
|
||||||
|
// asserted property (every workflow that dispatches a subagent documents the
|
||||||
|
// liveness phrase somewhere in that workflow) is unchanged; only the file
|
||||||
|
// boundary the check honors is corrected.
|
||||||
|
const hostFiles = fs
|
||||||
|
.readdirSync(WORKFLOWS_DIR, { withFileTypes: true })
|
||||||
|
.filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
|
||||||
|
.map((entry) => path.join(WORKFLOWS_DIR, entry.name));
|
||||||
const presenceViolations = [];
|
const presenceViolations = [];
|
||||||
|
|
||||||
for (const filePath of mdFiles) {
|
for (const filePath of hostFiles) {
|
||||||
const content = fs.readFileSync(filePath, 'utf-8');
|
const content = readWorkflowCombined(filePath);
|
||||||
const rel = path.relative(WORKFLOWS_DIR, filePath);
|
const rel = path.relative(WORKFLOWS_DIR, filePath);
|
||||||
|
|
||||||
if (content.includes('subagent_type') && !content.includes(LIVENESS_PHRASE)) {
|
if (content.includes('subagent_type') && !content.includes(LIVENESS_PHRASE)) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user