Merge branch 'next' into fix/2137-derive-progress-header-driven
This commit is contained in:
5
.changeset/2112-commit-files-pathspec.md
Normal file
5
.changeset/2112-commit-files-pathspec.md
Normal 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)
|
||||
5
.changeset/2118-milestone-complete-dry-run.md
Normal file
5
.changeset/2118-milestone-complete-dry-run.md
Normal 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)
|
||||
5
.changeset/2119-secure-phase-single-writer.md
Normal file
5
.changeset/2119-secure-phase-single-writer.md
Normal 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)
|
||||
5
.changeset/clever-eagles-romp.md
Normal file
5
.changeset/clever-eagles-romp.md
Normal 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)
|
||||
5
.changeset/proud-geese-caper.md
Normal file
5
.changeset/proud-geese-caper.md
Normal 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)
|
||||
5
.changeset/proud-ravens-jump.md
Normal file
5
.changeset/proud-ravens-jump.md
Normal 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)
|
||||
5
.changeset/sturdy-yaks-caper.md
Normal file
5
.changeset/sturdy-yaks-caper.md
Normal 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)
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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...
|
||||
|
||||
@@ -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}",
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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`);
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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 };
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
176
tests/commit-files-pathspec.test.cjs
Normal file
176
tests/commit-files-pathspec.test.cjs
Normal 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',
|
||||
);
|
||||
});
|
||||
});
|
||||
52
tests/fix-2196-debug-agent-handoff.test.cjs
Normal file
52
tests/fix-2196-debug-agent-handoff.test.cjs
Normal 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');
|
||||
});
|
||||
});
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
12
tests/fixtures/golden-install-parity/claude.json
vendored
12
tests/fixtures/golden-install-parity/claude.json
vendored
@@ -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",
|
||||
|
||||
12
tests/fixtures/golden-install-parity/cline.json
vendored
12
tests/fixtures/golden-install-parity/cline.json
vendored
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
16
tests/fixtures/golden-install-parity/codex.json
vendored
16
tests/fixtures/golden-install-parity/codex.json
vendored
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
12
tests/fixtures/golden-install-parity/cursor.json
vendored
12
tests/fixtures/golden-install-parity/cursor.json
vendored
@@ -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",
|
||||
|
||||
12
tests/fixtures/golden-install-parity/hermes.json
vendored
12
tests/fixtures/golden-install-parity/hermes.json
vendored
@@ -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",
|
||||
|
||||
12
tests/fixtures/golden-install-parity/kilo.json
vendored
12
tests/fixtures/golden-install-parity/kilo.json
vendored
@@ -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",
|
||||
|
||||
16
tests/fixtures/golden-install-parity/kimi.json
vendored
16
tests/fixtures/golden-install-parity/kimi.json
vendored
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
10
tests/fixtures/golden-install-parity/pi.json
vendored
10
tests/fixtures/golden-install-parity/pi.json
vendored
@@ -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",
|
||||
|
||||
12
tests/fixtures/golden-install-parity/qwen.json
vendored
12
tests/fixtures/golden-install-parity/qwen.json
vendored
@@ -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",
|
||||
|
||||
12
tests/fixtures/golden-install-parity/trae.json
vendored
12
tests/fixtures/golden-install-parity/trae.json
vendored
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
12
tests/fixtures/golden-install-parity/zcode.json
vendored
12
tests/fixtures/golden-install-parity/zcode.json
vendored
@@ -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",
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
73
tests/secure-phase-single-writer.test.cjs
Normal file
73
tests/secure-phase-single-writer.test.cjs
Normal 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',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user