Merge branch 'next' into fix/2003-capability-state-runtime-flag

This commit is contained in:
Tom Boucher
2026-07-07 00:19:08 -04:00
committed by GitHub
25 changed files with 418 additions and 30 deletions

View File

@@ -23,6 +23,7 @@ const {
isCapabilityActive,
_isSafePropKey,
_loadInstalledSkillsManifest,
_loadFlatCommandsGsdManifest,
_resolveManifest,
} = require('../gsd-core/bin/lib/capability-state.cjs');
@@ -824,6 +825,223 @@ describe('cmdCapabilityState — end-to-end via gsd-tools CLI', () => {
// FAIL before the fix and PASS after, regardless of whether commands/gsd
// happens to exist in the current checkout.
describe('regressions: flat commands/gsd-<stem>.md layout (#1858)', () => {
// Flat command layout (Claude local project install shape): skills live at
// <repo>/commands/gsd-<stem>.md — the gsd- prefix is baked into the filename
// and there is NO commands/gsd/ subdir. _resolveManifest must detect this
// layout, strip the gsd- prefix, and produce the same stems the nested
// loader (commands/gsd/<stem>.md) would, or every skill-bearing capability
// is silently reported surfaced:false / enabled:false / active:false.
function makeFlatCommandMd(stem, requires) {
const req = requires ? `requires: [${requires.join(', ')}]` : 'requires: [phase]';
return [
'---',
`name: gsd:${stem}`,
`description: ${stem} skill`,
'argument-hint: "[phase number]"',
'allowed-tools:',
' - Read',
req,
'---',
'Execute end-to-end.',
].join('\n') + '\n';
}
// ── Unit tests for _loadFlatCommandsGsdManifest ─────────────────────────────
test('_loadFlatCommandsGsdManifest: returns empty map when parent dir absent', () => {
const missing = path.join(os.tmpdir(), 'cap-flat-missing-' + Date.now());
const manifest = _loadFlatCommandsGsdManifest(missing);
assert.ok(manifest instanceof Map, 'should return a Map');
assert.strictEqual(manifest.size, 0, 'should be empty when parent dir absent');
});
test('_loadFlatCommandsGsdManifest: scans gsd-<stem>.md and strips the gsd- prefix', () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-flat-scan-'));
try {
fs.writeFileSync(path.join(tmpDir, 'gsd-validate-phase.md'), makeFlatCommandMd('validate-phase'), 'utf8');
fs.writeFileSync(path.join(tmpDir, 'gsd-secure-phase.md'), makeFlatCommandMd('secure-phase'), 'utf8');
// Non-gsd file must be ignored
fs.writeFileSync(path.join(tmpDir, 'random-doc.md'), '# not a skill\n', 'utf8');
// Non-markdown gsd file must be ignored
fs.writeFileSync(path.join(tmpDir, 'gsd-notskill.txt'), 'nope\n', 'utf8');
const manifest = _loadFlatCommandsGsdManifest(tmpDir);
assert.ok(manifest.has('validate-phase'), 'flat gsd-validate-phase.md -> stem validate-phase');
assert.ok(manifest.has('secure-phase'), 'flat gsd-secure-phase.md -> stem secure-phase');
assert.ok(!manifest.has('gsd-validate-phase'), 'must NOT keep the gsd- prefix on the stem');
assert.ok(!manifest.has('random-doc'), 'non-gsd file must be ignored');
assert.ok(!manifest.has('notskill'), 'non-.md gsd file must be ignored');
} finally {
cleanup(tmpDir);
}
});
test('_loadFlatCommandsGsdManifest: parses requires via shared parseRequires (no drift)', () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-flat-req-'));
try {
fs.writeFileSync(
path.join(tmpDir, 'gsd-my-skill.md'),
makeFlatCommandMd('my-skill', ['dep-a', 'dep-b']),
'utf8',
);
const manifest = _loadFlatCommandsGsdManifest(tmpDir);
assert.deepStrictEqual(manifest.get('my-skill'), ['dep-a', 'dep-b']);
} finally {
cleanup(tmpDir);
}
});
test('_loadFlatCommandsGsdManifest: companion _calls_agents_<stem> key present (parity with nested loader)', () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-flat-agents-'));
try {
fs.writeFileSync(path.join(tmpDir, 'gsd-validate-phase.md'), makeFlatCommandMd('validate-phase'), 'utf8');
const manifest = _loadFlatCommandsGsdManifest(tmpDir);
assert.ok(manifest.has('_calls_agents_validate-phase'),
'flat loader must emit the companion _calls_agents_ key (same Map shape as loadSkillsManifest)');
} finally {
cleanup(tmpDir);
}
});
// Low-1 (review): boundary — empty stem (gsd-.md) skipped, single-char stem kept.
test('_loadFlatCommandsGsdManifest: skips gsd-.md (empty stem) and keeps single-char stem (slice boundary)', () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-flat-edge-'));
try {
fs.writeFileSync(path.join(tmpDir, 'gsd-.md'), makeFlatCommandMd(''), 'utf8');
fs.writeFileSync(path.join(tmpDir, 'gsd-x.md'), makeFlatCommandMd('x'), 'utf8');
const manifest = _loadFlatCommandsGsdManifest(tmpDir);
assert.ok(!manifest.has(''), 'gsd-.md must NOT register an empty-string stem');
assert.ok(!manifest.has('_calls_agents_'), 'no companion key for an empty stem');
assert.ok(manifest.has('x'), 'gsd-x.md -> single-char stem "x" (slice(4,-3) boundary)');
} finally {
cleanup(tmpDir);
}
});
// Low-2 (review): unreadable file degrades both keys to [] (parity with nested
// loader's catch). POSIX-only AND must not run as root — root bypasses POSIX
// read permission bits, so chmod 0o000 would NOT make the file unreadable and
// the test would assert [] against the real parsed deps (false failure).
// Skip on win32 (DEFECT.WINDOWS-POSIX-MODE-BIT-ASSERT) and when getuid()==0.
const _skipUnreadable = process.platform === 'win32' || (typeof process.getuid === 'function' && process.getuid() === 0);
test('_loadFlatCommandsGsdManifest: unreadable file degrades to empty deps + agents (parity)', { skip: _skipUnreadable }, () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-flat-unread-'));
let madeUnreadable = false;
try {
const skillPath = path.join(tmpDir, 'gsd-secure.md');
fs.writeFileSync(skillPath, '---\nname: gsd:secure\nrequires: [phase]\n---\nbody\n', { mode: 0o644 });
fs.chmodSync(skillPath, 0o000);
madeUnreadable = true;
const manifest = _loadFlatCommandsGsdManifest(tmpDir);
assert.deepStrictEqual(manifest.get('secure'), [], 'unreadable file -> empty requires (parity with nested catch)');
assert.deepStrictEqual(manifest.get('_calls_agents_secure'), [], 'unreadable file -> empty agents (parity with nested catch)');
} finally {
// Restore writability so cleanup() can rm the tmp tree.
if (madeUnreadable) {
try { fs.chmodSync(path.join(tmpDir, 'gsd-secure.md'), 0o644); } catch { /* best effort */ }
}
cleanup(tmpDir);
}
});
// ── _resolveManifest picks the flat branch when nested is absent ────────────
test('_resolveManifest: detects flat commands/gsd-<stem>.md layout when nested commands/gsd/ is absent (#1858)', () => {
const tmpRepo = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-rm-flat-repo-'));
try {
// Flat source layout: <repo>/commands/gsd-<stem>.md
// NO commands/gsd/ subdir, NO skills/ dir.
const commandsDir = path.join(tmpRepo, 'commands');
fs.mkdirSync(commandsDir, { recursive: true });
fs.writeFileSync(path.join(commandsDir, 'gsd-validate-phase.md'), makeFlatCommandMd('validate-phase'), 'utf8');
fs.writeFileSync(path.join(commandsDir, 'gsd-secure-phase.md'), makeFlatCommandMd('secure-phase'), 'utf8');
// commandsGsdDir = <repo>/commands/gsd (nested — does NOT exist).
// dirname(commandsGsdDir) = <repo>/commands (where the flat files live).
const commandsGsdDir = path.join(commandsDir, 'gsd');
// configDir = a separate empty tmp dir (no skills/ → installed fallback empty).
const configDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-rm-flat-cfg-'));
try {
const manifest = _resolveManifest(commandsGsdDir, configDir);
assert.ok(manifest.has('validate-phase'),
'flat layout must populate validate-phase stem (was empty pre-fix → all skill caps unsurfaced)');
assert.ok(manifest.has('secure-phase'), 'flat layout must populate secure-phase stem');
assert.ok(!manifest.has('gsd-validate-phase'), 'stem must have gsd- prefix stripped');
} finally {
cleanup(configDir);
}
} finally {
cleanup(tmpRepo);
}
});
test('_resolveManifest: flat branch does NOT shadow a populated installed skills dir when no flat files exist', () => {
// Precedence: nested > flat-source > installed. If the flat parent dir has
// NO gsd-*.md files, the flat loader returns an empty Map and _resolveManifest
// must fall through to the installed-skills branch (not return empty).
const tmpRepo = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-rm-precedence-'));
try {
const commandsDir = path.join(tmpRepo, 'commands');
fs.mkdirSync(commandsDir, { recursive: true });
// No gsd-*.md files in commands/ — flat loader yields empty.
// Installed skills present under configDir:
const configDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-rm-precedence-cfg-'));
try {
const secureDir = path.join(configDir, 'skills', 'gsd-secure-phase');
fs.mkdirSync(secureDir, { recursive: true });
fs.writeFileSync(path.join(secureDir, 'SKILL.md'),
'---\nname: gsd:secure-phase\nrequires: [phase]\n---\nbody\n', 'utf8');
const commandsGsdDir = path.join(commandsDir, 'gsd'); // nested absent
const manifest = _resolveManifest(commandsGsdDir, configDir);
assert.ok(manifest.has('secure-phase'),
'when flat dir is empty, installed-skills fallback must still work (precedence flat > installed only when flat non-empty)');
} finally {
cleanup(configDir);
}
} finally {
cleanup(tmpRepo);
}
});
// ── Parity: flat loader produces the same stems as the nested loader ────────
// (DEFECT.GENERATIVE-FIX — guards against silent divergence between the two
// parallel manifest-builders.)
test('flat loader and nested loader produce identical stems for the same command set (parity)', () => {
const realCommandsGsdDir = path.resolve(__dirname, '..', 'commands', 'gsd');
if (!fs.existsSync(realCommandsGsdDir)) return; // skip outside a repo checkout
// Build a flat mirror of the real nested commands/gsd/<stem>.md as
// commands/gsd-<stem>.md in a temp dir, then compare stem sets.
const tmpFlat = fs.mkdtempSync(path.join(os.tmpdir(), 'cap-parity-flat-'));
try {
const nested = require('../gsd-core/bin/lib/install-profiles.cjs').loadSkillsManifest(realCommandsGsdDir);
for (const [stem] of nested) {
if (stem.startsWith('_calls_agents_')) continue;
const src = path.join(realCommandsGsdDir, stem + '.md');
if (!fs.existsSync(src)) continue;
fs.writeFileSync(path.join(tmpFlat, 'gsd-' + stem + '.md'), fs.readFileSync(src, 'utf8'), 'utf8');
}
const flat = _loadFlatCommandsGsdManifest(tmpFlat);
const nestedStems = [...nested.keys()].filter((k) => !k.startsWith('_calls_agents_')).sort();
const flatStems = [...flat.keys()].filter((k) => !k.startsWith('_calls_agents_')).sort();
assert.deepStrictEqual(flatStems, nestedStems,
'flat loader stem set must match nested loader stem set for the real command tree');
// Nit-2 (review): also compare _calls_agents_<stem> VALUES, not just the
// stem set — proves the shared parseCallsAgents output is identical.
for (const stem of flatStems) {
assert.deepStrictEqual(flat.get(`_calls_agents_${stem}`), nested.get(`_calls_agents_${stem}`),
`agent refs for stem "${stem}" must match between flat and nested loaders`);
assert.deepStrictEqual(flat.get(stem), nested.get(stem),
`requires for stem "${stem}" must match between flat and nested loaders`);
}
} finally {
cleanup(tmpFlat);
}
});
});
describe('regressions: installed-runtime capability surface (#1160)', () => {
// Minimal valid SKILL.md content (frontmatter only — matches what install emits)
function makeSkillMd(stem) {

View File

@@ -0,0 +1,76 @@
// allow-test-rule: source-text-is-the-product #1941
// Workflow .md files are the installed AI instructions — their text IS what the runtime
// loads. Testing text content tests the deployed contract. Per CONTRIBUTING.md exception matrix.
/**
* Regression tests for bug #1941: /gsd-quick worktree executor forks from a stale base —
* up to many commits behind, not just the one-commit gap #1265 already covers.
*
* Root cause: Claude Code's isolation="worktree" forks new worktrees from origin/HEAD, not
* the live local HEAD. When prior local commits (e.g. earlier quick tasks in the same
* session, or this task's own Step 5.6 pre-dispatch plan commit) advance local HEAD without
* an intervening `git push`, origin/HEAD stays pinned to a stale ancestor and the executor's
* worktree_branch_check guard halts with a base-mismatch fatal. The fix ports the
* worktree.base-check auto-degrade pattern already used by execute-phase (#683/#1369) into
* quick.md's single-dispatch path.
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const WORKFLOW_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'quick.md');
describe('quick: pre-dispatch worktree base re-check (#1941)', () => {
test('workflow file exists', () => {
assert.ok(fs.existsSync(WORKFLOW_PATH), 'workflows/quick.md should exist');
});
test('Step 6 runs worktree.base-check before capturing EXPECTED_BASE', () => {
const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
const step6Idx = content.indexOf('**Step 6: Spawn executor**');
const baseCheckIdx = content.indexOf('worktree.base-check', step6Idx);
const expectedBaseIdx = content.indexOf('EXPECTED_BASE=$(git rev-parse HEAD)', step6Idx);
assert.ok(step6Idx !== -1, '"Step 6: Spawn executor" must exist in quick.md');
assert.ok(baseCheckIdx !== -1, 'worktree.base-check must be invoked within Step 6');
assert.ok(expectedBaseIdx !== -1, 'EXPECTED_BASE capture must exist within Step 6');
assert.ok(
baseCheckIdx < expectedBaseIdx,
'worktree.base-check must run BEFORE EXPECTED_BASE is captured so the degrade decision reflects the most current local HEAD'
);
});
test('degrade check references #1941 for traceability', () => {
const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
assert.ok(content.includes('#1941'), 'quick.md must reference #1941');
});
test('degrade check sets USE_WORKTREES=false when shouldDegrade is true', () => {
const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
const baseCheckIdx = content.indexOf('worktree.base-check');
const block = content.slice(baseCheckIdx, baseCheckIdx + 600);
assert.ok(
block.includes('shouldDegrade') && block.includes('USE_WORKTREES=false'),
'degrade check must override USE_WORKTREES=false when shouldDegrade is true'
);
});
test('degrade check guards on RUNTIME=claude (worktree isolation is Claude Code-specific)', () => {
const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
const baseCheckIdx = content.indexOf('worktree.base-check');
const block = content.slice(Math.max(0, baseCheckIdx - 200), baseCheckIdx + 200);
assert.ok(
block.includes('RUNTIME') && (block.includes('"claude"') || block.includes("'claude'")),
'degrade check must guard on RUNTIME=claude'
);
});
test('degrade check names origin/HEAD as the stale fork base', () => {
const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
const step6Idx = content.indexOf('**Step 6: Spawn executor**');
const nextSection = content.indexOf('\n---', step6Idx);
const section = content.slice(step6Idx, nextSection === -1 ? undefined : nextSection);
assert.ok(section.includes('origin/HEAD'), 'Step 6 must name origin/HEAD as the stale fork base');
});
});

View File

@@ -274,7 +274,7 @@
"gsd-core/workflows/pr-branch.md": "dc5598ae8accdecd",
"gsd-core/workflows/profile-user.md": "355af92ac285567f",
"gsd-core/workflows/progress.md": "79a11ce798082054",
"gsd-core/workflows/quick.md": "a6aa362881f4e5da",
"gsd-core/workflows/quick.md": "35582887917ef938",
"gsd-core/workflows/reapply-patches.md": "4dcd6117d0a507ca",
"gsd-core/workflows/remove-phase.md": "23b9eb0858a2535e",
"gsd-core/workflows/remove-workspace.md": "d0bd7e0601138798",

View File

@@ -344,7 +344,7 @@
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
"gsd-core/workflows/profile-user.md": "14263db831230142",
"gsd-core/workflows/progress.md": "893aa3c36983f74b",
"gsd-core/workflows/quick.md": "3bafc0a59cc90d26",
"gsd-core/workflows/quick.md": "5790ceb09aa685be",
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",

View File

@@ -273,7 +273,7 @@
"gsd-core/workflows/pr-branch.md": "ab157cd8e49621dd",
"gsd-core/workflows/profile-user.md": "ff3820a27731ceb8",
"gsd-core/workflows/progress.md": "bd1ecf9207331bda",
"gsd-core/workflows/quick.md": "bd56a6313d5ed43c",
"gsd-core/workflows/quick.md": "da83b1a15b7f1bf7",
"gsd-core/workflows/reapply-patches.md": "ba9406b60f2c4041",
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
"gsd-core/workflows/remove-workspace.md": "f3ab3a88a7e9e1ed",

View File

@@ -277,7 +277,7 @@
"gsd-core/workflows/pr-branch.md": "9923878a4f6a2d91",
"gsd-core/workflows/profile-user.md": "26f74db0a7fcd268",
"gsd-core/workflows/progress.md": "9f326d63afb4b76b",
"gsd-core/workflows/quick.md": "10051a9b41d5d210",
"gsd-core/workflows/quick.md": "68a9dfcd53f0859f",
"gsd-core/workflows/reapply-patches.md": "eb4272145a117904",
"gsd-core/workflows/remove-phase.md": "e336350f8113a328",
"gsd-core/workflows/remove-workspace.md": "e685dfbd736dfd90",

View File

@@ -344,7 +344,7 @@
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
"gsd-core/workflows/profile-user.md": "4fa910d15dea5695",
"gsd-core/workflows/progress.md": "893aa3c36983f74b",
"gsd-core/workflows/quick.md": "5e7dcdd864513b87",
"gsd-core/workflows/quick.md": "9033dbe58443af36",
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",

View File

@@ -309,7 +309,7 @@
"gsd-core/workflows/pr-branch.md": "d13e1cc81de40896",
"gsd-core/workflows/profile-user.md": "05828c8cc61ef384",
"gsd-core/workflows/progress.md": "7bedc431bb55edb8",
"gsd-core/workflows/quick.md": "85fb48e52f0b3725",
"gsd-core/workflows/quick.md": "3e7686705da2af19",
"gsd-core/workflows/reapply-patches.md": "26297b84736e66a4",
"gsd-core/workflows/remove-phase.md": "9ee0fddd11a0d9d4",
"gsd-core/workflows/remove-workspace.md": "19d7465aaa50cb62",

View File

@@ -275,7 +275,7 @@
"gsd-core/workflows/pr-branch.md": "2833905f119b5722",
"gsd-core/workflows/profile-user.md": "5cc032206c99ef71",
"gsd-core/workflows/progress.md": "6b9a84a43dc55af5",
"gsd-core/workflows/quick.md": "89c34e07f1bf6f53",
"gsd-core/workflows/quick.md": "7ddfe17f048541ec",
"gsd-core/workflows/reapply-patches.md": "8fd59e24b486f180",
"gsd-core/workflows/remove-phase.md": "e262654e319d1bc4",
"gsd-core/workflows/remove-workspace.md": "ceddfeef5f2d6754",

View File

@@ -344,7 +344,7 @@
"gsd-core/workflows/pr-branch.md": "c67d90c65da47168",
"gsd-core/workflows/profile-user.md": "8c943983241260b5",
"gsd-core/workflows/progress.md": "65aabee5e8a6dd82",
"gsd-core/workflows/quick.md": "db98fdbe79d68ffc",
"gsd-core/workflows/quick.md": "762256cf6d177c06",
"gsd-core/workflows/reapply-patches.md": "ba9406b60f2c4041",
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
"gsd-core/workflows/remove-workspace.md": "433affcd1a200826",

View File

@@ -274,7 +274,7 @@
"gsd-core/workflows/pr-branch.md": "ecabd55e4eabf229",
"gsd-core/workflows/profile-user.md": "de5030437226cf2c",
"gsd-core/workflows/progress.md": "f18db000584d9cb1",
"gsd-core/workflows/quick.md": "66b26d6fe2bd4d71",
"gsd-core/workflows/quick.md": "5044ce4e7512e174",
"gsd-core/workflows/reapply-patches.md": "158083a310859594",
"gsd-core/workflows/remove-phase.md": "fce799aae3ab2715",
"gsd-core/workflows/remove-workspace.md": "8facde381657dd71",

View File

@@ -344,7 +344,7 @@
"gsd-core/workflows/pr-branch.md": "ab157cd8e49621dd",
"gsd-core/workflows/profile-user.md": "203ebe3f8f3876a8",
"gsd-core/workflows/progress.md": "9381c59676ccb937",
"gsd-core/workflows/quick.md": "11ba83f6350462fb",
"gsd-core/workflows/quick.md": "fd7461e5a92fe450",
"gsd-core/workflows/reapply-patches.md": "becf9728cdb124c4",
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
"gsd-core/workflows/remove-workspace.md": "fc83f362a2d0a1b7",

View File

@@ -310,7 +310,7 @@
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
"gsd-core/workflows/profile-user.md": "5abfae83739fa978",
"gsd-core/workflows/progress.md": "893aa3c36983f74b",
"gsd-core/workflows/quick.md": "d722803b72217377",
"gsd-core/workflows/quick.md": "e265e01cfe117215",
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",

View File

@@ -344,7 +344,7 @@
"gsd-core/workflows/pr-branch.md": "929b7cb0c99c7b9e",
"gsd-core/workflows/profile-user.md": "248d59a31948e0ed",
"gsd-core/workflows/progress.md": "8fc3404087f50b95",
"gsd-core/workflows/quick.md": "3866a5259e8912d8",
"gsd-core/workflows/quick.md": "10cc8ddd4bc3f0ee",
"gsd-core/workflows/reapply-patches.md": "a0e9b53f90abceb2",
"gsd-core/workflows/remove-phase.md": "dea4661e8f89596f",
"gsd-core/workflows/remove-workspace.md": "446847e71aa52504",

View File

@@ -274,7 +274,7 @@
"gsd-core/workflows/pr-branch.md": "cef0f65b16d500b4",
"gsd-core/workflows/profile-user.md": "263c0693563d98da",
"gsd-core/workflows/progress.md": "3b1b2142a74af85c",
"gsd-core/workflows/quick.md": "518740e30dcd4da3",
"gsd-core/workflows/quick.md": "0a9a7dacc73f8e53",
"gsd-core/workflows/reapply-patches.md": "de0ee8acfe7245b2",
"gsd-core/workflows/remove-phase.md": "e8ae4fbbfac700f0",
"gsd-core/workflows/remove-workspace.md": "4ac64de862dc650e",

View File

@@ -274,7 +274,7 @@
"gsd-core/workflows/pr-branch.md": "79fd55b88ea2db9c",
"gsd-core/workflows/profile-user.md": "672821e6b1266645",
"gsd-core/workflows/progress.md": "94768f835b0b8908",
"gsd-core/workflows/quick.md": "8ac5f3919f754499",
"gsd-core/workflows/quick.md": "9bbf907e39688638",
"gsd-core/workflows/reapply-patches.md": "21b38c374f19fd78",
"gsd-core/workflows/remove-phase.md": "a46c2fe853bf4e86",
"gsd-core/workflows/remove-workspace.md": "ae0e1c6d4438d663",

View File

@@ -274,7 +274,7 @@
"gsd-core/workflows/pr-branch.md": "acd59f915d018ad4",
"gsd-core/workflows/profile-user.md": "c4313672b81b5bcd",
"gsd-core/workflows/progress.md": "18813a345bd2343a",
"gsd-core/workflows/quick.md": "0fcad72e7dbe99c9",
"gsd-core/workflows/quick.md": "3acd391cc0c3813a",
"gsd-core/workflows/reapply-patches.md": "d449a23d3acf6379",
"gsd-core/workflows/remove-phase.md": "e7a6af429b36e77b",
"gsd-core/workflows/remove-workspace.md": "b5e60fbb33b3e33a",

View File

@@ -344,7 +344,7 @@
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
"gsd-core/workflows/profile-user.md": "e23bea0a69c0bb4b",
"gsd-core/workflows/progress.md": "893aa3c36983f74b",
"gsd-core/workflows/quick.md": "1bc59c4d97fed876",
"gsd-core/workflows/quick.md": "5747c2d8a37affbb",
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",

View File

@@ -58,7 +58,7 @@
"pr-branch.md": 15963,
"profile-user.md": 21246,
"progress.md": 30599,
"quick.md": 49183,
"quick.md": 50452,
"reapply-patches.md": 20312,
"remove-phase.md": 8513,
"remove-workspace.md": 7551,