Merge branch 'next' into fix/2137-derive-progress-header-driven

This commit is contained in:
Behruz Nassre Esfahani
2026-07-13 07:07:04 -07:00
committed by GitHub
46 changed files with 1108 additions and 170 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 2148
---
**`commit --files` now commits only the declared paths** — `gsd-tools commit --files A B` previously ran a bare `git commit` that absorbed the entire staged index, silently sweeping in unrelated files the caller never named. The commit now appends a pathspec (`-- <paths>`) so only the staged subset of `--files` lands in the commit; the no-`--files` default path is unchanged. Missing tracked files are still skipped (not committed as deletions, #2014), and when all declared files are missing the function short-circuits to `nothing_to_commit` instead of absorbing the index. (#2112)

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 2155
---
**`milestone complete --dry-run` now prints a preview plan instead of silently mutating** — `gsd-tools milestone complete --dry-run` was neither parsed nor rejected, so a caller expecting a preview triggered the full destructive mutation (archive phases, move audit artifacts, rewrite STATE.md) with no way to back out. The `--dry-run` flag is now honored: it returns a JSON plan listing `would_archive` (roadmap, requirements, audit, phase dirs) and `would_update` (MILESTONES.md, STATE.md) targets with zero filesystem mutations. (#2118)

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 2154
---
**`/gsd-secure-phase` now has a single SECURITY.md writer** — the `gsd-security-auditor` subagent previously held `Write`/`Edit` tools and was instructed to "write SECURITY.md" with no padded `<N>-` prefix and no template frontmatter, while the orchestrator's Step 6 also wrote the phase-scoped `<N>-SECURITY.md` from `templates/SECURITY.md`. The auditor is now return-only (drops `Write`/`Edit`, returns a structured verdict with `threats_open`); the orchestrator is the sole file writer. The workflow's Step 5 spawn constraints explicitly forbid the auditor from writing SECURITY.md. (#2119)

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 2231
---
**`phase complete --phase N` now works alongside the positional form** — the phase verb family treated the first positional as the phase number, so `--phase 12` was passed as the literal phase name and failed with 'Phase --phase not found'. The phase family now accepts the --phase flag consistently with the state family, and unrecognized flags yield a usage error. (#2201)

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 2227
---
**`/gsd-debug` no longer stalls on a phantom background handoff** — the orchestrator treated the foreground session-manager spawn as a background task and queried its agent ID via TaskOutput (which needs a task ID), then waited on a handoff that was never queryable. The workflow now states the spawn is foreground/blocking, forbids passing an agent ID to TaskOutput, and gives a lost-handoff recovery path. (#2196)

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 2229
---
**`phase complete` no longer checks the wrong ROADMAP checkbox or writes the plan count into a shipped milestone** — the roadmap mutators ran unanchored and un-milestone-scoped, so they could flip a bullet inside a backticked prose literal or a Backlog entry instead of the closing phase's, and write the plan count into a same-numbered phase in a shipped milestone. The checkbox flip is now line-anchored and both writers are scoped to the current milestone. (#2200)

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 2228
---
**Bullet/em-dash ROADMAP phases no longer resolve to `Phase null`** — the roadmap phase lookup matched only ATX headings with a colon, so a bullet entry like `- [ ] **Phase N — Name**` (which the roadmapper emits) failed to resolve and `Phase null` landed in STATE.md; a bullet-only ROADMAP also broke the milestone phase count. Phase lookup and the milestone filter now accept bullet/checkbox entries with an em-dash/en-dash/hyphen/colon separator. (#2199)

View File

@@ -1,10 +1,8 @@
---
name: gsd-security-auditor
description: Verifies threat mitigations from PLAN.md threat model exist in implemented code. Produces SECURITY.md. Spawned by /gsd:secure-phase.
description: Verifies threat mitigations from PLAN.md threat model exist in implemented code. Returns structured security verdict (SECURED / OPEN_THREATS / ESCALATE). Spawned by /gsd:secure-phase.
tools:
- Read
- Write
- Edit
- Bash
- Glob
- Grep
@@ -15,11 +13,11 @@ color: red
<role>
An implemented phase has been submitted for security audit. Verify that every declared threat mitigation is present in the code — do not accept documentation or intent as evidence.
Does NOT scan blindly for new vulnerabilities. Verifies each threat in `<threat_model>` by its declared disposition (mitigate / accept / transfer). Reports gaps. Writes SECURITY.md.
Does NOT scan blindly for new vulnerabilities. Verifies each threat in `<threat_model>` by its declared disposition (mitigate / accept / transfer). Reports gaps. Returns a structured verdict — the orchestrator owns the SECURITY.md file write (#2119: single-writer contract).
**Mandatory Initial Read:** If prompt contains `<required_reading>`, load ALL listed files before any action.
**Implementation files are READ-ONLY.** Only create/modify: SECURITY.md. Implementation security gaps → OPEN_THREATS or ESCALATE. Never patch implementation.
**Implementation files are READ-ONLY.** The auditor does NOT write any files — it returns a structured verdict (SECURED / OPEN_THREATS / ESCALATE). The orchestrator persists SECURITY.md. Implementation security gaps → OPEN_THREATS or ESCALATE. Never patch implementation.
</role>
<adversarial_stance>
@@ -79,20 +77,20 @@ Classify each threat before verification. Record classification for every threat
- L3: deep trace — follow the data flow end-to-end, check edge cases and ordering, confirm no bypass path exists.
</step>
<step name="verify_and_write">
<step name="verify_and_return">
For each `mitigate` threat: grep for declared mitigation pattern in cited files → found = `CLOSED`, not found = `OPEN`. Apply depth per `asvs_level` (see analyze_threats step).
For `accept` threats: check SECURITY.md accepted risks log → entry present = `CLOSED`, absent = `OPEN`.
For `accept` threats: check existing SECURITY.md accepted risks log → entry present = `CLOSED`, absent = `OPEN`.
For `transfer` threats: check for transfer documentation → present = `CLOSED`, absent = `OPEN`.
For each `threat_flag` in SUMMARY.md `## Threat Flags`: if maps to existing threat ID → informational. If no mapping → log as `unregistered_flag` in SECURITY.md (not a blocker).
For each `threat_flag` in SUMMARY.md `## Threat Flags`: if maps to existing threat ID → informational. If no mapping → log as `unregistered_flag` in the structured return (not a blocker).
**Severity-aware `threats_open` computation (severity order: critical > high > medium > low):**
`threats_open` (the SECURITY.md frontmatter gate field) = the count of threats whose status is OPEN AND whose severity rank ≥ the `block_on` rank. `block_on: none` ⇒ 0 (nothing ever blocks). `block_on: low` ⇒ all open threats block. `block_on: high` (default) ⇒ only high and critical open threats block.
Open threats BELOW the block threshold are recorded in SECURITY.md as **open — below {block_on} threshold (non-blocking)** and MUST NOT be counted in `threats_open`.
Open threats BELOW the block threshold are recorded in the return as **open — below {block_on} threshold (non-blocking)** and MUST NOT be counted in `threats_open`.
**Fail-closed for missing severity:** if an OPEN threat has no severity or an unparseable severity (e.g. a legacy register predating the Severity column), treat it as `critical` for this computation — it COUNTS toward `threats_open` (blocking). Never silently drop an unranked open threat.
Write SECURITY.md. Set `threats_open` to the severity-filtered count. Return structured result.
Return the structured result (SECURED / OPEN_THREATS / ESCALATE) with `threats_open` set to the severity-filtered count. The orchestrator writes SECURITY.md from this data — the auditor does NOT write any files (#2119).
</step>
</execution_flow>
@@ -116,7 +114,7 @@ Write SECURITY.md. Set `threats_open` to the severity-filtered count. Return str
### Unregistered Flags
{none / list from SUMMARY.md ## Threat Flags with no threat mapping}
SECURITY.md: {path}
**threats_open:** {count}
```
## OPEN_THREATS
@@ -145,9 +143,9 @@ SECURITY.md: {path}
*Only blocking-open threats count toward `threats_open` in SECURITY.md frontmatter.*
Next: Implement mitigations or document as accepted in SECURITY.md accepted risks log, then re-run /gsd:secure-phase.
Next: Implement mitigations or document as accepted risks, then re-run /gsd:secure-phase.
SECURITY.md: {path}
**threats_open:** {count}
```
## ESCALATE
@@ -172,6 +170,6 @@ SECURITY.md: {path}
- [ ] Each threat verified by disposition type (mitigate / accept / transfer)
- [ ] Threat flags from SUMMARY.md `## Threat Flags` incorporated
- [ ] Implementation files never modified
- [ ] SECURITY.md written to correct path
- [ ] Structured return: SECURED / OPEN_THREATS / ESCALATE
- [ ] No files written — structured verdict returned only (orchestrator writes SECURITY.md)
- [ ] Structured return: SECURED / OPEN_THREATS / ESCALATE with `threats_open` count
</success_criteria>

View File

@@ -479,10 +479,10 @@ Communication style, decision patterns, debugging approach, UX preferences, vend
|----------|-------|
| **Spawned by** | `/gsd-secure-phase` |
| **Parallelism** | Single instance |
| **Tools** | Read, Write, Edit, Bash, Glob, Grep |
| **Tools** | Read, Bash, Glob, Grep |
| **Model (balanced)** | Sonnet |
| **Color** | Red |
| **Produces** | `{phase}-SECURITY.md` |
| **Produces** | Structured verdict (SECURED / OPEN_THREATS / ESCALATE) — orchestrator writes `{phase}-SECURITY.md` (#2119) |
**Key behaviors:**
- Verifies each threat by its declared disposition (mitigate / accept / transfer)

View File

@@ -1500,7 +1500,9 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
// new-milestone never inherits un-archived dirs. --no-archive-phases opts out.
const archivePhases = !args.includes('--no-archive-phases');
const force = args.includes('--force');
milestone.cmdMilestoneComplete(cwd, args[2], { name: milestoneName, archivePhases, force }, raw);
// #2118: --dry-run prints a preview plan without mutating.
const dryRun = args.includes('--dry-run');
milestone.cmdMilestoneComplete(cwd, args[2], { name: milestoneName, archivePhases, force, dryRun }, raw);
} else {
error('Unknown milestone subcommand. Available: complete', ERROR_REASON.SDK_UNKNOWN_COMMAND);
}

View File

@@ -308,7 +308,7 @@ If there are passing tests to commit:
```bash
git add {test files}
git commit -m "test(phase-${phase_number}): add unit and E2E tests from add-tests command"
git commit -m "test(phase-${phase_number}): add unit and E2E tests from add-tests command" -- {test files}
```
Present next steps:

View File

@@ -190,6 +190,8 @@ Create `.planning/debug/{slug}.md` with initial state using the Write tool (neve
After initial context setup, spawn the session manager to handle the full checkpoint/continuation loop. The session manager handles specialist_hint dispatch internally: when gsd-debugger returns ROOT CAUSE FOUND it extracts the specialist_hint field and invokes the matching skill (e.g. typescript-expert, swift-concurrency) before offering fix options.
> **Foreground, blocking spawn — #2196.** The `Agent(subagent_type="gsd-debug-session-manager", …)` call below is FOREGROUND and BLOCKING — it returns the compact session summary directly. Wait for it; do not background it, and do not poll for it. Never pass an agent or session identifier to `TaskOutput` — an agent ID is NOT a task ID, so `TaskOutput <agent-id>` always returns `No task found with ID`. If the spawn returns no usable result (the handoff is lost), do NOT claim the session is still running: preserve the checkpoint at `.planning/debug/{slug}.md`, report the failed handoff plainly, and resume by re-spawning the session manager or via `/gsd:debug continue {slug}`.
Print before spawning (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze):
```
[debug] Delegating loop to session manager...

View File

@@ -108,7 +108,7 @@ Agent(
"<files_to_read>{PLAN, SUMMARY, impl files, SECURITY.md}</files_to_read>" +
"<threat_register>{threat register}</threat_register>" +
"<config>asvs_level: {SECURITY_ASVS}, block_on: {SECURITY_BLOCK_ON}</config>" +
"<constraints>Never modify implementation files. Verify mitigations exist — do not scan for new threats. Escalate implementation gaps.</constraints>" +
"<constraints>Never modify implementation files. Verify mitigations exist — do not scan for new threats. Escalate implementation gaps. Return a structured verdict only — do NOT write SECURITY.md (the orchestrator owns the file write).</constraints>" +
"${AGENT_SKILLS_AUDITOR}",
subagent_type="gsd-security-auditor",
model="{AUDITOR_MODEL}",

View File

@@ -456,7 +456,7 @@ Write to: `{phase_dir}/{padded_phase}-SPEC.md`
```bash
git add "${phase_dir}/${padded_phase}-SPEC.md"
git commit -m "spec(phase-${phase_number}): add SPEC.md for ${phase_name} — ${requirement_count} requirements (#2213)"
git commit -m "spec(phase-${phase_number}): add SPEC.md for ${phase_name} — ${requirement_count} requirements (#2213)" -- "${phase_dir}/${padded_phase}-SPEC.md"
```
If `commit_docs` is false: Skip commit. Note that SPEC.md was written but not committed.

View File

@@ -69,12 +69,17 @@ function cleanup(root) {
// With no args, regenerates ALL runtimes. With args, only the named runtimes.
const targets = process.argv.slice(2).length > 0 ? process.argv.slice(2) : Object.keys(RUNTIME_META);
fs.mkdirSync(FIXTURE_DIR, { recursive: true });
// Track the claude root so we can reuse it for the local layout fixture below.
let claudeRoot = null;
for (const runtime of targets) {
if (!Object.prototype.hasOwnProperty.call(RUNTIME_META, runtime)) {
process.stderr.write(`[gen] unknown runtime '${runtime}' (not in RUNTIME_META) — skipping\n`);
continue;
}
const { configDir, root } = runMinimalInstall({ runtime, scope: 'global' });
if (runtime === 'claude') claudeRoot = root;
let actual;
try {
actual = buildParityManifest(configDir, root);
@@ -85,3 +90,17 @@ for (const runtime of targets) {
fs.writeFileSync(fixturePath, JSON.stringify(actual, null, 2) + '\n', 'utf8');
process.stdout.write(`[gen] ${runtime}: wrote ${Object.keys(actual).length} file hashes -> ${fixturePath}\n`);
}
// Also regenerate the claude LOCAL legacy-layout fixture (claude-local.json).
// This layout is distinct from the global install (commands/gsd-*.md +
// agents/gsd-*.md) and has its own parity assertion in the test harness.
const { configDir: localConfigDir, root: localRoot } = runMinimalInstall({ runtime: 'claude', scope: 'local' });
let localActual;
try {
localActual = buildParityManifest(localConfigDir, localRoot);
} finally {
cleanup(localRoot);
}
const localFixturePath = path.join(FIXTURE_DIR, 'claude-local.json');
fs.writeFileSync(localFixturePath, JSON.stringify(localActual, null, 2) + '\n', 'utf8');
process.stdout.write(`[gen] claude-local: wrote ${Object.keys(localActual).length} file hashes -> ${localFixturePath}\n`);

View File

@@ -694,6 +694,7 @@ function cmdCommit(cwd: string, message: string | undefined, files: string[] | u
// Stage files
const explicitFiles = files && files.length > 0;
const filesToStage = explicitFiles ? files : ['.planning/'];
const stagedPaths: string[] = [];
for (const file of filesToStage) {
const fullPath = path.join(cwd, file);
if (!fs.existsSync(fullPath)) {
@@ -708,12 +709,32 @@ function cmdCommit(cwd: string, message: string | undefined, files: string[] | u
execGit(['rm', '--cached', '--ignore-unmatch', file], { cwd });
} else {
execGit(['add', file], { cwd });
stagedPaths.push(file);
}
}
// Commit (--no-verify skips pre-commit hooks, used by parallel executor agents)
const commitArgs = amend ? ['commit', '--amend', '--no-edit'] : ['commit', '-m', sanitizedMessage as string];
// Commit — when the caller declared a scope (--files), append a pathspec so
// only the declared files land in the commit, not the entire index (#2112).
// The pathspec uses stagedPaths (not filesToStage) so skipped missing files
// are excluded — otherwise git would record them as deletions (#2014).
// During a merge, git refuses partial commits — fall back to a bare commit.
// --amend is left without a pathspec: amending with -- <paths> is a different
// operation that rewrites the tip with only those paths.
if (explicitFiles && stagedPaths.length === 0 && !amend) {
const result = { committed: false, hash: null, reason: 'nothing_to_commit' };
output(result, raw, 'nothing');
return;
}
const isMergeInProgress = execGit(['rev-parse', '-q', '--verify', 'MERGE_HEAD'], { cwd }).exitCode === 0;
const canScope = explicitFiles && stagedPaths.length > 0 && !amend
&& !isMergeInProgress;
const commitArgs = amend
? ['commit', '--amend', '--no-edit']
: ['commit', '-m', sanitizedMessage as string];
if (noVerify) commitArgs.push('--no-verify');
if (canScope) {
commitArgs.push('--', ...stagedPaths);
}
const commitResult = execGit(commitArgs, { cwd });
if (commitResult.exitCode !== 0) {
if (commitResult.stdout.includes('nothing to commit') || commitResult.stderr.includes('nothing to commit')) {
@@ -817,13 +838,22 @@ function cmdCommitToSubrepo(cwd: string, message: string | undefined, files: str
const repoCwd = path.join(cwd, repo);
// Stage files (strip sub-repo prefix for paths relative to that repo)
const stagedRelPaths: string[] = [];
for (const file of repoFiles) {
const relativePath = file.slice(repo.length + 1);
execGit(['add', relativePath], { cwd: repoCwd });
const addResult = execGit(['add', relativePath], { cwd: repoCwd });
if (addResult.exitCode === 0) {
stagedRelPaths.push(relativePath);
}
}
// Commit
const commitResult = execGit(['commit', '-m', message as string], { cwd: repoCwd });
// Commit — pathspec limits the commit to the staged files only (#2112)
const isMergeInProgressSub = execGit(['rev-parse', '-q', '--verify', 'MERGE_HEAD'], { cwd: repoCwd }).exitCode === 0;
const canScopeSub = stagedRelPaths.length > 0 && !isMergeInProgressSub;
const commitArgs = canScopeSub
? ['commit', '-m', message as string, '--', ...stagedRelPaths]
: ['commit', '-m', message as string];
const commitResult = execGit(commitArgs, { cwd: repoCwd });
if (commitResult.exitCode !== 0) {
if (commitResult.stdout.includes('nothing to commit') || commitResult.stderr.includes('nothing to commit')) {
repos[repo] = { committed: false, hash: null, files: repoFiles, reason: 'nothing_to_commit' };
@@ -969,8 +999,16 @@ function cmdPrSubrepo(
}
}
// 5. Commit
const commitResult = execGit(['commit', '-m', commitMessage as string], { cwd: repoCwd });
// 5. Commit — pathspec limits the commit to the staged files only (#2112).
// changedFiles includes both old and new paths for renames so the full
// rename is captured atomically (pathspec on newPath alone would leave the
// deletion of oldPath stranded in the index).
const isMergeInProgressPr = execGit(['rev-parse', '-q', '--verify', 'MERGE_HEAD'], { cwd: repoCwd }).exitCode === 0;
const canScopePr = changedFiles.length > 0 && !isMergeInProgressPr;
const commitArgs = canScopePr
? ['commit', '-m', commitMessage as string, '--', ...changedFiles]
: ['commit', '-m', commitMessage as string];
const commitResult = execGit(commitArgs, { cwd: repoCwd });
if (commitResult.exitCode !== 0) {
rollback();
error(`Failed to commit in ${repo}: ${commitResult.stderr}`);

View File

@@ -38,6 +38,7 @@ interface MilestoneCompleteOptions {
name?: string;
force?: boolean;
archivePhases?: boolean;
dryRun?: boolean;
}
function cmdRequirementsMarkComplete(cwd: string, reqIdsRaw: string[], raw: boolean): void {
@@ -161,8 +162,10 @@ function cmdMilestoneComplete(cwd: string, version: string, options: MilestoneCo
const today = realClock.localToday();
const milestoneName = options.name || version;
// Ensure archive directory exists
platformEnsureDir(archiveDir);
// Ensure archive directory exists (skipped in dry-run — no mutations)
if (!options.dryRun) {
platformEnsureDir(archiveDir);
}
// Scope stats and accomplishments to only the phases belonging to the
// current milestone's ROADMAP. Uses the shared filter from roadmap-parser.cjs
@@ -295,6 +298,47 @@ function cmdMilestoneComplete(cwd: string, version: string, options: MilestoneCo
/* intentionally empty */
}
// #2118: --dry-run preview — compute what WOULD happen without mutating.
// The stats above are read-only; all mutations start at the archive section below.
if (options.dryRun) {
const phaseDirsToArchive: string[] = [];
if (options.archivePhases !== false) {
try {
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
for (const e of entries) {
if (e.isDirectory() && isDirInMilestone(e.name)) {
phaseDirsToArchive.push(e.name);
}
}
} catch { /* phasesDir missing — nothing to archive */ }
}
const dryRunResult = {
dry_run: true,
version,
name: milestoneName,
stats: { phases: phaseCount, plans: totalPlans, tasks: totalTasks },
accomplishments,
would_archive: {
roadmap: fs.existsSync(roadmapPath)
? { source: path.relative(cwd, roadmapPath).split(path.sep).join('/'), target: path.relative(cwd, path.join(archiveDir, `${version}-ROADMAP.md`)).split(path.sep).join('/') }
: null,
requirements: fs.existsSync(reqPath)
? { source: path.relative(cwd, reqPath).split(path.sep).join('/'), target: path.relative(cwd, path.join(archiveDir, `${version}-REQUIREMENTS.md`)).split(path.sep).join('/') }
: null,
audit: fs.existsSync(path.join(planningBase, `${version}-MILESTONE-AUDIT.md`))
? { source: path.relative(cwd, path.join(planningBase, `${version}-MILESTONE-AUDIT.md`)).split(path.sep).join('/'), target: path.relative(cwd, path.join(archiveDir, `${version}-MILESTONE-AUDIT.md`)).split(path.sep).join('/') }
: null,
phases: phaseDirsToArchive,
},
would_update: {
milestones_md: path.relative(cwd, milestonesPath).split(path.sep).join('/'),
state_md: fs.existsSync(statePath) ? path.relative(cwd, statePath).split(path.sep).join('/') : null,
},
};
output(dryRunResult, raw);
return;
}
// Archive ROADMAP.md
if (fs.existsSync(roadmapPath)) {
const roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');

View File

@@ -163,7 +163,28 @@ function routePhaseCommand({ phase, args, cwd, raw, error }: RoutePhaseCommandOp
return { ok: true as const, data: null };
},
complete: (_ctx: Record<string, unknown>): { ok: true; data: null } => {
phase.cmdPhaseComplete(cwd, args[2], raw);
// #2201: accept --phase N as well as the positional form (the state
// family already accepts --phase). An unrecognized flag is a usage
// error, not "Phase --phase not found".
let phaseNum: string | null = null;
for (let i = 2; i < args.length; i++) {
if (args[i] === '--phase') {
phaseNum = args[++i];
if (!phaseNum || phaseNum.startsWith('--'))
return makeInvalidArgs('--phase', '--phase requires a value') as never;
} else if (args[i].startsWith('--phase=')) {
phaseNum = args[i].slice(8);
} else if (args[i] === '--raw') {
continue;
} else if (args[i].startsWith('--')) {
return makeInvalidArgs(args[i], `phase complete does not support ${args[i]}`) as never;
} else {
phaseNum = args[i];
}
}
if (!phaseNum)
return makeInvalidArgs('--phase', 'phase number required (positional or --phase N)') as never;
phase.cmdPhaseComplete(cwd, phaseNum, raw);
return { ok: true as const, data: null };
},
'uat-passed': (_ctx: Record<string, unknown>): { ok: true; data: null } => {
@@ -185,7 +206,26 @@ function routePhaseCommand({ phase, args, cwd, raw, error }: RoutePhaseCommandOp
},
// #1437 — list plan files for a phase
'list-plans': (_ctx: Record<string, unknown>): { ok: true; data: null } => {
phase.cmdPhaseListPlans(cwd, args[2], raw);
// #2201: accept --phase N as well as positional.
let phaseNum: string | null = null;
for (let i = 2; i < args.length; i++) {
if (args[i] === '--phase') {
phaseNum = args[++i];
if (!phaseNum || phaseNum.startsWith('--'))
return makeInvalidArgs('--phase', '--phase requires a value') as never;
} else if (args[i].startsWith('--phase=')) {
phaseNum = args[i].slice(8);
} else if (args[i] === '--raw') {
continue;
} else if (args[i].startsWith('--')) {
return makeInvalidArgs(args[i], `phase list-plans does not support ${args[i]}`) as never;
} else {
phaseNum = args[i];
}
}
if (!phaseNum)
return makeInvalidArgs('--phase', 'phase number required (positional or --phase N)') as never;
phase.cmdPhaseListPlans(cwd, phaseNum, raw);
return { ok: true as const, data: null };
},
},

View File

@@ -44,7 +44,7 @@ import phaseLocatorMod = require('./phase-locator.cjs');
const { findPhaseInternal, getArchivedPhaseDirs } = phaseLocatorMod;
// eslint-disable-next-line @typescript-eslint/no-require-imports -- roadmap-parser.cjs is an export= CommonJS module
import roadmapParserMod = require('./roadmap-parser.cjs');
const { stripShippedMilestones, extractCurrentMilestone, getMilestonePhaseFilter } = roadmapParserMod;
const { stripShippedMilestones, extractCurrentMilestone, getMilestonePhaseFilter, currentMilestoneRawRanges } = roadmapParserMod;
// eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module
import planningWorkspace = require('./planning-workspace.cjs');
// eslint-disable-next-line @typescript-eslint/no-require-imports -- frontmatter.cjs is an export= CommonJS module
@@ -1475,13 +1475,13 @@ function cmdPhaseComplete(cwd: string, phaseNum: string, raw: boolean): void {
// so completing an already-checked phase (idempotent re-run) checked the
// wrong phase's box. Mirrors the tight pattern used by phase-insert
// (`]\\s*(?:\\*\\*)?Phase`).
// #2067/#2200: line-anchored (^ + m flag, optional leading indent) so an
// inline / backticked prose literal cannot match. Milestone-scoped below
// (mutateMilestonePhase) so a Backlog entry or a same-numbered shipped-
// milestone phase cannot be flipped either.
const checkboxPattern = new RegExp(
`(-\\s*\\[)[ ](\\]\\s*(?:\\*\\*)?\\s*Phase\\s+${phaseEscaped}${OPTIONAL_PHASE_TAG_SOURCE}[:\\s][^\\n]*)`,
'i',
);
roadmapContent = roadmapContent.replace(
checkboxPattern,
`$1x$2 (completed ${today})`,
`^[ \\t]*(-\\s*\\[)[ ](\\]\\s*(?:\\*\\*)?\\s*Phase\\s+${phaseEscaped}${OPTIONAL_PHASE_TAG_SOURCE}[:\\s][^\\n]*)`,
'im',
);
const tableRowPattern = new RegExp(
@@ -1522,21 +1522,47 @@ function cmdPhaseComplete(cwd: string, phaseNum: string, raw: boolean): void {
`(#{2,4}\\s*Phase\\s+${phaseEscaped}(?:(?!\\n#{1,4}\\s)[\\s\\S])*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,
'i',
);
roadmapContent = roadmapContent.replace(
planCountPattern,
`$1${summaryCount}/${planCount} plans complete`,
);
const phaseInfoSummaries = phaseInfo['summaries'] as string[];
for (const summaryFile of phaseInfoSummaries) {
const planId = summaryFile.replace('-SUMMARY.md', '').replace('SUMMARY.md', '');
if (!planId) continue;
const planEscaped = escapeRegex(planId);
const planCheckboxPattern = new RegExp(
`(-\\s*\\[) (\\]\\s*(?:\\*\\*)?${planEscaped}(?:\\*\\*)?)`,
'i',
);
roadmapContent = (roadmapContent).replace(planCheckboxPattern, '$1x$2');
// #2200: apply the phase-checkbox flip, the plan-count write, and the
// per-plan checkbox flips ONLY within the current milestone's region(s)
// (primary section + optional Phase Details section). A bullet/heading in
// a shipped milestone, a Backlog section, or a backticked prose literal is
// outside the window and stays untouched. With no versioned active
// milestone, fall back to whole-content mutation (prior behaviour).
const mutateMilestonePhase = (slice: string): string => {
let s = slice;
s = s.replace(checkboxPattern, `$1x$2 (completed ${today})`);
s = s.replace(planCountPattern, `$1${summaryCount}/${planCount} plans complete`);
for (const summaryFile of phaseInfoSummaries) {
const planId = summaryFile.replace('-SUMMARY.md', '').replace('SUMMARY.md', '');
if (!planId) continue;
const planEscaped = escapeRegex(planId);
const planCheckboxPattern = new RegExp(
`(-\\s*\\[) (\\]\\s*(?:\\*\\*)?${planEscaped}(?:\\*\\*)?)`,
'i',
);
s = s.replace(planCheckboxPattern, '$1x$2');
}
return s;
};
const milestoneRanges = currentMilestoneRawRanges(roadmapContent, cwd);
if (milestoneRanges) {
// Splice later windows first so an earlier window's offsets are not
// shifted by a length-changing mutation in a later window.
const windows = [milestoneRanges.details, milestoneRanges.primary]
.filter((w): w is { start: number; end: number } => w !== null)
.sort((a, b) => b.start - a.start);
for (const w of windows) {
roadmapContent =
roadmapContent.slice(0, w.start)
+ mutateMilestonePhase(roadmapContent.slice(w.start, w.end))
+ roadmapContent.slice(w.end);
}
} else {
roadmapContent = mutateMilestonePhase(roadmapContent);
}
writes.push({

View File

@@ -202,6 +202,23 @@ function replaceInCurrentMilestone(content: string, pattern: RegExp, replacement
// ─── Roadmap phase lookup ─────────────────────────────────────────────────────
// #2199: a bullet/checkbox phase entry, e.g. `- [ ] **Phase 36 — Authentication**`
// (the bundled roadmapper emits this in bullet-house-style ROADMAPs). The number
// is captured in group 1, the name in group 2; the separator may be an em-dash,
// en-dash, hyphen, or colon. Used as a fallback when no ATX heading matches, and
// to count phases in a milestone that uses the bullet form.
const BULLET_PHASE_LINE_PATTERN =
/^\s*[-*]\s+(?:\[[ xX]\]\s+)?\*\*Phase\s+([\w][\w.-]*)(?:\s*\([^)\n]{0,200}\))?\s*[—–:\-]\s*(.+?)\*\*/im;
/** Build a bullet-phase-line regex pinned to a specific phase number (#2199). */
function bulletPhaseLineFor(phaseNum: unknown, phaseSource?: string): RegExp {
const num = phaseSource ?? phaseMarkdownRegexSource(phaseNum);
return new RegExp(
`^\\s*[-*]\\s+(?:\\[[ xX]\\]\\s+)?\\*\\*Phase\\s+(${num})${OPTIONAL_PHASE_TAG_SOURCE}\\s*[—–:\\-]\\s*(.+?)\\*\\*`,
'im',
);
}
interface RoadmapPhaseResult {
found: boolean;
phase_number: string;
@@ -241,6 +258,22 @@ function findRoadmapPhaseInContent(content: string, phaseNum: unknown, phaseSour
};
}
function findRoadmapBulletPhaseInContent(content: string, phaseNum: unknown, phaseSource?: string): RoadmapPhaseResult | null {
// #2199: bullet/checkbox entry fallback (`- [ ] **Phase N — name**`). Returns
// the single bullet line as the section (no multi-line body) — used only as a
// last resort, AFTER heading lookup on scoped + full content has failed, so a
// heading with a Requirements/Goal section always wins.
const bulletMatch = content.match(bulletPhaseLineFor(phaseNum, phaseSource));
if (!bulletMatch) return null;
return {
found: true,
phase_number: String(phaseNum),
phase_name: bulletMatch[2].trim(),
goal: null,
section: bulletMatch[0].trim(),
};
}
function getRoadmapPhaseInternal(cwd: string, phaseNum: unknown): RoadmapPhaseResult | null {
if (!phaseNum) return null;
const normalizedPhase = stripProjectCodePrefix(phaseNum);
@@ -262,6 +295,17 @@ function getRoadmapPhaseInternal(cwd: string, phaseNum: unknown): RoadmapPhaseRe
if (fullResult) return fullResult;
}
// #2199: no ATX heading matched on scoped or full content — fall back to a
// bullet/checkbox entry (em-dash/en-dash/hyphen/colon separator). Last resort
// so a bullet never pre-empts a heading that carries the Requirements section.
for (const source of roadmapPhaseLookupSources(phaseNum)) {
const scopedBullet = findRoadmapBulletPhaseInContent(content, phaseNum, source);
if (scopedBullet) return scopedBullet;
const fullBullet = findRoadmapBulletPhaseInContent(fullContent, phaseNum, source);
if (fullBullet) return fullBullet;
}
return null;
} catch {
return null;
@@ -458,6 +502,16 @@ function getMilestonePhaseFilter(cwd: string, versionOverride?: string | null, p
// Exclude 999.x backlog phases from milestone phase set. Mirrors init.cts filter.
if (pm && !/^999\b/.test(pm[1])) milestonePhaseNums.add(pm[1]);
}
// #2199: also count bullet/checkbox phase entries (`- [ ] **Phase N — name**`)
// so a bullet-house-style ROADMAP populates the milestone phase set instead of
// collapsing to a zero-count pass-all filter.
{
let bm: RegExpExecArray | null;
const scanner = new RegExp(BULLET_PHASE_LINE_PATTERN.source, 'gim');
while ((bm = scanner.exec(roadmap)) !== null) {
if (!/^999\b/.test(bm[1])) milestonePhaseNums.add(bm[1]);
}
}
} catch { /* intentionally empty */ }
if (milestonePhaseNums.size === 0) {
@@ -502,6 +556,92 @@ function getMilestonePhaseFilter(cwd: string, versionOverride?: string | null, p
return isDirInMilestone as MilestonePhaseFilter;
}
/**
* #2200: raw [start,end) offsets of the current milestone's region(s) in ROADMAP
* content, for scoping write-path mutations (phase-checkbox flip, Plans-count
* writer) so they cannot touch a backticked prose literal, a Backlog entry, or a
* same-numbered phase in a shipped milestone.
*
* Mirrors the region selection in `extractCurrentMilestone` (version detection →
* active heading → next milestone boundary → optional Phase Details section).
* Returns null when there is no versioned active milestone; callers then fall
* back to whole-content mutation (the prior behaviour).
*
* NOTE: keep the region logic here in sync with extractCurrentMilestone.
*/
function currentMilestoneRawRanges(
content: string,
cwd?: string,
): { primary: { start: number; end: number }; details: { start: number; end: number } | null } | null {
if (!cwd) return null;
let version: string | null = null;
try {
const statePath = path.join(planningDir(cwd), 'STATE.md');
const stateRaw = platformReadSync(statePath);
if (stateRaw !== null) {
const milestoneMatch = stateRaw.match(/^milestone:\s*(.+)/m);
if (milestoneMatch) version = milestoneMatch[1].trim();
}
} catch { /* ignore */ }
if (!version) {
const inProgressMatch = content.match(/(?:🚧|🔄)\s*\*\*v(\d+\.\d+)\s/);
if (inProgressMatch) version = 'v' + inProgressMatch[1];
}
if (!version) return null;
const escapedVersion = escapeRegex(version);
const sectionPattern = new RegExp(
`(^#{1,3}\\s+(?!Phase\\s+\\S).*${escapedVersion}\\b[^\\n]*)`,
'gmi',
);
const headingMatches = [...content.matchAll(sectionPattern)];
if (headingMatches.length === 0) return null;
const closedMarkerPattern = /\b(?:CLOSED|ARCHIVED|ABANDONED|SHIPPED|FAILED)\b|✅|🗄/i;
const activeMarkerPattern = /\b(?:STARTED|ACTIVE|WIP)\b|in\s+progress|🚧|🔄/i;
const isClosed = (h: string) => closedMarkerPattern.test(h) && !activeMarkerPattern.test(h);
const firstMatch = headingMatches[0];
const selected = headingMatches.find((m) => !isClosed(m[1])) || firstMatch;
const sectionStart = selected.index ?? 0;
const computeSectionEnd = (headingText: string, headingStart: number): number => {
const level = (headingText.match(/^(#{1,3})\s/) ?? ['', '#'])[1].length;
const afterHeading = headingStart + headingText.length;
for (const h of tokenizeHeadings(content)) {
if (h.offset <= headingStart) continue;
if (h.offset < afterHeading) continue;
if (h.level > level) continue;
if (/^Phase\s+\S/i.test(h.text)) continue;
if (!/v\d+\.\d+|✅|📋|🚧/i.test(h.text)) continue;
return h.offset;
}
return content.length;
};
const sectionEnd = computeSectionEnd(selected[0], sectionStart);
const selectedVersionToken = selected[1].match(
/v\d+(?:\.\d+)+(?:[-.][A-Za-z0-9]+)*/i,
)?.[0];
const detailsVersionBoundary = selectedVersionToken
? new RegExp(`${escapeRegex(selectedVersionToken)}(?![\\w.-])`, 'i')
: null;
const detailsMatch = headingMatches.find(
(m) =>
/\(Phase\s+Details\)/i.test(m[1]) &&
!isClosed(m[1]) &&
(!detailsVersionBoundary || detailsVersionBoundary.test(m[1])) &&
(m.index ?? 0) >= sectionEnd,
);
let details: { start: number; end: number } | null = null;
if (detailsMatch) {
const detailsStart = detailsMatch.index ?? 0;
details = { start: detailsStart, end: computeSectionEnd(detailsMatch[0], detailsStart) };
}
return { primary: { start: sectionStart, end: sectionEnd }, details };
}
export = {
stripShippedMilestones,
extractCurrentMilestone,
@@ -509,4 +649,5 @@ export = {
getRoadmapPhaseInternal,
getMilestoneInfo,
getMilestonePhaseFilter,
currentMilestoneRawRanges,
};

View File

@@ -27,7 +27,7 @@
"gsd-project-researcher.md": 22242,
"gsd-research-synthesizer.md": 13847,
"gsd-roadmapper.md": 22273,
"gsd-security-auditor.md": 8981,
"gsd-security-auditor.md": 9431,
"gsd-ui-auditor.md": 17249,
"gsd-ui-checker.md": 14118,
"gsd-ui-researcher.md": 19557,

View File

@@ -0,0 +1,176 @@
/**
* Regression test for #2112: gsd-tools commit --files commits the entire
* index, not the declared paths.
*
* `cmdCommit` staged exactly the files named in --files but then ran a bare
* `git commit` with no pathspec, absorbing anything else that happened to be
* staged into a commit whose message described only the named files.
*
* The fix adds `'--', ...stagedPaths` to the commit args **only when** the
* caller declared a scope (explicitFiles), and only for paths that were
* actually staged (skipped missing files are excluded to avoid #2014).
*/
const { describe, test, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const { createTempGitProject, cleanup, runGsdTools } = require('./helpers.cjs');
describe('commit --files: pathspec honors declared scope (#2112)', () => {
let tmpDir;
beforeEach(() => {
tmpDir = createTempGitProject();
});
afterEach(() => {
cleanup(tmpDir);
});
test('commit --files does not absorb unrelated staged files', () => {
// Developer stages a WIP file via git add (not via --files).
fs.writeFileSync(path.join(tmpDir, 'src-wip.txt'), 'work in progress\n');
execSync('git add src-wip.txt', { cwd: tmpDir, stdio: 'pipe' });
// GSD writes and commits a planning artifact, naming ONLY that file.
fs.writeFileSync(path.join(tmpDir, '.planning', 'PLAN.md'), '# Plan\n');
runGsdTools(
['commit', 'docs(01): add PLAN.md', '--files', '.planning/PLAN.md'],
tmpDir,
);
// The commit must contain ONLY .planning/PLAN.md.
const diffOutput = execSync('git diff HEAD~1 HEAD --name-only', {
cwd: tmpDir,
encoding: 'utf-8',
}).trim();
assert.strictEqual(
diffOutput,
'.planning/PLAN.md',
'commit --files must contain only the named files, got:\n' + diffOutput,
);
// The WIP file must still be staged, not committed.
const statusOutput = execSync('git status --porcelain', {
cwd: tmpDir,
encoding: 'utf-8',
}).trim();
assert.ok(
statusOutput.includes('A src-wip.txt') || statusOutput.includes('A\tsrc-wip.txt'),
'src-wip.txt should remain staged, not committed. Status:\n' + statusOutput,
);
});
test('commit --files with two files commits exactly those two', () => {
fs.writeFileSync(path.join(tmpDir, '.planning', 'PLAN.md'), '# Plan\n');
fs.writeFileSync(path.join(tmpDir, '.planning', 'RESEARCH.md'), '# Research\n');
runGsdTools(
['commit', 'docs: artifacts', '--files', '.planning/PLAN.md', '.planning/RESEARCH.md'],
tmpDir,
);
const diffOutput = execSync('git diff HEAD~1 HEAD --name-only', {
cwd: tmpDir,
encoding: 'utf-8',
});
const files = diffOutput.trim().split('\n').sort();
assert.deepEqual(
files,
['.planning/PLAN.md', '.planning/RESEARCH.md'],
'commit should contain exactly the two named files',
);
});
test('commit without --files still commits the entire .planning/ index (default path)', () => {
// Write a planning artifact and stage it.
fs.writeFileSync(path.join(tmpDir, '.planning', 'PLAN.md'), '# Plan\n');
execSync('git add .planning/PLAN.md', { cwd: tmpDir, stdio: 'pipe' });
// Also stage an unrelated file.
fs.writeFileSync(path.join(tmpDir, 'extra.txt'), 'extra\n');
execSync('git add extra.txt', { cwd: tmpDir, stdio: 'pipe' });
runGsdTools(['commit', 'docs: default commit'], tmpDir);
// Default path (no --files) commits everything staged.
const diffOutput = execSync('git diff HEAD~1 HEAD --name-only', {
cwd: tmpDir,
encoding: 'utf-8',
});
const files = diffOutput.trim().split('\n').sort();
assert.ok(
files.includes('.planning/PLAN.md') && files.includes('extra.txt'),
'default commit (no --files) should commit everything staged, got:\n' + files,
);
});
test('missing tracked file in --files is still not committed as deletion (#2014 guard)', () => {
// Create and commit STATE.md, then remove it from disk.
fs.writeFileSync(path.join(tmpDir, '.planning', 'STATE.md'), '# State\n');
execSync('git add .planning/STATE.md', { cwd: tmpDir, stdio: 'pipe' });
execSync('git commit -m "add STATE.md"', { cwd: tmpDir, stdio: 'pipe' });
fs.unlinkSync(path.join(tmpDir, '.planning', 'STATE.md'));
// Also create a valid file to commit.
fs.writeFileSync(path.join(tmpDir, '.planning', 'PLAN.md'), '# Plan\n');
runGsdTools(
['commit', 'docs: add plan', '--files', '.planning/PLAN.md', '.planning/STATE.md'],
tmpDir,
);
const diffOutput = execSync('git diff HEAD~1 HEAD --name-status', {
cwd: tmpDir,
encoding: 'utf-8',
});
assert.ok(
!diffOutput.includes('D\t.planning/STATE.md'),
'missing tracked file must not appear as a deletion, diff was:\n' + diffOutput,
);
assert.ok(
diffOutput.includes('.planning/PLAN.md'),
'PLAN.md should be committed',
);
});
test('commit --files with only missing files returns nothing_to_commit', () => {
// Create and commit STATE.md, then remove it from disk.
fs.writeFileSync(path.join(tmpDir, '.planning', 'STATE.md'), '# State\n');
execSync('git add .planning/STATE.md', { cwd: tmpDir, stdio: 'pipe' });
execSync('git commit -m "add STATE.md"', { cwd: tmpDir, stdio: 'pipe' });
fs.unlinkSync(path.join(tmpDir, '.planning', 'STATE.md'));
// Stage an unrelated file so the index is non-empty.
fs.writeFileSync(path.join(tmpDir, 'extra.txt'), 'extra\n');
execSync('git add extra.txt', { cwd: tmpDir, stdio: 'pipe' });
const result = runGsdTools(
['commit', 'docs: try', '--files', '.planning/STATE.md'],
tmpDir,
);
const parsed = JSON.parse(result.output);
assert.strictEqual(
parsed.committed, false,
'should not commit when all --files are missing',
);
assert.strictEqual(
parsed.reason, 'nothing_to_commit',
'should report nothing_to_commit, not absorb the index',
);
// The unrelated staged file must still be staged, not committed.
const statusOutput = execSync('git status --porcelain', {
cwd: tmpDir,
encoding: 'utf-8',
}).trim();
assert.ok(
statusOutput.includes('extra.txt'),
'extra.txt should remain staged, not absorbed into a commit',
);
});
});

View File

@@ -0,0 +1,52 @@
'use strict';
/**
* #2194… no — #2196: the /gsd-debug orchestrator misused the foreground
* session-manager Agent() spawn as a background task, then queried the returned
* agent ID via TaskOutput (which expects a task ID) — yielding "No task found
* with ID" and leaving the workflow waiting on a handoff that was never
* queryable, with no recovery.
*
* The fix makes debug.md state explicitly that the spawn is foreground/blocking,
* that an agent ID must never be passed to TaskOutput, and that a lost handoff
* must be recovered (preserve checkpoint + resume). debug.md IS the product the
* runtime loads, so this asserts the deployed text carries that contract.
*/
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const DEBUG_MD = path.join(__dirname, '..', 'gsd-core', 'workflows', 'debug.md');
describe('#2196 debug.md session-manager spawn contract', () => {
const content = fs.readFileSync(DEBUG_MD, 'utf-8');
const sectionStart = content.indexOf('Session Management');
const section = sectionStart !== -1 ? content.slice(sectionStart) : '';
test('debug.md has the Session Management section', () => {
assert.notEqual(sectionStart, -1, 'debug.md must contain the Session Management section');
});
test('the session-manager spawn is declared foreground/blocking (not backgrounded)', () => {
assert.ok(/foreground/i.test(section) && /blocking/i.test(section),
'the Agent() spawn must be declared foreground and blocking so it is not polled');
});
test('an agent ID must not be passed to TaskOutput', () => {
assert.ok(/TaskOutput/.test(section),
'the contract must mention TaskOutput by name');
assert.ok(/agent ID is NOT a task ID|agent ID is not a task ID/i.test(section),
'the contract must state an agent ID is not a task ID');
});
test('a lost handoff has a recovery path (preserve checkpoint + resume)', () => {
// Pin the CANONICAL colon form — the retired /gsd-debug hyphen syntax is
// rejected by the slash-command-namespace guard, so this must be /gsd:debug.
assert.ok(/\/gsd:debug continue \{slug\}/.test(section),
'the contract must point to /gsd:debug continue {slug} (canonical colon form) as the resume path');
assert.ok(/do not claim|do NOT claim/i.test(section),
'the contract must forbid claiming a lost-handoff session is still running');
});
});

View File

@@ -29,7 +29,7 @@
"agents/gsd-project-researcher.md": "85de7f562872ee9b",
"agents/gsd-research-synthesizer.md": "18a2e1b30ff7ae3a",
"agents/gsd-roadmapper.md": "7a8465ac6d4dd29e",
"agents/gsd-security-auditor.md": "2ce13af25179dd10",
"agents/gsd-security-auditor.md": "7730a026680cb821",
"agents/gsd-ui-auditor.md": "f777f9c7bf62788c",
"agents/gsd-ui-checker.md": "216af34b01e277aa",
"agents/gsd-ui-researcher.md": "5f86de1decbd16d2",
@@ -38,7 +38,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "ea841e2865248e74",
"gsd-core/bin/gsd-tools.cjs": "e8ccbbe8cefc2e9a",
"gsd-core/bin/gsd-tools.cjs": "600e9ed0cb6ed7bb",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -198,7 +198,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "8cc2e884a021b79b",
"gsd-core/workflows/add-phase.md": "f82afa2f93be19fa",
"gsd-core/workflows/add-tests.md": "f695210f67d2635e",
"gsd-core/workflows/add-tests.md": "ad6d49634e04ca7c",
"gsd-core/workflows/add-todo.md": "de1ac76acfcc0133",
"gsd-core/workflows/ai-integration-phase.md": "22f5466085c47c00",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
@@ -211,7 +211,7 @@
"gsd-core/workflows/code-review-fix.md": "60640e633b0a124b",
"gsd-core/workflows/code-review.md": "5c40505c01871153",
"gsd-core/workflows/complete-milestone.md": "aaf272074acec69d",
"gsd-core/workflows/debug.md": "68f1ddc74886ebe5",
"gsd-core/workflows/debug.md": "af2d1ae03b24fc71",
"gsd-core/workflows/diagnose-issues.md": "c8c41993c277363c",
"gsd-core/workflows/discovery-phase.md": "3de990caffdde4f8",
"gsd-core/workflows/discuss-phase-assumptions.md": "8581fd77def7ce84",
@@ -287,7 +287,7 @@
"gsd-core/workflows/resume-project.md": "98e2cf8908e73a52",
"gsd-core/workflows/review.md": "43c052bba1cbd4ac",
"gsd-core/workflows/scan.md": "a7fecd67e5cd655f",
"gsd-core/workflows/secure-phase.md": "96b199dfac00e60f",
"gsd-core/workflows/secure-phase.md": "52ddc46233e8fa66",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "6d25100a9de15b31",
"gsd-core/workflows/settings-integrations.md": "4d3001ad2b5dad8f",
@@ -296,7 +296,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "0f842a609851a401",
"gsd-core/workflows/sketch.md": "314b7d323c6b57eb",
"gsd-core/workflows/smart-entry.md": "3ce5b6228238fdb6",
"gsd-core/workflows/spec-phase.md": "11c9e6b01bd2880a",
"gsd-core/workflows/spec-phase.md": "978462218855c213",
"gsd-core/workflows/spike-wrap-up.md": "8db41edc87cb7886",
"gsd-core/workflows/spike.md": "dd8d139947a8fb31",
"gsd-core/workflows/stats.md": "b64bda5c0a1a06fb",

View File

@@ -29,7 +29,7 @@
"agents/gsd-project-researcher.md": "4531b7cc8f5e5f7d",
"agents/gsd-research-synthesizer.md": "4a4f68e6c75b133a",
"agents/gsd-roadmapper.md": "bb2f57695dbab32c",
"agents/gsd-security-auditor.md": "f22374cc1db28dca",
"agents/gsd-security-auditor.md": "4db2c41181ad1f97",
"agents/gsd-ui-auditor.md": "dcd5712e6b160a53",
"agents/gsd-ui-checker.md": "5c27ec0d88ef87c2",
"agents/gsd-ui-researcher.md": "bcc591f2dfebdb60",
@@ -109,7 +109,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
"gsd-core/bin/gsd-tools.cjs": "6a7616125440c8b1",
"gsd-core/bin/gsd-tools.cjs": "37fb6081fcb18038",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -269,7 +269,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "1bc7377b105194fc",
"gsd-core/workflows/add-phase.md": "46e0551ffdd8ce1a",
"gsd-core/workflows/add-tests.md": "2c1da65d41dc12d2",
"gsd-core/workflows/add-tests.md": "24b0d8157a9ccb8b",
"gsd-core/workflows/add-todo.md": "cc0efe270004c8fb",
"gsd-core/workflows/ai-integration-phase.md": "40c217869a06981f",
"gsd-core/workflows/analyze-dependencies.md": "52942af10f140717",
@@ -282,7 +282,7 @@
"gsd-core/workflows/code-review-fix.md": "2e113d1f4350a075",
"gsd-core/workflows/code-review.md": "334c90c401f291f8",
"gsd-core/workflows/complete-milestone.md": "c1f91b77f4ace7f2",
"gsd-core/workflows/debug.md": "849d7e5b9c12dcae",
"gsd-core/workflows/debug.md": "c581e89aa9d9d71e",
"gsd-core/workflows/diagnose-issues.md": "6cc3900891dfb927",
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
"gsd-core/workflows/discuss-phase-assumptions.md": "f5b765d33eba4f88",
@@ -358,7 +358,7 @@
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
"gsd-core/workflows/scan.md": "003883d71c37da7d",
"gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62",
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
@@ -367,7 +367,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "5f5ebb6a80d610c6",
"gsd-core/workflows/sketch.md": "8319cedf3f93fc35",
"gsd-core/workflows/smart-entry.md": "449238eb94abe187",
"gsd-core/workflows/spec-phase.md": "8c480bd91f3cef6f",
"gsd-core/workflows/spec-phase.md": "fa8669b75392cf6c",
"gsd-core/workflows/spike-wrap-up.md": "8467dadf2af758e6",
"gsd-core/workflows/spike.md": "7727c07bb221c8cc",
"gsd-core/workflows/stats.md": "76a42cbeaf6007c2",

View File

@@ -28,7 +28,7 @@
"agents/gsd-project-researcher.md": "d7f355894519f9fe",
"agents/gsd-research-synthesizer.md": "1c738df9932d325a",
"agents/gsd-roadmapper.md": "453e9471ad27c7ea",
"agents/gsd-security-auditor.md": "45bd98918cd3a004",
"agents/gsd-security-auditor.md": "4551e5f621cf13ff",
"agents/gsd-ui-auditor.md": "a0b09cc8e4645956",
"agents/gsd-ui-checker.md": "33ffdc73d2105a24",
"agents/gsd-ui-researcher.md": "4b36852c839f1134",
@@ -108,7 +108,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
"gsd-core/bin/gsd-tools.cjs": "6a7616125440c8b1",
"gsd-core/bin/gsd-tools.cjs": "37fb6081fcb18038",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -268,7 +268,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "8d05775f367d1e48",
"gsd-core/workflows/add-phase.md": "7285e6e8894a41c5",
"gsd-core/workflows/add-tests.md": "8012263b2d27b83e",
"gsd-core/workflows/add-tests.md": "ffbaf55a25f455a2",
"gsd-core/workflows/add-todo.md": "1fc850476cd8340d",
"gsd-core/workflows/ai-integration-phase.md": "3503f52a7356caf0",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
@@ -281,7 +281,7 @@
"gsd-core/workflows/code-review-fix.md": "78c716068ccdf820",
"gsd-core/workflows/code-review.md": "2d21452eb0449fdd",
"gsd-core/workflows/complete-milestone.md": "9962377cddee50d7",
"gsd-core/workflows/debug.md": "3354c726abbfd75b",
"gsd-core/workflows/debug.md": "18c97b804f2dd5dd",
"gsd-core/workflows/diagnose-issues.md": "db6a599674efbc4d",
"gsd-core/workflows/discovery-phase.md": "a20dfb32adec51de",
"gsd-core/workflows/discuss-phase-assumptions.md": "35a3b2d1285565d8",
@@ -357,7 +357,7 @@
"gsd-core/workflows/resume-project.md": "af9761bcec0f6fe9",
"gsd-core/workflows/review.md": "eec3a15bebb7fcf0",
"gsd-core/workflows/scan.md": "75c670d08cee8680",
"gsd-core/workflows/secure-phase.md": "64ec4d06ca85720a",
"gsd-core/workflows/secure-phase.md": "8030d2b2a5bfdf07",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "94d61da368e9f85b",
"gsd-core/workflows/settings-integrations.md": "dfe3672c4fabf139",
@@ -366,7 +366,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "d52a5462bafda830",
"gsd-core/workflows/sketch.md": "dbe6acc4d976060c",
"gsd-core/workflows/smart-entry.md": "1850447c045f36d8",
"gsd-core/workflows/spec-phase.md": "e03fa9f1a44613dc",
"gsd-core/workflows/spec-phase.md": "ec4d7e4e83bb001d",
"gsd-core/workflows/spike-wrap-up.md": "4bdfaf9d05c63e7c",
"gsd-core/workflows/spike.md": "1571a05457beea8d",
"gsd-core/workflows/stats.md": "3953356f476b5053",

View File

@@ -28,7 +28,7 @@
"agents/gsd-project-researcher.md": "f468e96f8339d1e0",
"agents/gsd-research-synthesizer.md": "7be02e47f4fd901b",
"agents/gsd-roadmapper.md": "8a7f1f1256a6aed5",
"agents/gsd-security-auditor.md": "4f9fc3f654af4944",
"agents/gsd-security-auditor.md": "757f72894f79f11b",
"agents/gsd-ui-auditor.md": "40c0dcc15bcfb9fb",
"agents/gsd-ui-checker.md": "8126405043f99cb6",
"agents/gsd-ui-researcher.md": "9e3ac030767167e0",
@@ -37,7 +37,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
"gsd-core/bin/gsd-tools.cjs": "6a7616125440c8b1",
"gsd-core/bin/gsd-tools.cjs": "37fb6081fcb18038",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -197,7 +197,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "485c4b6673ddf832",
"gsd-core/workflows/add-phase.md": "0d90083e9c17bec1",
"gsd-core/workflows/add-tests.md": "330b3e7d969fc2d1",
"gsd-core/workflows/add-tests.md": "c4b133a102d36a27",
"gsd-core/workflows/add-todo.md": "8488f10f56ac24ca",
"gsd-core/workflows/ai-integration-phase.md": "a898d99b8d844215",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
@@ -210,7 +210,7 @@
"gsd-core/workflows/code-review-fix.md": "722416b71b31c5fc",
"gsd-core/workflows/code-review.md": "506412604f767adc",
"gsd-core/workflows/complete-milestone.md": "dcf1182398efb1ca",
"gsd-core/workflows/debug.md": "a9397fd35cca2240",
"gsd-core/workflows/debug.md": "7183935f8e145a01",
"gsd-core/workflows/diagnose-issues.md": "75ffc381ac3059ff",
"gsd-core/workflows/discovery-phase.md": "6161c60d752d0058",
"gsd-core/workflows/discuss-phase-assumptions.md": "a8cd1db094fefd35",
@@ -286,7 +286,7 @@
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
"gsd-core/workflows/review.md": "b0baf1dafebe3821",
"gsd-core/workflows/scan.md": "47371c2073d6c0be",
"gsd-core/workflows/secure-phase.md": "59d3c50aba8c9a6c",
"gsd-core/workflows/secure-phase.md": "d6ac1f4db6a5da75",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "339def28c34b0797",
"gsd-core/workflows/settings-integrations.md": "53649313d20694ae",
@@ -295,7 +295,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "121ed4b8127abf04",
"gsd-core/workflows/sketch.md": "737c0492686fea2d",
"gsd-core/workflows/smart-entry.md": "ad20cf74ae2e8291",
"gsd-core/workflows/spec-phase.md": "82150c52a9077602",
"gsd-core/workflows/spec-phase.md": "d935df4ab9dd4f7c",
"gsd-core/workflows/spike-wrap-up.md": "e2704024992b3fee",
"gsd-core/workflows/spike.md": "6dce83a2c5e7e49c",
"gsd-core/workflows/stats.md": "e7852c5d8f2b05a9",

View File

@@ -32,7 +32,7 @@
"agents/gsd-project-researcher.md": "049f816c6caa4316",
"agents/gsd-research-synthesizer.md": "2f7dcbff50371d4c",
"agents/gsd-roadmapper.md": "bbb23d3097911516",
"agents/gsd-security-auditor.md": "c35c8ec2f85b331f",
"agents/gsd-security-auditor.md": "38935b2d0c532c29",
"agents/gsd-ui-auditor.md": "9338efa31b9b7b99",
"agents/gsd-ui-checker.md": "151d12b4e007cbbf",
"agents/gsd-ui-researcher.md": "1bb303f3a3c4dfc9",
@@ -41,7 +41,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "476aa24e8c4f03cf",
"gsd-core/bin/gsd-tools.cjs": "93273f4eda2750d2",
"gsd-core/bin/gsd-tools.cjs": "a84914f7cab74332",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -201,7 +201,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "6c4393113fa9d209",
"gsd-core/workflows/add-phase.md": "cac9b86f66ab26f5",
"gsd-core/workflows/add-tests.md": "dd80832a428afb45",
"gsd-core/workflows/add-tests.md": "0354376bffdc0080",
"gsd-core/workflows/add-todo.md": "b71e80304fe484eb",
"gsd-core/workflows/ai-integration-phase.md": "5159c6bdf102f74b",
"gsd-core/workflows/analyze-dependencies.md": "52942af10f140717",
@@ -214,7 +214,7 @@
"gsd-core/workflows/code-review-fix.md": "e4549af672e74e6f",
"gsd-core/workflows/code-review.md": "a65e3e869508f89e",
"gsd-core/workflows/complete-milestone.md": "c0808127038a8f86",
"gsd-core/workflows/debug.md": "f42e8e3cbc298694",
"gsd-core/workflows/debug.md": "337fc2076e7fc449",
"gsd-core/workflows/diagnose-issues.md": "e616d0d730328d68",
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
"gsd-core/workflows/discuss-phase-assumptions.md": "9efcb2ef6a9245b3",
@@ -290,7 +290,7 @@
"gsd-core/workflows/resume-project.md": "e23981178fa37b3d",
"gsd-core/workflows/review.md": "6c689f4ff8146d28",
"gsd-core/workflows/scan.md": "dfd92717caea0ce7",
"gsd-core/workflows/secure-phase.md": "cf78183f06a02582",
"gsd-core/workflows/secure-phase.md": "00de56d6d993bb2c",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "69f3a19bf2c61160",
"gsd-core/workflows/settings-integrations.md": "76eee76d6eb57657",
@@ -299,7 +299,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "1f44789553180d84",
"gsd-core/workflows/sketch.md": "2226779b6003a71c",
"gsd-core/workflows/smart-entry.md": "9a64f43927641ca4",
"gsd-core/workflows/spec-phase.md": "55d727dbf400ed9a",
"gsd-core/workflows/spec-phase.md": "641208ed90684364",
"gsd-core/workflows/spike-wrap-up.md": "846958e0012a0899",
"gsd-core/workflows/spike.md": "868b15cdf229cc43",
"gsd-core/workflows/stats.md": "f9241bb65770c5a4",

View File

@@ -29,7 +29,7 @@
"agents/gsd-project-researcher.md": "e43c59f7f1f2f37a",
"agents/gsd-research-synthesizer.md": "87955470c3c129b2",
"agents/gsd-roadmapper.md": "20b69eff61a7a9fa",
"agents/gsd-security-auditor.md": "d3b8f44034a76c9d",
"agents/gsd-security-auditor.md": "bcd9c4859eb5b448",
"agents/gsd-ui-auditor.md": "a26bbc733817959b",
"agents/gsd-ui-checker.md": "25822359044cd708",
"agents/gsd-ui-researcher.md": "e37d9f53ade25d1f",
@@ -109,7 +109,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
"gsd-core/bin/gsd-tools.cjs": "6a7616125440c8b1",
"gsd-core/bin/gsd-tools.cjs": "37fb6081fcb18038",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -269,7 +269,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "1bc7377b105194fc",
"gsd-core/workflows/add-phase.md": "46e0551ffdd8ce1a",
"gsd-core/workflows/add-tests.md": "2c1da65d41dc12d2",
"gsd-core/workflows/add-tests.md": "24b0d8157a9ccb8b",
"gsd-core/workflows/add-todo.md": "cc0efe270004c8fb",
"gsd-core/workflows/ai-integration-phase.md": "40c217869a06981f",
"gsd-core/workflows/analyze-dependencies.md": "52942af10f140717",
@@ -282,7 +282,7 @@
"gsd-core/workflows/code-review-fix.md": "2e113d1f4350a075",
"gsd-core/workflows/code-review.md": "334c90c401f291f8",
"gsd-core/workflows/complete-milestone.md": "c1f91b77f4ace7f2",
"gsd-core/workflows/debug.md": "849d7e5b9c12dcae",
"gsd-core/workflows/debug.md": "c581e89aa9d9d71e",
"gsd-core/workflows/diagnose-issues.md": "77d98ac07c4a26ff",
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
"gsd-core/workflows/discuss-phase-assumptions.md": "f5b765d33eba4f88",
@@ -358,7 +358,7 @@
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
"gsd-core/workflows/scan.md": "003883d71c37da7d",
"gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62",
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
@@ -367,7 +367,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "2aba89ecd8f41a0d",
"gsd-core/workflows/sketch.md": "5eedd93f9a5b49d5",
"gsd-core/workflows/smart-entry.md": "449238eb94abe187",
"gsd-core/workflows/spec-phase.md": "8c480bd91f3cef6f",
"gsd-core/workflows/spec-phase.md": "fa8669b75392cf6c",
"gsd-core/workflows/spike-wrap-up.md": "2b004744c52e565c",
"gsd-core/workflows/spike.md": "24a0fee1447b6f0a",
"gsd-core/workflows/stats.md": "76a42cbeaf6007c2",

View File

@@ -128,8 +128,8 @@
"agents/gsd-research-synthesizer.toml": "053616d4941a1458",
"agents/gsd-roadmapper.md": "e15d82d7781dbff3",
"agents/gsd-roadmapper.toml": "0b17f618eafe9be8",
"agents/gsd-security-auditor.md": "a4008a6b1d01833b",
"agents/gsd-security-auditor.toml": "42f529475bd28c22",
"agents/gsd-security-auditor.md": "d7b62f9a93cfbf78",
"agents/gsd-security-auditor.toml": "e991aa6e7c70f813",
"agents/gsd-ui-auditor.md": "abbf560bc8d5069c",
"agents/gsd-ui-auditor.toml": "5e9dd62a12a16a1e",
"agents/gsd-ui-checker.md": "b65d350a4e0564e9",
@@ -140,11 +140,11 @@
"agents/gsd-user-profiler.toml": "b9c244bb8fbf8140",
"agents/gsd-verifier.md": "4ac4b860e2504374",
"agents/gsd-verifier.toml": "8ed9fb961409e894",
"config.toml": "fa48d84b92174890",
"config.toml": "b5f627b42f060910",
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
"gsd-core/bin/gsd-tools.cjs": "6a7616125440c8b1",
"gsd-core/bin/gsd-tools.cjs": "37fb6081fcb18038",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -304,7 +304,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "62431b27424ff1ff",
"gsd-core/workflows/add-phase.md": "27f282bf5b8af59d",
"gsd-core/workflows/add-tests.md": "995918665fc78d39",
"gsd-core/workflows/add-tests.md": "5cdb15508c4b6075",
"gsd-core/workflows/add-todo.md": "097e171f797737df",
"gsd-core/workflows/ai-integration-phase.md": "1dfa15d8f28c022d",
"gsd-core/workflows/analyze-dependencies.md": "f799abc00907377f",
@@ -317,7 +317,7 @@
"gsd-core/workflows/code-review-fix.md": "ae7f9c6b39a23c12",
"gsd-core/workflows/code-review.md": "eadada9e0a89adf2",
"gsd-core/workflows/complete-milestone.md": "017df7443bd08da5",
"gsd-core/workflows/debug.md": "cc7b2d2fd4307a78",
"gsd-core/workflows/debug.md": "7c3b407470762585",
"gsd-core/workflows/diagnose-issues.md": "e38bb21d06dff077",
"gsd-core/workflows/discovery-phase.md": "71a4b78ff876a854",
"gsd-core/workflows/discuss-phase-assumptions.md": "0d936ec25299917c",
@@ -393,7 +393,7 @@
"gsd-core/workflows/resume-project.md": "9965f87eb278f7f8",
"gsd-core/workflows/review.md": "5faef3f4feb45c99",
"gsd-core/workflows/scan.md": "1a3caa5d724d39e9",
"gsd-core/workflows/secure-phase.md": "db91810d16964b1e",
"gsd-core/workflows/secure-phase.md": "ab387a4bca381c18",
"gsd-core/workflows/session-report.md": "dd8fa011c9394075",
"gsd-core/workflows/settings-advanced.md": "2431433811616f76",
"gsd-core/workflows/settings-integrations.md": "77730321d3d6d317",
@@ -402,7 +402,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "07724a390fbb43f6",
"gsd-core/workflows/sketch.md": "576f300dfdde1b7d",
"gsd-core/workflows/smart-entry.md": "6f686195ae531b03",
"gsd-core/workflows/spec-phase.md": "deaec2464a6560ec",
"gsd-core/workflows/spec-phase.md": "3dd8ae6137492d80",
"gsd-core/workflows/spike-wrap-up.md": "ace265c114298376",
"gsd-core/workflows/spike.md": "f42a9983eb9c89a1",
"gsd-core/workflows/stats.md": "6dfc500555849d74",

View File

@@ -29,7 +29,7 @@
"agents/gsd-project-researcher.agent.md": "d73bdbe986ffa8a6",
"agents/gsd-research-synthesizer.agent.md": "f03eed4aa89e47c5",
"agents/gsd-roadmapper.agent.md": "322048cf8ddcb4e5",
"agents/gsd-security-auditor.agent.md": "6f6a88b35dc2a24b",
"agents/gsd-security-auditor.agent.md": "464cfbe9aafcd5af",
"agents/gsd-ui-auditor.agent.md": "92f50549e84ef482",
"agents/gsd-ui-checker.agent.md": "47d8cf3486009e11",
"agents/gsd-ui-researcher.agent.md": "0746daeb54f83008",
@@ -39,7 +39,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "ea841e2865248e74",
"gsd-core/bin/gsd-tools.cjs": "e8ccbbe8cefc2e9a",
"gsd-core/bin/gsd-tools.cjs": "600e9ed0cb6ed7bb",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -199,7 +199,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "98f2dbb7c7cc93c2",
"gsd-core/workflows/add-phase.md": "34e6dd38c25b5b61",
"gsd-core/workflows/add-tests.md": "e88bc8bc3416bfdd",
"gsd-core/workflows/add-tests.md": "7f5da4d2a9fa8a1a",
"gsd-core/workflows/add-todo.md": "10253591112b7d06",
"gsd-core/workflows/ai-integration-phase.md": "ef0c2474cee76b00",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
@@ -212,7 +212,7 @@
"gsd-core/workflows/code-review-fix.md": "fda53892ae4b17fc",
"gsd-core/workflows/code-review.md": "f1c045ec4d33abc8",
"gsd-core/workflows/complete-milestone.md": "470cf39261400ee2",
"gsd-core/workflows/debug.md": "36cb536be452d79e",
"gsd-core/workflows/debug.md": "505ed67d48b1fa9d",
"gsd-core/workflows/diagnose-issues.md": "42acbe2a43fc886e",
"gsd-core/workflows/discovery-phase.md": "8e99da61fb2b7074",
"gsd-core/workflows/discuss-phase-assumptions.md": "ab0c432b84038681",
@@ -288,7 +288,7 @@
"gsd-core/workflows/resume-project.md": "40db7f350f5866d8",
"gsd-core/workflows/review.md": "4b649f31865a1785",
"gsd-core/workflows/scan.md": "dcc2f76d0850e2fb",
"gsd-core/workflows/secure-phase.md": "d87bd706f85bcad6",
"gsd-core/workflows/secure-phase.md": "9bec6635ee1cbaed",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "230a658de9c017a6",
"gsd-core/workflows/settings-integrations.md": "a1d146d6bfd14db5",
@@ -297,7 +297,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "f2590cb6ddbfad94",
"gsd-core/workflows/sketch.md": "e2063966439af8c1",
"gsd-core/workflows/smart-entry.md": "6c707b959a41900b",
"gsd-core/workflows/spec-phase.md": "858184a1966d4d79",
"gsd-core/workflows/spec-phase.md": "44bd9c50b42e73fb",
"gsd-core/workflows/spike-wrap-up.md": "a34d1e9ec15ae904",
"gsd-core/workflows/spike.md": "89f299c07ac0edd2",
"gsd-core/workflows/stats.md": "1db01bd96aa570fe",

View File

@@ -29,7 +29,7 @@
"agents/gsd-project-researcher.md": "beeac940d3a10e76",
"agents/gsd-research-synthesizer.md": "6315f016d55176f4",
"agents/gsd-roadmapper.md": "d28e7d4bac46dde2",
"agents/gsd-security-auditor.md": "5ff44ee432387d93",
"agents/gsd-security-auditor.md": "1e6a10833f556e4c",
"agents/gsd-ui-auditor.md": "d824acc3b2a18c53",
"agents/gsd-ui-checker.md": "c6c24e8066470830",
"agents/gsd-ui-researcher.md": "0f5be5e55501f7e8",
@@ -109,7 +109,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "2525f1ae8b086828",
"gsd-core/bin/gsd-tools.cjs": "3fa86b509ea5c8fc",
"gsd-core/bin/gsd-tools.cjs": "db16ded31dd6eecf",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -269,7 +269,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "2926457ed07b4500",
"gsd-core/workflows/add-phase.md": "0d90083e9c17bec1",
"gsd-core/workflows/add-tests.md": "1837b9d2affa6913",
"gsd-core/workflows/add-tests.md": "37f031c4b3236a32",
"gsd-core/workflows/add-todo.md": "d483ebe8edcf193a",
"gsd-core/workflows/ai-integration-phase.md": "b79f320d59a68773",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
@@ -282,7 +282,7 @@
"gsd-core/workflows/code-review-fix.md": "c57af378033b1b58",
"gsd-core/workflows/code-review.md": "32c37bcbec8b8698",
"gsd-core/workflows/complete-milestone.md": "1400a4856f592f3e",
"gsd-core/workflows/debug.md": "a73d8018986131ba",
"gsd-core/workflows/debug.md": "c02dec84c8a346b9",
"gsd-core/workflows/diagnose-issues.md": "cd582747131726e3",
"gsd-core/workflows/discovery-phase.md": "7dcf150998559c11",
"gsd-core/workflows/discuss-phase-assumptions.md": "c25c6a6c633d71b6",
@@ -358,7 +358,7 @@
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
"gsd-core/workflows/review.md": "3ba8bb85c8ede5b8",
"gsd-core/workflows/scan.md": "47371c2073d6c0be",
"gsd-core/workflows/secure-phase.md": "c55975672c4e1895",
"gsd-core/workflows/secure-phase.md": "3063b0b6f7b56d46",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "bf8ea69c8f7ae019",
"gsd-core/workflows/settings-integrations.md": "166ca51b1f33c2a3",
@@ -367,7 +367,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "5be73b7bdf96b539",
"gsd-core/workflows/sketch.md": "373bc0d83368a411",
"gsd-core/workflows/smart-entry.md": "c8fc316358cdcd7b",
"gsd-core/workflows/spec-phase.md": "d6e23579376c9e91",
"gsd-core/workflows/spec-phase.md": "239b6e6cbddaf8c2",
"gsd-core/workflows/spike-wrap-up.md": "4a3bf1058691e55d",
"gsd-core/workflows/spike.md": "f741bead24d5669a",
"gsd-core/workflows/stats.md": "e7852c5d8f2b05a9",

View File

@@ -29,7 +29,7 @@
"agents/gsd-project-researcher.md": "425a7df7f37a5c06",
"agents/gsd-research-synthesizer.md": "9d31c87fc2c87ffa",
"agents/gsd-roadmapper.md": "64dce5d5f9fa5654",
"agents/gsd-security-auditor.md": "e4d35ada4ea67d7f",
"agents/gsd-security-auditor.md": "ed330ecbd9dbc377",
"agents/gsd-ui-auditor.md": "86797e85f718dfac",
"agents/gsd-ui-checker.md": "cfc8a3bac0a0ef5b",
"agents/gsd-ui-researcher.md": "8799b6013e06ae49",
@@ -38,7 +38,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "3a3409215044af9f",
"gsd-core/bin/gsd-tools.cjs": "e979f092ce2b15ee",
"gsd-core/bin/gsd-tools.cjs": "b3f927ee4b4c4711",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -198,7 +198,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "79a453a2270483fb",
"gsd-core/workflows/add-phase.md": "b4c4b32c111a116a",
"gsd-core/workflows/add-tests.md": "63390575b418e270",
"gsd-core/workflows/add-tests.md": "02f9e11ad2371c00",
"gsd-core/workflows/add-todo.md": "5fe3ddc3227e0e22",
"gsd-core/workflows/ai-integration-phase.md": "ddab2912d025db65",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
@@ -211,7 +211,7 @@
"gsd-core/workflows/code-review-fix.md": "e829d3baf9901b54",
"gsd-core/workflows/code-review.md": "50a05ab8957bd05f",
"gsd-core/workflows/complete-milestone.md": "f1866541148dc291",
"gsd-core/workflows/debug.md": "15cf6999e85dcc8c",
"gsd-core/workflows/debug.md": "639348c8657e7147",
"gsd-core/workflows/diagnose-issues.md": "16f2d2a85335641f",
"gsd-core/workflows/discovery-phase.md": "6161c60d752d0058",
"gsd-core/workflows/discuss-phase-assumptions.md": "3a1e215890d2b3f4",
@@ -287,7 +287,7 @@
"gsd-core/workflows/resume-project.md": "a0443839f1f83c2d",
"gsd-core/workflows/review.md": "761dd1ae5be40613",
"gsd-core/workflows/scan.md": "b28f65d88c522767",
"gsd-core/workflows/secure-phase.md": "f2957d4b88fb3746",
"gsd-core/workflows/secure-phase.md": "a503dc469fd7a252",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "49be159144d7f426",
"gsd-core/workflows/settings-integrations.md": "1dce76db0aca08a5",
@@ -296,7 +296,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "f1ece50ac65ea281",
"gsd-core/workflows/sketch.md": "d5887983e62b574a",
"gsd-core/workflows/smart-entry.md": "47f5c5e8608e5f7a",
"gsd-core/workflows/spec-phase.md": "6c8835fc65ea1cb8",
"gsd-core/workflows/spec-phase.md": "6ace28390408475f",
"gsd-core/workflows/spike-wrap-up.md": "367325e6d567b904",
"gsd-core/workflows/spike.md": "cdacb2887a321e11",
"gsd-core/workflows/stats.md": "49a991d0d3905a56",

View File

@@ -29,7 +29,7 @@
"agents/gsd-project-researcher.md": "60573a38d3dfd9fe",
"agents/gsd-research-synthesizer.md": "1f7cd286c5783c86",
"agents/gsd-roadmapper.md": "277e0a3252553ab7",
"agents/gsd-security-auditor.md": "0113435969e869c4",
"agents/gsd-security-auditor.md": "30b1f87cdfc05de1",
"agents/gsd-ui-auditor.md": "9b988b95d28e56ed",
"agents/gsd-ui-checker.md": "e078ea5a07313976",
"agents/gsd-ui-researcher.md": "cc9578c4f686d926",
@@ -109,7 +109,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
"gsd-core/bin/gsd-tools.cjs": "6a7616125440c8b1",
"gsd-core/bin/gsd-tools.cjs": "37fb6081fcb18038",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -269,7 +269,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "485c4b6673ddf832",
"gsd-core/workflows/add-phase.md": "0d90083e9c17bec1",
"gsd-core/workflows/add-tests.md": "ecb959f321820e5e",
"gsd-core/workflows/add-tests.md": "6e54f3d71a202671",
"gsd-core/workflows/add-todo.md": "5082510bf0449204",
"gsd-core/workflows/ai-integration-phase.md": "c85c6afdc64f0da6",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
@@ -282,7 +282,7 @@
"gsd-core/workflows/code-review-fix.md": "722416b71b31c5fc",
"gsd-core/workflows/code-review.md": "506412604f767adc",
"gsd-core/workflows/complete-milestone.md": "59753bf44d4300da",
"gsd-core/workflows/debug.md": "a72d830ac3df74aa",
"gsd-core/workflows/debug.md": "28c964bceb6f03af",
"gsd-core/workflows/diagnose-issues.md": "210b5b313e8a559a",
"gsd-core/workflows/discovery-phase.md": "ca7b2be46e59e862",
"gsd-core/workflows/discuss-phase-assumptions.md": "3ef1df313e715387",
@@ -358,7 +358,7 @@
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
"gsd-core/workflows/review.md": "f8109b9ec1f56962",
"gsd-core/workflows/scan.md": "47371c2073d6c0be",
"gsd-core/workflows/secure-phase.md": "e8855104c1e0417c",
"gsd-core/workflows/secure-phase.md": "4977cf9e0462745b",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "edd858cd6cfddaf1",
"gsd-core/workflows/settings-integrations.md": "3ea8095d5fad891b",
@@ -367,7 +367,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "888c0548e63197b3",
"gsd-core/workflows/sketch.md": "34a0c10fa56af7ea",
"gsd-core/workflows/smart-entry.md": "7ffe4fdb93935400",
"gsd-core/workflows/spec-phase.md": "c231f8eb723e7604",
"gsd-core/workflows/spec-phase.md": "a5f3de15eeb7ce72",
"gsd-core/workflows/spike-wrap-up.md": "458230d4d7c44b09",
"gsd-core/workflows/spike.md": "8e63427fc146b45c",
"gsd-core/workflows/stats.md": "e7852c5d8f2b05a9",

View File

@@ -30,7 +30,7 @@
".kimi/hooks/managed-hooks-registry.cjs": "08ec2585a3f8f132",
".kimi/package.json": "dbf8353f77358bc1",
"agents/gsd.md": "60fee7782ae4f2c6",
"agents/gsd.yaml": "253a23ddda06c6c2",
"agents/gsd.yaml": "b5f16c9fcf92cbff",
"agents/subagents/gsd-advisor-researcher.md": "81bdc6cbd8fcde40",
"agents/subagents/gsd-advisor-researcher.yaml": "662ced4837207406",
"agents/subagents/gsd-ai-researcher.md": "f4cd2f17d2c3e35f",
@@ -87,8 +87,8 @@
"agents/subagents/gsd-research-synthesizer.yaml": "898104ab3bb0b81a",
"agents/subagents/gsd-roadmapper.md": "62359d6876022b48",
"agents/subagents/gsd-roadmapper.yaml": "679772cb14f3a015",
"agents/subagents/gsd-security-auditor.md": "e621d1ee6b7aee6c",
"agents/subagents/gsd-security-auditor.yaml": "fe8a4345cc13571d",
"agents/subagents/gsd-security-auditor.md": "c6108af63b5f481d",
"agents/subagents/gsd-security-auditor.yaml": "924783f1da6777b4",
"agents/subagents/gsd-ui-auditor.md": "e4a319070959ebbc",
"agents/subagents/gsd-ui-auditor.yaml": "3fc98c1d9e8f10fd",
"agents/subagents/gsd-ui-checker.md": "07cd4e382ca55994",
@@ -102,7 +102,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
"gsd-core/bin/gsd-tools.cjs": "6a7616125440c8b1",
"gsd-core/bin/gsd-tools.cjs": "37fb6081fcb18038",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -262,7 +262,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "1bc7377b105194fc",
"gsd-core/workflows/add-phase.md": "46e0551ffdd8ce1a",
"gsd-core/workflows/add-tests.md": "2c1da65d41dc12d2",
"gsd-core/workflows/add-tests.md": "24b0d8157a9ccb8b",
"gsd-core/workflows/add-todo.md": "cc0efe270004c8fb",
"gsd-core/workflows/ai-integration-phase.md": "40c217869a06981f",
"gsd-core/workflows/analyze-dependencies.md": "52942af10f140717",
@@ -275,7 +275,7 @@
"gsd-core/workflows/code-review-fix.md": "2e113d1f4350a075",
"gsd-core/workflows/code-review.md": "334c90c401f291f8",
"gsd-core/workflows/complete-milestone.md": "c1f91b77f4ace7f2",
"gsd-core/workflows/debug.md": "849d7e5b9c12dcae",
"gsd-core/workflows/debug.md": "c581e89aa9d9d71e",
"gsd-core/workflows/diagnose-issues.md": "67c058fc7ae6026b",
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
"gsd-core/workflows/discuss-phase-assumptions.md": "f5b765d33eba4f88",
@@ -351,7 +351,7 @@
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
"gsd-core/workflows/scan.md": "003883d71c37da7d",
"gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62",
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
@@ -360,7 +360,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "b767a1d3db129a8a",
"gsd-core/workflows/sketch.md": "88cfdf4edcf222ab",
"gsd-core/workflows/smart-entry.md": "449238eb94abe187",
"gsd-core/workflows/spec-phase.md": "8c480bd91f3cef6f",
"gsd-core/workflows/spec-phase.md": "fa8669b75392cf6c",
"gsd-core/workflows/spike-wrap-up.md": "da0dcb252c54fa82",
"gsd-core/workflows/spike.md": "7813e120577282d4",
"gsd-core/workflows/stats.md": "76a42cbeaf6007c2",

View File

@@ -29,7 +29,7 @@
"agents/gsd-project-researcher.md": "dae210ae0b3c6e2b",
"agents/gsd-research-synthesizer.md": "e02c6ad5d1b74171",
"agents/gsd-roadmapper.md": "1658a40b20d8b575",
"agents/gsd-security-auditor.md": "e36e436c0d5c26d5",
"agents/gsd-security-auditor.md": "64aa435793af52ec",
"agents/gsd-ui-auditor.md": "e810012e685b2466",
"agents/gsd-ui-checker.md": "4f88fd4c9d4c56a3",
"agents/gsd-ui-researcher.md": "ecb617901cb7ad06",
@@ -109,7 +109,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
"gsd-core/bin/gsd-tools.cjs": "6a7616125440c8b1",
"gsd-core/bin/gsd-tools.cjs": "37fb6081fcb18038",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -269,7 +269,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "08289088a88d410f",
"gsd-core/workflows/add-phase.md": "5154a5fb3e0d4370",
"gsd-core/workflows/add-tests.md": "81d0a836878e1a10",
"gsd-core/workflows/add-tests.md": "b4deb88c74d491c7",
"gsd-core/workflows/add-todo.md": "72fcef9fd0cafda5",
"gsd-core/workflows/ai-integration-phase.md": "90e5f97018715b18",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
@@ -282,7 +282,7 @@
"gsd-core/workflows/code-review-fix.md": "adb9388bb157610c",
"gsd-core/workflows/code-review.md": "ae6bcbd1575aeec4",
"gsd-core/workflows/complete-milestone.md": "614299b2c08e66c3",
"gsd-core/workflows/debug.md": "9d4a8afc8d36be93",
"gsd-core/workflows/debug.md": "437b47e14eba8786",
"gsd-core/workflows/diagnose-issues.md": "2971c699d52f1b85",
"gsd-core/workflows/discovery-phase.md": "724408336596c50c",
"gsd-core/workflows/discuss-phase-assumptions.md": "92e43cd12200c610",
@@ -358,7 +358,7 @@
"gsd-core/workflows/resume-project.md": "ad9f06a10bab8cc0",
"gsd-core/workflows/review.md": "2d28a6683ff587de",
"gsd-core/workflows/scan.md": "ad8ebcad4626d4a8",
"gsd-core/workflows/secure-phase.md": "e9a488cec3b4efdc",
"gsd-core/workflows/secure-phase.md": "71e6e689e80288ec",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "252b0d3edc315339",
"gsd-core/workflows/settings-integrations.md": "d1711a95f44fdbde",
@@ -367,7 +367,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "681800323681c5c6",
"gsd-core/workflows/sketch.md": "fcb7af914159ef7b",
"gsd-core/workflows/smart-entry.md": "2a253fe437496eea",
"gsd-core/workflows/spec-phase.md": "cb6cbe6eac816b0a",
"gsd-core/workflows/spec-phase.md": "5c4a3001095ca722",
"gsd-core/workflows/spike-wrap-up.md": "30a73aa19a1db271",
"gsd-core/workflows/spike.md": "f8b07d411473560a",
"gsd-core/workflows/stats.md": "846cd2808461ccdc",

View File

@@ -5,7 +5,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
"gsd-core/bin/gsd-tools.cjs": "6a7616125440c8b1",
"gsd-core/bin/gsd-tools.cjs": "37fb6081fcb18038",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -165,7 +165,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "1bc7377b105194fc",
"gsd-core/workflows/add-phase.md": "46e0551ffdd8ce1a",
"gsd-core/workflows/add-tests.md": "2c1da65d41dc12d2",
"gsd-core/workflows/add-tests.md": "24b0d8157a9ccb8b",
"gsd-core/workflows/add-todo.md": "cc0efe270004c8fb",
"gsd-core/workflows/ai-integration-phase.md": "40c217869a06981f",
"gsd-core/workflows/analyze-dependencies.md": "52942af10f140717",
@@ -178,7 +178,7 @@
"gsd-core/workflows/code-review-fix.md": "2e113d1f4350a075",
"gsd-core/workflows/code-review.md": "334c90c401f291f8",
"gsd-core/workflows/complete-milestone.md": "c1f91b77f4ace7f2",
"gsd-core/workflows/debug.md": "849d7e5b9c12dcae",
"gsd-core/workflows/debug.md": "c581e89aa9d9d71e",
"gsd-core/workflows/diagnose-issues.md": "d6d978fddfd5da8d",
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
"gsd-core/workflows/discuss-phase-assumptions.md": "f5b765d33eba4f88",
@@ -254,7 +254,7 @@
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
"gsd-core/workflows/scan.md": "003883d71c37da7d",
"gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62",
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
@@ -263,7 +263,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "838c701bd072ae73",
"gsd-core/workflows/sketch.md": "c7725562b3efd311",
"gsd-core/workflows/smart-entry.md": "449238eb94abe187",
"gsd-core/workflows/spec-phase.md": "8c480bd91f3cef6f",
"gsd-core/workflows/spec-phase.md": "fa8669b75392cf6c",
"gsd-core/workflows/spike-wrap-up.md": "0b24057c340a8a17",
"gsd-core/workflows/spike.md": "9134cdc3b75282c9",
"gsd-core/workflows/stats.md": "76a42cbeaf6007c2",

View File

@@ -29,7 +29,7 @@
"agents/gsd-project-researcher.md": "b5baac64a15c85e2",
"agents/gsd-research-synthesizer.md": "6cd9b501dc97bd50",
"agents/gsd-roadmapper.md": "c357a77ab919e9e5",
"agents/gsd-security-auditor.md": "d6d8f82501f10b92",
"agents/gsd-security-auditor.md": "9e3bc9a62036352c",
"agents/gsd-ui-auditor.md": "47937e784c9ae541",
"agents/gsd-ui-checker.md": "7d708c53a106f748",
"agents/gsd-ui-researcher.md": "e3768304c1c77753",
@@ -38,7 +38,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "6e98d76e955e35a2",
"gsd-core/bin/gsd-tools.cjs": "0341a6cf54b9acbe",
"gsd-core/bin/gsd-tools.cjs": "6454021dec4d9563",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -198,7 +198,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "cee41d5fc52d4e37",
"gsd-core/workflows/add-phase.md": "cc63a3e108c9a065",
"gsd-core/workflows/add-tests.md": "b62585823e5636bb",
"gsd-core/workflows/add-tests.md": "7cd945118e83246e",
"gsd-core/workflows/add-todo.md": "5e037b444e657557",
"gsd-core/workflows/ai-integration-phase.md": "afdc7c15f03a95fc",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
@@ -211,7 +211,7 @@
"gsd-core/workflows/code-review-fix.md": "f3725ae9d685bed2",
"gsd-core/workflows/code-review.md": "29125604bed2c467",
"gsd-core/workflows/complete-milestone.md": "40085d32b15805c8",
"gsd-core/workflows/debug.md": "c23d067580b09f63",
"gsd-core/workflows/debug.md": "a4e4c2f6d004460e",
"gsd-core/workflows/diagnose-issues.md": "652ae26975f82242",
"gsd-core/workflows/discovery-phase.md": "6161c60d752d0058",
"gsd-core/workflows/discuss-phase-assumptions.md": "18712f78bb960ec8",
@@ -287,7 +287,7 @@
"gsd-core/workflows/resume-project.md": "7f20769f302e5427",
"gsd-core/workflows/review.md": "bcbc20cb8df021cc",
"gsd-core/workflows/scan.md": "949692db4834dd27",
"gsd-core/workflows/secure-phase.md": "6758f1acf4113e9e",
"gsd-core/workflows/secure-phase.md": "ef7b5ad194b687bf",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "5e05212fb5cba531",
"gsd-core/workflows/settings-integrations.md": "29c5de27fbbb18e9",
@@ -296,7 +296,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "89e0eab2af946b04",
"gsd-core/workflows/sketch.md": "483387542d6fc3af",
"gsd-core/workflows/smart-entry.md": "d309710bcabd4675",
"gsd-core/workflows/spec-phase.md": "0d888493bde5146b",
"gsd-core/workflows/spec-phase.md": "546b3a8eb1a97013",
"gsd-core/workflows/spike-wrap-up.md": "fe04cb3d30dcbc6c",
"gsd-core/workflows/spike.md": "fad28f2ecf2dac52",
"gsd-core/workflows/stats.md": "89a08155b92f4a2d",

View File

@@ -29,7 +29,7 @@
"agents/gsd-project-researcher.md": "ddf7794e81300032",
"agents/gsd-research-synthesizer.md": "a124b00271748d07",
"agents/gsd-roadmapper.md": "493ef92b42b12cf4",
"agents/gsd-security-auditor.md": "fbd3798eec23651b",
"agents/gsd-security-auditor.md": "1e2eea5b2ab16d6e",
"agents/gsd-ui-auditor.md": "771ae08260534d5c",
"agents/gsd-ui-checker.md": "7ecd910efa6eb00e",
"agents/gsd-ui-researcher.md": "9e1a84a55a4cac99",
@@ -38,7 +38,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "de4627dff103d527",
"gsd-core/bin/gsd-tools.cjs": "75bf5691ded4927b",
"gsd-core/bin/gsd-tools.cjs": "c28c9076058c5e73",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -198,7 +198,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "1851e97996d445d5",
"gsd-core/workflows/add-phase.md": "09bdebbc62f3310e",
"gsd-core/workflows/add-tests.md": "ff3a598cdbc5aa6e",
"gsd-core/workflows/add-tests.md": "8ad08d29415f8f43",
"gsd-core/workflows/add-todo.md": "0fe07cbd29ec252b",
"gsd-core/workflows/ai-integration-phase.md": "d469eb120e52de0f",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
@@ -211,7 +211,7 @@
"gsd-core/workflows/code-review-fix.md": "f2761f7f8c4a5674",
"gsd-core/workflows/code-review.md": "47663a2922756c5e",
"gsd-core/workflows/complete-milestone.md": "6e918b72bd885426",
"gsd-core/workflows/debug.md": "197d3642a6704de5",
"gsd-core/workflows/debug.md": "7783c3cb81fef70d",
"gsd-core/workflows/diagnose-issues.md": "9274b11a3db98c65",
"gsd-core/workflows/discovery-phase.md": "b32b6197b66c9a13",
"gsd-core/workflows/discuss-phase-assumptions.md": "e376b1cf29379df4",
@@ -287,7 +287,7 @@
"gsd-core/workflows/resume-project.md": "f242e4c8aba18ea2",
"gsd-core/workflows/review.md": "835cf8c9594f17c1",
"gsd-core/workflows/scan.md": "63631467651d9ca8",
"gsd-core/workflows/secure-phase.md": "6cc236e53c2e7d56",
"gsd-core/workflows/secure-phase.md": "4a647aec1e4d2dfe",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "39e66386f6c48025",
"gsd-core/workflows/settings-integrations.md": "f8f756709ec02363",
@@ -296,7 +296,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "dbec602d104cb951",
"gsd-core/workflows/sketch.md": "44ff275150b6d045",
"gsd-core/workflows/smart-entry.md": "d8018578571f08d5",
"gsd-core/workflows/spec-phase.md": "ab8fad8038b14cad",
"gsd-core/workflows/spec-phase.md": "7b8873b0cd3eb9d7",
"gsd-core/workflows/spike-wrap-up.md": "6fe876cb87603bc3",
"gsd-core/workflows/spike.md": "c042a437baaf10ad",
"gsd-core/workflows/stats.md": "e4f84542fb5721ef",

View File

@@ -29,7 +29,7 @@
"agents/gsd-project-researcher.md": "f6697b316b5995ba",
"agents/gsd-research-synthesizer.md": "04036f38c1d373ea",
"agents/gsd-roadmapper.md": "fb62e1e3de84b5f9",
"agents/gsd-security-auditor.md": "13b660e2d336ed8e",
"agents/gsd-security-auditor.md": "cacf711cb835300b",
"agents/gsd-ui-auditor.md": "20cc99872e9b998b",
"agents/gsd-ui-checker.md": "56698909c270b130",
"agents/gsd-ui-researcher.md": "c348aa3ff8412ecb",
@@ -38,7 +38,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "5636ca0b726871b2",
"gsd-core/bin/gsd-tools.cjs": "dd746ae946645155",
"gsd-core/bin/gsd-tools.cjs": "568e7c6bdd415c1a",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -198,7 +198,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "61169973a10b1321",
"gsd-core/workflows/add-phase.md": "f9b1f7cc229b57f8",
"gsd-core/workflows/add-tests.md": "b5e03955cb29d7d8",
"gsd-core/workflows/add-tests.md": "d51e69bb433535a4",
"gsd-core/workflows/add-todo.md": "cab0d8215579fbdd",
"gsd-core/workflows/ai-integration-phase.md": "8948988717506320",
"gsd-core/workflows/analyze-dependencies.md": "77aff48f97fa6f1c",
@@ -211,7 +211,7 @@
"gsd-core/workflows/code-review-fix.md": "b99b1f20bb27c291",
"gsd-core/workflows/code-review.md": "faa87faf07ae765a",
"gsd-core/workflows/complete-milestone.md": "f463bf4e86ac26f6",
"gsd-core/workflows/debug.md": "52243eb936a43150",
"gsd-core/workflows/debug.md": "d0ee63e547f4d997",
"gsd-core/workflows/diagnose-issues.md": "447072aa72385271",
"gsd-core/workflows/discovery-phase.md": "7dcf150998559c11",
"gsd-core/workflows/discuss-phase-assumptions.md": "b091b3d3e580dd29",
@@ -287,7 +287,7 @@
"gsd-core/workflows/resume-project.md": "82cfe1b8cb17c085",
"gsd-core/workflows/review.md": "3e72508a5dccd45a",
"gsd-core/workflows/scan.md": "12c11b2edc165df9",
"gsd-core/workflows/secure-phase.md": "7bf923689bf58288",
"gsd-core/workflows/secure-phase.md": "185a15d389951e6e",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "2f86ec7b998f9485",
"gsd-core/workflows/settings-integrations.md": "b082fc518b484c07",
@@ -296,7 +296,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "10063f56c2c7f141",
"gsd-core/workflows/sketch.md": "25c1f8f7acfb1da9",
"gsd-core/workflows/smart-entry.md": "bd81482cb6a7ac53",
"gsd-core/workflows/spec-phase.md": "c05348d644f9d77a",
"gsd-core/workflows/spec-phase.md": "744faedd273da0f7",
"gsd-core/workflows/spike-wrap-up.md": "8029ca4effb5b716",
"gsd-core/workflows/spike.md": "a94af5171bb9d2ce",
"gsd-core/workflows/stats.md": "d58325fc3fb8d1b6",

View File

@@ -29,7 +29,7 @@
"agents/gsd-project-researcher.md": "f572892f138734ff",
"agents/gsd-research-synthesizer.md": "29949bf3f049a8f1",
"agents/gsd-roadmapper.md": "840ac933e3b094f9",
"agents/gsd-security-auditor.md": "b7202c44366697dd",
"agents/gsd-security-auditor.md": "4b86fa11ebda981e",
"agents/gsd-ui-auditor.md": "76f446c50edf81f8",
"agents/gsd-ui-checker.md": "15949ccab982b71c",
"agents/gsd-ui-researcher.md": "0e8ec8509d476904",
@@ -109,7 +109,7 @@
"gsd-core/VERSION": "ef0deccd81a6723c",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/ensure-runtime-build.cjs": "51bc64467ab30f62",
"gsd-core/bin/gsd-tools.cjs": "6a7616125440c8b1",
"gsd-core/bin/gsd-tools.cjs": "37fb6081fcb18038",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "6bba2b9c9fa47cb8",
@@ -269,7 +269,7 @@
"gsd-core/workflows/_runtime-launcher.snippet.sh": "bf2dd5d1debd5335",
"gsd-core/workflows/add-backlog.md": "1bc7377b105194fc",
"gsd-core/workflows/add-phase.md": "46e0551ffdd8ce1a",
"gsd-core/workflows/add-tests.md": "2c1da65d41dc12d2",
"gsd-core/workflows/add-tests.md": "24b0d8157a9ccb8b",
"gsd-core/workflows/add-todo.md": "cc0efe270004c8fb",
"gsd-core/workflows/ai-integration-phase.md": "40c217869a06981f",
"gsd-core/workflows/analyze-dependencies.md": "52942af10f140717",
@@ -282,7 +282,7 @@
"gsd-core/workflows/code-review-fix.md": "2e113d1f4350a075",
"gsd-core/workflows/code-review.md": "334c90c401f291f8",
"gsd-core/workflows/complete-milestone.md": "c1f91b77f4ace7f2",
"gsd-core/workflows/debug.md": "849d7e5b9c12dcae",
"gsd-core/workflows/debug.md": "c581e89aa9d9d71e",
"gsd-core/workflows/diagnose-issues.md": "aa8d787db8f3c46c",
"gsd-core/workflows/discovery-phase.md": "3ba7cfb89fb1e761",
"gsd-core/workflows/discuss-phase-assumptions.md": "f5b765d33eba4f88",
@@ -358,7 +358,7 @@
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
"gsd-core/workflows/scan.md": "003883d71c37da7d",
"gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62",
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
"gsd-core/workflows/settings-advanced.md": "414db4dbea97ba44",
"gsd-core/workflows/settings-integrations.md": "70515c5838fb9826",
@@ -367,7 +367,7 @@
"gsd-core/workflows/sketch-wrap-up.md": "86db87b16548117e",
"gsd-core/workflows/sketch.md": "e96f1866d3e60cab",
"gsd-core/workflows/smart-entry.md": "449238eb94abe187",
"gsd-core/workflows/spec-phase.md": "8c480bd91f3cef6f",
"gsd-core/workflows/spec-phase.md": "fa8669b75392cf6c",
"gsd-core/workflows/spike-wrap-up.md": "d643447e16e95753",
"gsd-core/workflows/spike.md": "265e5c5a13ea0deb",
"gsd-core/workflows/stats.md": "76a42cbeaf6007c2",

View File

@@ -113,6 +113,131 @@ describe('milestone complete command', () => {
assert.ok(milestones.includes('Set up project infrastructure'));
});
test('#2118 — --dry-run does NOT mutate: no archive, no STATE.md rewrite, no phase move', () => {
writeRoadmap(tmpDir, `# Roadmap v1.0 MVP\n\n### Phase 1: Foundation\n**Goal:** Setup\n`);
fs.writeFileSync(
path.join(tmpDir, '.planning', 'REQUIREMENTS.md'),
`# Requirements\n\n- [x] User auth\n`,
);
writeState(tmpDir);
const phasePath = mkPhaseDir(tmpDir, '01-foundation', { oneLiner: 'Set up project infrastructure' });
// Capture pre-state
const stateBefore = fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
const result = runGsdTools('milestone complete v1.0 --name Test --dry-run', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
assert.strictEqual(output.dry_run, true, 'dry_run must be true');
assert.strictEqual(output.version, 'v1.0');
assert.ok(output.stats.phases >= 1, 'should count at least 1 phase');
assert.ok(output.would_archive.roadmap, 'should list roadmap archive plan');
assert.ok(output.would_archive.requirements, 'should list requirements archive plan');
assert.ok(
output.would_archive.phases.includes('01-foundation'),
'should list phase dir for archive',
);
assert.ok(
Array.isArray(output.accomplishments) && output.accomplishments.includes('Set up project infrastructure'),
'dry-run preview should surface accomplishments from phase SUMMARY one-liners (#2118)',
);
// CRITICAL: no mutations occurred
assert.ok(
fs.existsSync(path.join(tmpDir, '.planning', 'ROADMAP.md')),
'ROADMAP.md must NOT be archived (dry-run)',
);
assert.ok(
fs.existsSync(path.join(tmpDir, '.planning', 'REQUIREMENTS.md')),
'REQUIREMENTS.md must NOT be archived (dry-run)',
);
assert.ok(
fs.existsSync(phasePath),
'phase directory must NOT be moved (dry-run)',
);
assert.ok(
!fs.existsSync(path.join(tmpDir, '.planning', 'MILESTONES.md')),
'MILESTONES.md must NOT be created (dry-run)',
);
assert.strictEqual(
fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8'),
stateBefore,
'STATE.md must be unchanged (dry-run)',
);
assert.ok(
!fs.existsSync(path.join(tmpDir, '.planning', 'milestones')),
'archive dir must NOT be created (dry-run) — platformEnsureDir must be gated',
);
});
test('#2118 — --dry-run --no-archive-phases omits phase list from preview', () => {
writeRoadmap(tmpDir, `# Roadmap v1.0 MVP\n\n### Phase 1: Foundation\n**Goal:** Setup\n`);
writeState(tmpDir);
mkPhaseDir(tmpDir, '01-foundation');
const result = runGsdTools('milestone complete v1.0 --dry-run --no-archive-phases', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
assert.strictEqual(output.dry_run, true);
assert.deepStrictEqual(output.would_archive.phases, [], 'phases list must be empty with --no-archive-phases');
});
test('#2118 — --dry-run --force bypasses the unstarted-phases guard', () => {
writeRoadmap(tmpDir, `# Roadmap v1.0 MVP\n\n### Phase 1: Foundation\n**Goal:** Setup\n`);
writeState(tmpDir, 'milestone: v1.0\n');
// No phase directory for Phase 1 — guard would block without --force
// Capture pre-state
const stateBefore = fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
const result = runGsdTools('milestone complete v1.0 --dry-run --force', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
assert.strictEqual(output.dry_run, true, 'preview should run past the guard with --force');
// CRITICAL: --force must still be a zero-mutation dry-run preview
assert.ok(
!fs.existsSync(path.join(tmpDir, '.planning', 'milestones')),
'archive dir must NOT be created (dry-run --force) — platformEnsureDir must be gated',
);
assert.strictEqual(
fs.readFileSync(path.join(tmpDir, '.planning', 'STATE.md'), 'utf-8'),
stateBefore,
'STATE.md must be unchanged (dry-run --force)',
);
assert.ok(
!fs.existsSync(path.join(tmpDir, '.planning', 'MILESTONES.md')),
'MILESTONES.md must NOT be created (dry-run --force)',
);
});
test('#2118 — --dry-run --raw emits structured preview JSON, not the literal "dry-run" string', () => {
writeRoadmap(tmpDir, `# Roadmap v1.0 MVP\n\n### Phase 1: Foundation\n**Goal:** Setup\n`);
writeState(tmpDir);
mkPhaseDir(tmpDir, '01-foundation', { oneLiner: 'Set up project infrastructure' });
const result = runGsdTools(['milestone', 'complete', 'v1.0', '--name', 'Test', '--dry-run', '--raw'], tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
// Before the fix, output(dryRunResult, raw, 'dry-run') meant --raw discarded
// the structured payload and printed only the literal string "dry-run",
// which is not parseable JSON.
let output;
assert.doesNotThrow(
() => { output = JSON.parse(result.output); },
`--dry-run --raw output must be parseable JSON, not the literal "dry-run" string; got ${JSON.stringify(result.output)}`,
);
assert.strictEqual(output.dry_run, true, 'dry_run must be true, not the literal string "dry-run"');
assert.strictEqual(output.version, 'v1.0');
assert.ok(
Array.isArray(output.accomplishments) && output.accomplishments.includes('Set up project infrastructure'),
'structured preview surviving --raw should include accomplishments',
);
});
test('prepends to existing MILESTONES.md (reverse chronological)', () => {
fs.writeFileSync(
path.join(tmpDir, '.planning', 'MILESTONES.md'),

View File

@@ -1964,3 +1964,167 @@ describe('feat-3594: roadmap parser does not crash on ANY corpus fixture', () =>
});
});
}
// ─── #2200: currentMilestoneRawRanges scopes phase-complete writes ────────────
// The phase-complete roadmap mutators (checkbox-flip + Plans writer) must mutate
// only within the active milestone so they cannot touch a backticked prose
// literal, a Backlog entry, or a same-numbered phase in a shipped milestone.
// This tests the scoping helper the fix rests on (the mutators' command path is
// covered by the existing phase-complete suite; gsd-test confirms no regression).
{
const { describe: d3, test: t3 } = require('node:test');
const a3 = require('node:assert/strict');
const fs3 = require('node:fs');
const path3 = require('node:path');
const { createTempProject: ctp3, cleanup: cu3 } = require('./helpers.cjs');
const rp3 = require('../gsd-core/bin/lib/roadmap-parser.cjs');
d3('#2200 currentMilestoneRawRanges — scopes writes to the active milestone', () => {
t3('the active window contains the active phase bullet, excludes Backlog + prose + shipped', () => {
const tmpDir = ctp3('fix-2200-');
try {
// Shipped milestone (in a <details> block) + Backlog + a backticked prose
// literal all come BEFORE the active milestone — the typical layout.
const roadmap = [
'# Roadmap', '',
'## Backlog', '- [ ] **Phase 1: Some Future Idea**', '',
'> See `- [ ] **Phase 1: Alpha**` in the active milestone.', '',
'<details><summary>✅ v0.9 Old</summary>',
'- [x] **Phase 1: Legacy**',
'### Phase 1: Legacy',
'**Plans:** 9/9 plans complete',
'</details>', '',
'## v1.0 — Active', '',
'- [ ] **Phase 1: Alpha**', '',
'### Phase 1: Alpha',
'**Plans:** 0/1 plans complete', '',
].join('\n');
fs3.writeFileSync(path3.join(tmpDir, '.planning', 'ROADMAP.md'), roadmap);
fs3.writeFileSync(path3.join(tmpDir, '.planning', 'STATE.md'), '---\nmilestone: v1.0\ncurrent_phase: 1\n---\n');
const ranges = rp3.currentMilestoneRawRanges(roadmap, tmpDir);
a3.ok(ranges, 'a versioned active milestone must yield ranges');
const primary = roadmap.slice(ranges.primary.start, ranges.primary.end);
a3.ok(primary.includes('- [ ] **Phase 1: Alpha**'), 'active phase bullet is inside the window');
a3.ok(!primary.includes('Some Future Idea'), 'a Backlog entry is outside the active window');
a3.ok(!primary.includes('See `- [ ]'), 'a backticked prose literal is outside the active window');
a3.ok(!primary.includes('9/9 plans complete'), 'a shipped milestone plan-count line is outside the active window');
} finally {
cu3(tmpDir);
}
});
t3('returns null without a versioned active milestone (whole-content fallback)', () => {
a3.strictEqual(rp3.currentMilestoneRawRanges('# Roadmap\n- [ ] **Phase 1: X**\n', undefined), null);
});
});
}
// ─── #2199: bullet/em-dash ROADMAP phase resolution ───────────────────────────
// Self-contained block: phase lookup + milestone filter must accept bullet/
// checkbox entries with an em-dash/en-dash/hyphen/colon separator, not just the
// ATX-heading + colon form. Previously such an entry resolved found:false and
// `Phase null` was written into STATE.md; a bullet-only ROADMAP collapsed the
// milestone filter to a zero-count pass-all.
{
const { describe: d2, test: t2, beforeEach: be2, afterEach: ae2 } = require('node:test');
const a2 = require('node:assert/strict');
const fs2 = require('node:fs');
const path2 = require('node:path');
const { createTempProject: ctp2, cleanup: cu2 } = require('./helpers.cjs');
const rp2 = require('../gsd-core/bin/lib/roadmap-parser.cjs');
const writeRoadmap2 = (d, c) => fs2.writeFileSync(path2.join(d, '.planning', 'ROADMAP.md'), c);
d2('#2199 roadmap bullet/em-dash phase resolution', () => {
let tmpDir;
be2(() => { tmpDir = ctp2('fix-2199-'); });
ae2(() => { cu2(tmpDir); });
t2('an all-bullet em-dash ROADMAP resolves each phase (no Phase null)', () => {
writeRoadmap2(tmpDir, [
'# Roadmap', '', '## v1.0 Active', '',
'- [ ] **Phase 1 — Authentication**: login flow',
'- [ ] **Phase 2 — Authorization**: RBAC',
'- [x] **Phase 3 — Audit Logging**: events',
'',
].join('\n'));
const p1 = rp2.getRoadmapPhaseInternal(tmpDir, '1');
a2.ok(p1 && p1.found, 'Phase 1 must resolve on a bullet ROADMAP');
a2.strictEqual(p1.phase_name, 'Authentication');
const p2 = rp2.getRoadmapPhaseInternal(tmpDir, '2');
a2.ok(p2 && p2.found);
a2.strictEqual(p2.phase_name, 'Authorization');
const p3 = rp2.getRoadmapPhaseInternal(tmpDir, '3');
a2.ok(p3 && p3.found, 'a checked [x] bullet must also resolve');
a2.strictEqual(p3.phase_name, 'Audit Logging');
const absent = rp2.getRoadmapPhaseInternal(tmpDir, '99');
a2.ok(!absent || !absent.found, 'an absent phase must not resolve');
});
t2('bullet entries with colon / en-dash / hyphen separators all resolve', () => {
writeRoadmap2(tmpDir, [
'# Roadmap', '', '## v1.0 Active', '',
'- [ ] **Phase 1: Colon Sep**: one',
'- [ ] **Phase 2 – En Dash**: two',
'- [ ] **Phase 3 - Hyphen Sep**: three',
'',
].join('\n'));
a2.strictEqual(rp2.getRoadmapPhaseInternal(tmpDir, '1').phase_name, 'Colon Sep');
a2.strictEqual(rp2.getRoadmapPhaseInternal(tmpDir, '2').phase_name, 'En Dash');
a2.strictEqual(rp2.getRoadmapPhaseInternal(tmpDir, '3').phase_name, 'Hyphen Sep');
});
t2('mixed heading + bullet forms both resolve', () => {
writeRoadmap2(tmpDir, [
'# Roadmap', '', '## v1.0 Active', '',
'### Phase 1: Heading Form',
'body',
'- [ ] **Phase 2 — Bullet Form**: two',
'',
].join('\n'));
const p1 = rp2.getRoadmapPhaseInternal(tmpDir, '1');
a2.ok(p1 && p1.found, 'heading form still resolves (no regression)');
a2.ok(/Heading Form/.test(p1.phase_name));
const p2 = rp2.getRoadmapPhaseInternal(tmpDir, '2');
a2.ok(p2 && p2.found, 'bullet form resolves alongside heading form');
a2.strictEqual(p2.phase_name, 'Bullet Form');
});
t2('milestone phase-count counts bullet-form phases (not zero)', () => {
writeRoadmap2(tmpDir, [
'# Roadmap', '', '## v1.0 Active', '',
'- [ ] **Phase 1 — One**: a',
'- [ ] **Phase 2 — Two**: b',
'- [ ] **Phase 3 — Three**: c',
'',
].join('\n'));
const filter = rp2.getMilestonePhaseFilter(tmpDir);
a2.strictEqual(filter.phaseCount, 3,
'a bullet-only ROADMAP must populate the milestone phase set (was a zero-count pass-all before #2199)');
a2.ok(filter('1'), 'phase 1 dir is in the milestone set');
a2.ok(filter('2'), 'phase 2 dir is in the milestone set');
a2.ok(!filter('99'), 'a non-listed phase is excluded');
});
t2('#2199 heading in Phase Details (full content) beats a bullet in the active scope', () => {
// The exact first-attempt regression: a bullet for the phase exists in the
// active-milestone scope, but the heading (carrying Requirements) lives in a
// Phase Details section outside that scope (only in fullContent). The heading
// MUST win — otherwise the bullet's single-line section yields null req_ids.
writeRoadmap2(tmpDir, [
'# Roadmap', '',
'## Milestones', '',
'- 🚧 **v1.0 Active** - Phases 10-11', '',
'## v1.0 Active', '',
'- [ ] **Phase 11 — Second Active Phase**',
'',
'## Phase Details', '',
'### Phase 11: Second Active Phase',
'**Requirements**: REQ-02, REQ-03',
'',
].join('\n'));
const p11 = rp2.getRoadmapPhaseInternal(tmpDir, '11');
a2.ok(p11 && p11.found, 'phase 11 resolves');
a2.ok(/REQ-02/.test(p11.section),
'the heading section (with Requirements) must win over the scoped bullet line');
});
});
}

View File

@@ -0,0 +1,73 @@
/**
* Regression test for #2119: /gsd-secure-phase dual SECURITY.md writers.
*
* The gsd-security-auditor agent must NOT have Write/Edit tools — the
* orchestrator (secure-phase.md Step 6) is the sole SECURITY.md writer.
* The auditor returns a structured verdict; it never writes files.
*/
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const AGENT_PATH = path.join(__dirname, '..', 'agents', 'gsd-security-auditor.md');
function parseYamlTools(content) {
const lines = content.split(/\r?\n/);
let inFrontmatter = false;
let inTools = false;
const tools = [];
for (const line of lines) {
const trimmed = line.trim();
if (trimmed === '---') {
inFrontmatter = !inFrontmatter;
if (!inFrontmatter) break;
continue;
}
if (!inFrontmatter) continue;
if (trimmed.startsWith('tools:')) {
inTools = true;
continue;
}
if (inTools) {
if (trimmed.startsWith('- ')) {
tools.push(trimmed.slice(2).trim());
} else if (trimmed && !trimmed.startsWith('#')) {
inTools = false;
}
}
}
return tools;
}
describe('#2119 — security auditor is return-only (no file writes)', () => {
const content = fs.readFileSync(AGENT_PATH, 'utf-8');
test('auditor tools do not include Write or Edit', () => {
const tools = parseYamlTools(content);
assert.ok(tools.length > 0, 'tools list should be non-empty');
assert.ok(
!tools.includes('Write'),
`Write must not be in auditor tools (got: ${tools.join(', ')}) — orchestrator is the sole SECURITY.md writer (#2119)`,
);
assert.ok(
!tools.includes('Edit'),
`Edit must not be in auditor tools (got: ${tools.join(', ')}) — orchestrator is the sole SECURITY.md writer (#2119)`,
);
});
test('auditor description does not claim to produce SECURITY.md', () => {
const lines = content.split(/\r?\n/);
const descLine = lines.find((l) => l.startsWith('description:'));
assert.ok(descLine, 'description field must exist');
assert.ok(
!descLine.includes('Produces SECURITY.md'),
'description must not claim to produce SECURITY.md — auditor returns a verdict, orchestrator writes (#2119)',
);
assert.ok(
descLine.includes('Returns structured') || descLine.includes('returns'),
'description should state the auditor returns a structured verdict',
);
});
});

View File

@@ -57,15 +57,18 @@ describe('SECURE: gsd-security-auditor agent', () => {
);
});
test('tools include Read, Write, Bash, Glob, Grep', () => {
test('tools include Read, Bash, Glob, Grep but NOT Write or Edit (#2119)', () => {
const content = fs.readFileSync(agentPath, 'utf-8');
const requiredTools = ['Read', 'Write', 'Bash', 'Glob', 'Grep'];
const requiredTools = ['Read', 'Bash', 'Glob', 'Grep'];
for (const tool of requiredTools) {
assert.ok(
content.includes(`- ${tool}`),
`tools must include ${tool}`
);
}
// #2119: auditor is return-only — orchestrator is the sole SECURITY.md writer
assert.ok(!content.includes('- Write'), 'tools must NOT include Write (#2119)');
assert.ok(!content.includes('- Edit'), 'tools must NOT include Edit (#2119)');
});
test('has <role> section', () => {

View File

@@ -1,7 +1,7 @@
{
"add-backlog.md": 7176,
"add-phase.md": 7247,
"add-tests.md": 16945,
"add-tests.md": 16961,
"add-todo.md": 8996,
"ai-integration-phase.md": 14805,
"analyze-dependencies.md": 3887,
@@ -14,7 +14,7 @@
"code-review-fix.md": 24320,
"code-review.md": 31916,
"complete-milestone.md": 31071,
"debug.md": 13549,
"debug.md": 14241,
"diagnose-issues.md": 12864,
"discovery-phase.md": 8651,
"discuss-phase-assumptions.md": 27302,
@@ -66,7 +66,7 @@
"resume-project.md": 17270,
"review.md": 47168,
"scan.md": 7732,
"secure-phase.md": 13520,
"secure-phase.md": 13622,
"session-report.md": 4044,
"settings-advanced.md": 40019,
"settings-integrations.md": 15892,
@@ -75,7 +75,7 @@
"sketch-wrap-up.md": 14267,
"sketch.md": 20004,
"smart-entry.md": 11124,
"spec-phase.md": 31945,
"spec-phase.md": 31987,
"spike-wrap-up.md": 15136,
"spike.md": 24561,
"stats.md": 6762,