fix(#2287): surface unresolved deferred-items.md entries in progress + audit-uat (#2318)

The executor SCOPE BOUNDARY convention (agents/gsd-executor.md) logs
out-of-scope discoveries to a phase directory's deferred-items.md, but no
reader ever consumed it — forensic_audit, cmdAuditUat, and capture --list
all skipped it — so deferred items were permanently invisible.

cmdAuditUat (src/uat.cts) now scans each phase dir's deferred-items.md via
a new parseDeferredItems (reusing the collectSection/splitGapsEntries/
extractGapEntryFields seams) and surfaces entries whose status != resolved
(fail-safe: a missing/garbled status surfaces rather than hides, matching
the false-negative-averse posture of #2286). forensic_audit
(gsd-core/workflows/progress.md) gains Check 7 that globs
.planning/phases/*/deferred-items.md and reports unresolved entries.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-07-15 21:40:59 -04:00
committed by GitHub
parent a22333034b
commit b041f101fb
23 changed files with 466 additions and 24 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 2318
---
**Deferred out-of-scope findings logged to `deferred-items.md` are now surfaced** — the executor's SCOPE BOUNDARY convention writes discoveries to a phase directory's `deferred-items.md`, but nothing read it back, so those items were permanently invisible. `/gsd-progress`'s forensic audit and `audit-uat` now glob `.planning/phases/*/deferred-items.md` and surface unresolved entries. (#2287)

View File

@@ -264,6 +264,7 @@ Track: `outstanding_debt` — `summary.total_items` from the audit.
|-------|------|-------|
| {phase} | {filename} | {pending_count} pending, {skipped_count} skipped, {blocked_count} blocked |
| {phase} | {filename} | human_needed — {count} items |
| {phase} | {filename} | {unresolved_count} deferred items |
Review: `/gsd:audit-uat ${GSD_WS}` — full cross-phase audit
Resume testing: `/gsd:verify-work {phase} ${GSD_WS}` — retest specific phase
@@ -675,7 +676,7 @@ If `--forensic` IS present: after the standard report and routing suggestion hav
## Forensic Integrity Audit
Running 6 deep checks against project state...
Running 7 deep checks against project state...
Run each check in order. For each check, emit ✓ (pass) or ⚠ (warning) with concrete evidence when a problem is found.
@@ -748,11 +749,24 @@ Emit:
- ✓ `Working tree clean` — if no modified files outside `.planning/`
- ⚠ `Uncommitted changes in source files` — list up to 10 file paths
**Check 7 — Unresolved deferred items**
Glob every phase directory's SCOPE BOUNDARY log (executor writes out-of-scope discoveries here per `agents/gsd-executor.md`):
```bash
ls .planning/phases/*/deferred-items.md 2>/dev/null || true
```
For each `deferred-items.md` found, read its entries (bullet list, one entry per top-level `- ` line, continuation lines indented beneath it). An entry is RESOLVED only if it carries an explicit `status: resolved` field (case-insensitive) on one of its lines; every other entry — including one with no `status:` field at all — is UNRESOLVED and must be surfaced (fail-safe: never silently drop a possibly-open item).
Emit:
- ✓ `No unresolved deferred items` — if no `deferred-items.md` files exist, or every entry in every file is `status: resolved`
- ⚠ `Unresolved deferred items found` — list each file's phase directory and its unresolved entry text (max 5 per file, truncated at 80 chars)
---
After all 6 checks, display the verdict:
After all 7 checks, display the verdict:
**If all 6 checks passed:**
**If all 7 checks passed:**
```
### Verdict: CLEAN
@@ -773,6 +787,7 @@ Then for each failed check, add a concrete next action:
- Check 4 (memory pending): `Review the flagged memory entries and resolve or clear them`
- Check 5 (blocking todos): `Complete the operational steps in .planning/todos/pending/ before continuing`
- Check 6 (uncommitted code): `Commit or stash the uncommitted changes before advancing`
- Check 7 (unresolved deferred items): `Address each deferred item and mark it status: resolved in its deferred-items.md, or fold it into the roadmap`
- Check 1 (STATE inconsistency): `Run /gsd:verify-work ${PHASE} ${GSD_WS} to reconcile state`
</step>

View File

@@ -40,7 +40,7 @@ import { requireSafePath, sanitizeForDisplay } from './security.cjs';
// ─── Types ────────────────────────────────────────────────────────────────────
type UatResult = string;
type UatCategory = 'server_blocked' | 'device_needed' | 'build_needed' | 'third_party' | 'blocked' | 'skipped_unresolved' | 'pending' | 'human_uat' | 'unknown';
type UatCategory = 'server_blocked' | 'device_needed' | 'build_needed' | 'third_party' | 'blocked' | 'skipped_unresolved' | 'pending' | 'human_uat' | 'unknown' | 'deferred';
interface UatItem {
test?: number;
@@ -57,7 +57,7 @@ interface UatFileResult {
phase_dir: string;
file: string;
file_path: string;
type: 'uat' | 'verification';
type: 'uat' | 'verification' | 'deferred';
status: string;
items: UatItem[];
}
@@ -129,6 +129,30 @@ function cmdAuditUat(cwd: string, raw: boolean): void {
}
}
}
// Process deferred-items.md (#2287) — the SCOPE BOUNDARY convention
// (agents/gsd-executor.md) has the executor log out-of-scope discoveries
// to this file; nothing previously read it back. Surface every
// UNRESOLVED entry (see parseDeferredItems for the resolved/unresolved
// parsing rule) as a 'deferred'-typed result, keeping deferred-items.md
// itself the single source of truth — no duplicate pending-todo entry
// required.
const deferredFile = 'deferred-items.md';
if (files.includes(deferredFile)) {
const content = fs.readFileSync(path.join(phaseDir, deferredFile), 'utf-8');
const items = parseDeferredItems(content);
if (items.length > 0) {
results.push({
phase: phaseNum,
phase_dir: dir,
file: deferredFile,
file_path: toPosixPath(path.relative(cwd, path.join(phaseDir, deferredFile))),
type: 'deferred',
status: 'unresolved',
items,
});
}
}
}
// Compute summary
@@ -445,6 +469,62 @@ function parseGapsItems(content: string): UatItem[] {
return items;
}
// ─── parseDeferredItems ────────────────────────────────────────────────────────
/**
* Extract unresolved entries from a phase directory's `deferred-items.md`
* (#2287) — the SCOPE BOUNDARY convention `agents/gsd-executor.md` instructs
* the executor to follow: "Log out-of-scope discoveries to `deferred-items.md`
* in the phase directory". Nothing previously read this file back, so a
* deferred entry was permanently invisible outside the phase directory.
*
* The writer convention (unchanged by this fix, per the issue's stated
* out-of-scope) emits a plain bullet list, typically under a `## Deferred
* Items` heading (see the issue's own reproduction fixture), one entry per
* top-level `- ` line with optional indented continuation lines. There is no
* mandated heading text, so if no `## Deferred Items`-shaped level-2 heading
* is found, the WHOLE file is scanned as the entry list — fail-safe, so an
* agent writing a differently-headed (or headless) deferred-items.md still
* has its entries surfaced rather than silently skipped.
*
* Reuses the same per-line field/entry-splitting seams as `parseGapsItems`
* (`splitGapsEntries`, `extractGapEntryFields`, `rawGapEntryText`) — an entry
* is RESOLVED only when it carries an explicit `status: resolved` field
* (case-insensitive), mirroring the established Gaps convention so a human or
* follow-up agent can mark a deferred item done in place, keeping
* `deferred-items.md` the single source of truth (no duplicate
* `.planning/todos/pending/*.md` entry required). Every other entry —
* including one with no `status:` field at all — is UNRESOLVED and is
* surfaced.
*/
function parseDeferredItems(content: string): UatItem[] {
const deferredSection = collectSection(
content,
(h) => /^deferred\s+items$/i.test(h.text) && h.level === 2,
{ levelBounded: true },
);
const sectionBody = deferredSection ? deferredSection.body : content;
const items: UatItem[] = [];
for (const entryLines of splitGapsEntries(sectionBody)) {
const fields = extractGapEntryFields(entryLines);
const rawStatus = fields.status;
if (rawStatus && rawStatus.toLowerCase() === 'resolved') continue;
const text = rawGapEntryText(entryLines);
if (!text) continue;
items.push({
name: text,
result: 'unresolved',
category: 'deferred',
});
}
return items;
}
/**
* Split a `## Gaps` section body into per-entry line groups on TOP-LEVEL
* `- ` bullet openers.
@@ -747,4 +827,5 @@ export = {
cmdRenderCheckpoint,
parseCurrentTest,
buildCheckpoint,
parseDeferredItems,
};

View File

@@ -0,0 +1,341 @@
/**
* #2287 — deferred-items.md has no reader anywhere in gsd-core.
*
* The SCOPE BOUNDARY convention (`agents/gsd-executor.md`) instructs the
* executor to log out-of-scope discoveries to `deferred-items.md` inside the
* phase directory. Nothing read that file back: `cmdAuditUat` (src/uat.cts)
* filtered phase-directory files down to `*-UAT.md` / `*-VERIFICATION.md`
* only, and the `forensic_audit` workflow step (gsd-core/workflows/
* progress.md) ran 6 checks, none of which globbed the phase-directory
* `deferred-items.md` path. An entry written there was permanently invisible.
*
* This fix:
* - `cmdAuditUat` gains a `deferred-items.md` scan per phase directory,
* surfacing every UNRESOLVED entry as a `type: 'deferred'` result. An
* entry is resolved only when it carries an explicit `status: resolved`
* field (mirroring the established `## Gaps` convention from #2286) — a
* missing/garbled status fails safe and is surfaced.
* - `forensic_audit` gains a 7th check that globs the same path and reports
* unresolved entries with the same ✓/⚠ semantics as the other 6 checks.
*
* `deferred-items.md` remains the single source of truth — no duplicate
* `.planning/todos/pending/*.md` entry is required.
*/
'use strict';
const { test, describe, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const fc = require('./helpers/fast-check-setup.cjs');
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
const { parseDeferredItems } = require('../gsd-core/bin/lib/uat.cjs');
// ─── cmdAuditUat behavioral coverage ───────────────────────────────────────
describe('#2287 cmdAuditUat: deferred-items.md awareness', () => {
let tmpDir;
beforeEach(() => {
tmpDir = createTempProject();
});
afterEach(() => {
cleanup(tmpDir);
});
test('no deferred-items.md present (0 entries) → no results, no false positive', () => {
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-foundation');
fs.mkdirSync(phaseDir, { recursive: true });
fs.writeFileSync(path.join(phaseDir, '.gitkeep'), '');
const result = runGsdTools('audit-uat --raw', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
assert.deepStrictEqual(output.results, []);
assert.strictEqual(output.summary.total_items, 0);
assert.strictEqual(output.summary.total_files, 0);
});
test('deferred-items.md with only a resolved entry (0 unresolved) → no result surfaced', () => {
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-foundation');
fs.mkdirSync(phaseDir, { recursive: true });
fs.writeFileSync(path.join(phaseDir, 'deferred-items.md'), [
'## Deferred Items',
'',
'- Already handled unrelated lint warning.',
' status: resolved',
].join('\n'));
const result = runGsdTools('audit-uat --raw', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
assert.deepStrictEqual(output.results, [],
'a fully-resolved deferred-items.md must not surface any result');
assert.strictEqual(output.summary.total_items, 0);
});
test('deferred-items.md with 1 unresolved entry → surfaced in structured JSON output', () => {
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-foundation');
fs.mkdirSync(phaseDir, { recursive: true });
fs.writeFileSync(path.join(phaseDir, 'deferred-items.md'), [
'## Deferred Items',
'',
'- Found an unrelated pre-existing test failure in `some-other-module` while working on',
' this phase\'s task. Out of scope for this task — logged here per SCOPE BOUNDARY.',
].join('\n'));
const result = runGsdTools('audit-uat --raw', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
assert.strictEqual(output.summary.total_items, 1);
assert.strictEqual(output.summary.total_files, 1);
assert.strictEqual(output.summary.by_category.deferred, 1);
assert.strictEqual(output.summary.by_phase['01'], 1);
const deferredResult = output.results.find(r => r.type === 'deferred');
assert.ok(deferredResult, 'a deferred-typed result must be present');
assert.strictEqual(deferredResult.phase, '01');
assert.strictEqual(deferredResult.file, 'deferred-items.md');
assert.strictEqual(
deferredResult.file_path,
'.planning/phases/01-foundation/deferred-items.md',
);
assert.strictEqual(deferredResult.items.length, 1);
assert.match(deferredResult.items[0].name, /unrelated pre-existing test failure/);
assert.strictEqual(deferredResult.items[0].result, 'unresolved');
assert.strictEqual(deferredResult.items[0].category, 'deferred');
});
test('deferred-items.md with 2+ entries (mixed resolved/unresolved) → only unresolved surfaced', () => {
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-foundation');
fs.mkdirSync(phaseDir, { recursive: true });
fs.writeFileSync(path.join(phaseDir, 'deferred-items.md'), [
'## Deferred Items',
'',
'- First unrelated finding, still open.',
'- Second unrelated finding, also still open.',
'- Third finding, already fixed separately.',
' status: resolved',
].join('\n'));
const result = runGsdTools('audit-uat --raw', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
const deferredResult = output.results.find(r => r.type === 'deferred');
assert.ok(deferredResult);
assert.strictEqual(deferredResult.items.length, 2,
'exactly the 2 unresolved entries must surface; the resolved 3rd must not');
const names = deferredResult.items.map(i => i.name);
assert.ok(names.some(n => n.includes('First unrelated finding')));
assert.ok(names.some(n => n.includes('Second unrelated finding')));
assert.ok(!names.some(n => n.includes('Third finding')));
});
test('deferred entries surface across multiple phase directories', () => {
const phase1 = path.join(tmpDir, '.planning', 'phases', '01-foundation');
const phase2 = path.join(tmpDir, '.planning', 'phases', '02-auth');
fs.mkdirSync(phase1, { recursive: true });
fs.mkdirSync(phase2, { recursive: true });
fs.writeFileSync(path.join(phase1, 'deferred-items.md'), [
'## Deferred Items',
'',
'- Phase 1 unrelated finding.',
].join('\n'));
fs.writeFileSync(path.join(phase2, 'deferred-items.md'), [
'## Deferred Items',
'',
'- Phase 2 unrelated finding.',
].join('\n'));
const result = runGsdTools('audit-uat --raw', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
const deferredResults = output.results.filter(r => r.type === 'deferred');
assert.strictEqual(deferredResults.length, 2);
assert.strictEqual(output.summary.total_items, 2);
assert.strictEqual(output.summary.by_phase['01'], 1);
assert.strictEqual(output.summary.by_phase['02'], 1);
});
test('an entry with a garbled/missing status fails safe and is surfaced (not silently dropped)', () => {
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-foundation');
fs.mkdirSync(phaseDir, { recursive: true });
fs.writeFileSync(path.join(phaseDir, 'deferred-items.md'), [
'## Deferred Items',
'',
'- An entry with no status field at all.',
].join('\n'));
const result = runGsdTools('audit-uat --raw', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
assert.strictEqual(output.summary.total_items, 1,
'missing status must SURFACE the entry, not silently drop it');
});
test('existing UAT/VERIFICATION scanning is unchanged when a deferred-items.md is also present', () => {
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-foundation');
fs.mkdirSync(phaseDir, { recursive: true });
fs.writeFileSync(path.join(phaseDir, '01-UAT.md'), [
'---',
'status: testing',
'phase: 01-foundation',
'started: 2025-01-01T00:00:00Z',
'updated: 2025-01-01T00:00:00Z',
'---',
'',
'## Tests',
'',
'### 1. Login Form',
'expected: Form displays with email and password fields',
'result: pending',
].join('\n'));
fs.writeFileSync(path.join(phaseDir, 'deferred-items.md'), [
'## Deferred Items',
'',
'- An unrelated out-of-scope finding.',
].join('\n'));
const result = runGsdTools('audit-uat --raw', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
assert.strictEqual(output.results.length, 2, 'both the UAT file and deferred-items.md must surface as separate results');
const uatResult = output.results.find(r => r.type === 'uat');
const deferredResult = output.results.find(r => r.type === 'deferred');
assert.ok(uatResult, 'existing uat-type result must still be present');
assert.strictEqual(uatResult.items.length, 1);
assert.strictEqual(uatResult.items[0].result, 'pending');
assert.ok(deferredResult, 'new deferred-type result must be present');
assert.strictEqual(deferredResult.items.length, 1);
});
});
// ─── forensic_audit workflow-prose source-contract guard ──────────────────
const PROGRESS_MD = path.join(__dirname, '..', 'gsd-core', 'workflows', 'progress.md');
describe('#2287 progress.md forensic_audit: deferred-items.md contract', () => {
const content = fs.readFileSync(PROGRESS_MD, 'utf-8');
const stepStart = content.indexOf('<step name="forensic_audit">');
const stepEnd = content.indexOf('</step>', stepStart);
const section = stepStart !== -1 && stepEnd !== -1 ? content.slice(stepStart, stepEnd) : '';
test('forensic_audit step exists', () => {
assert.notEqual(stepStart, -1, 'progress.md must contain the forensic_audit step');
});
test('forensic_audit now runs 7 checks (was 6) and globs deferred-items.md', () => {
assert.ok(/running 7 deep checks/i.test(section),
'forensic_audit must advertise 7 deep checks (was 6) now that deferred-items.md is read');
assert.ok(/\.planning\/phases\/\*\/deferred-items\.md/.test(section),
'forensic_audit must glob .planning/phases/*/deferred-items.md');
});
test('the new check reports unresolved deferred items with the same ✓/⚠ semantics as the other checks', () => {
assert.ok(/check\s*7/i.test(section),
'a 7th check must be present');
assert.ok(/unresolved deferred items/i.test(section),
'the check must be framed around unresolved deferred items');
assert.ok(/✓[^\n]*no unresolved deferred items/i.test(section),
'the check must emit a ✓ pass line when no unresolved deferred items exist');
assert.ok(/⚠[^\n]*unresolved deferred items found/i.test(section),
'the check must emit a ⚠ warning line when unresolved deferred items exist');
});
test('an entry is resolved only via an explicit status: resolved field (fail-safe otherwise)', () => {
assert.ok(/status:\s*resolved/i.test(section),
'the resolved/unresolved parsing rule must be documented in the step prose');
});
test('the verdict summary now gates on 7 checks (was 6)', () => {
assert.ok(/after all 7 checks/i.test(section),
'the verdict section must say "after all 7 checks"');
assert.ok(/if all 7 checks passed/i.test(section),
'the verdict section must say "if all 7 checks passed"');
assert.ok(!/after all 6 checks/i.test(section) && !/if all 6 checks passed/i.test(section),
'stale "6 checks" phrasing must not remain in the step');
});
});
// ─── parseDeferredItems property test ──────────────────────────────────────
describe('#2287 parseDeferredItems: property (status: resolved fail-safe)', () => {
// Single-line entry text: no newlines (would break bullet-entry splitting),
// non-empty after trim, and never itself SHAPED like a `status:` field line
// (that would be indistinguishable from a real field regardless of intent).
const plainText = fc.string({ minLength: 1, maxLength: 40 })
.map((s) => s.replace(/[\r\n]/g, ' ').trim())
.filter((s) => s.length > 0 && !/^status:/i.test(s));
// Decoy: entry text that CONTAINS a `status: resolved`-shaped substring
// mid-line (not at line start) — must never be misread as a resolved
// marker, since extractGapEntryFields only recognises a field anchored to
// the START of its own trimmed line (see parseDeferredItems' doc comment).
const decoyText = plainText.map((s) => `${s} status: resolved trailing note`);
const textArb = fc.oneof(plainText, decoyText);
const entryArb = fc.record({ text: textArb, resolved: fc.boolean() });
test('property: an entry is surfaced iff it is NOT marked status: resolved; surfaced count == non-resolved count', () => {
fc.assert(
fc.property(
fc.array(entryArb, { maxLength: 20 }),
(rawEntries) => {
// Index-prefix for uniqueness so surfaced items can be mapped back
// to their source entry unambiguously even with colliding random text.
const entries = rawEntries.map((e, i) => ({ text: `E${i}_${e.text}`, resolved: e.resolved }));
const lines = ['## Deferred Items', ''];
for (const e of entries) {
lines.push(`- ${e.text}`);
if (e.resolved) lines.push(' status: resolved');
}
const content = lines.join('\n');
const items = parseDeferredItems(content);
const surfacedNames = new Set(items.map((it) => it.name));
const expectedUnresolved = entries.filter((e) => !e.resolved);
const expectedResolved = entries.filter((e) => e.resolved);
// Total surfaced count equals the count of non-resolved entries.
assert.strictEqual(items.length, expectedUnresolved.length);
// Every non-resolved entry IS surfaced (including status:-shaped
// decoy substrings embedded mid-line — those must not flip the
// outcome).
for (const e of expectedUnresolved) {
assert.ok(surfacedNames.has(e.text), `expected unresolved entry to surface: ${e.text}`);
}
// No status:-resolved entry is EVER surfaced.
for (const e of expectedResolved) {
assert.ok(!surfacedNames.has(e.text), `status: resolved entry must never surface: ${e.text}`);
}
// Every returned item carries the fixed deferred category/result shape.
for (const item of items) {
assert.strictEqual(item.result, 'unresolved');
assert.strictEqual(item.category, 'deferred');
}
}
)
);
});
});

View File

@@ -279,7 +279,7 @@
"gsd-core/workflows/plant-seed.md": "6cf726be6427a35a",
"gsd-core/workflows/pr-branch.md": "dc5598ae8accdecd",
"gsd-core/workflows/profile-user.md": "355af92ac285567f",
"gsd-core/workflows/progress.md": "4f0c285ce35553e3",
"gsd-core/workflows/progress.md": "0e41db02a270a2b2",
"gsd-core/workflows/quick.md": "424c0eb61769e747",
"gsd-core/workflows/reapply-patches.md": "4dcd6117d0a507ca",
"gsd-core/workflows/remove-phase.md": "23b9eb0858a2535e",

View File

@@ -350,7 +350,7 @@
"gsd-core/workflows/plant-seed.md": "10b92ae08a6fdede",
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
"gsd-core/workflows/profile-user.md": "14263db831230142",
"gsd-core/workflows/progress.md": "281a77e800d11614",
"gsd-core/workflows/progress.md": "9457fb10437c9402",
"gsd-core/workflows/quick.md": "25f0cc40bc22ed3e",
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",

View File

@@ -349,7 +349,7 @@
"gsd-core/workflows/plant-seed.md": "fbe964fcdb244802",
"gsd-core/workflows/pr-branch.md": "513f6cff722eff2d",
"gsd-core/workflows/profile-user.md": "3b34dcb337d50f4b",
"gsd-core/workflows/progress.md": "eb0885959b4ac66e",
"gsd-core/workflows/progress.md": "f6739fc007de6f66",
"gsd-core/workflows/quick.md": "f1b474b46327034f",
"gsd-core/workflows/reapply-patches.md": "44a96b52b975e9bb",
"gsd-core/workflows/remove-phase.md": "8effc8742d58a11a",

View File

@@ -278,7 +278,7 @@
"gsd-core/workflows/plant-seed.md": "af50e9f10d3cc6e1",
"gsd-core/workflows/pr-branch.md": "ab157cd8e49621dd",
"gsd-core/workflows/profile-user.md": "ff3820a27731ceb8",
"gsd-core/workflows/progress.md": "5ce5a11b468bd419",
"gsd-core/workflows/progress.md": "8aa2fcebb8b4876e",
"gsd-core/workflows/quick.md": "b4237633eaf5a246",
"gsd-core/workflows/reapply-patches.md": "ba9406b60f2c4041",
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",

View File

@@ -282,7 +282,7 @@
"gsd-core/workflows/plant-seed.md": "a2cdd513663226f1",
"gsd-core/workflows/pr-branch.md": "9923878a4f6a2d91",
"gsd-core/workflows/profile-user.md": "26f74db0a7fcd268",
"gsd-core/workflows/progress.md": "c5ce768042ca9b8b",
"gsd-core/workflows/progress.md": "a2d948b084a48500",
"gsd-core/workflows/quick.md": "797b1c8d62c3d33d",
"gsd-core/workflows/reapply-patches.md": "eb4272145a117904",
"gsd-core/workflows/remove-phase.md": "e336350f8113a328",

View File

@@ -350,7 +350,7 @@
"gsd-core/workflows/plant-seed.md": "10b92ae08a6fdede",
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
"gsd-core/workflows/profile-user.md": "4fa910d15dea5695",
"gsd-core/workflows/progress.md": "281a77e800d11614",
"gsd-core/workflows/progress.md": "9457fb10437c9402",
"gsd-core/workflows/quick.md": "0bd83bd88c5e63d1",
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",

View File

@@ -385,7 +385,7 @@
"gsd-core/workflows/plant-seed.md": "5b07de07e4593281",
"gsd-core/workflows/pr-branch.md": "d13e1cc81de40896",
"gsd-core/workflows/profile-user.md": "05828c8cc61ef384",
"gsd-core/workflows/progress.md": "7ec6495e273904cf",
"gsd-core/workflows/progress.md": "03f12b223eb3a96e",
"gsd-core/workflows/quick.md": "bf70b7beb314aa50",
"gsd-core/workflows/reapply-patches.md": "26297b84736e66a4",
"gsd-core/workflows/remove-phase.md": "9ee0fddd11a0d9d4",

View File

@@ -280,7 +280,7 @@
"gsd-core/workflows/plant-seed.md": "21e461cd39e5181b",
"gsd-core/workflows/pr-branch.md": "2833905f119b5722",
"gsd-core/workflows/profile-user.md": "5cc032206c99ef71",
"gsd-core/workflows/progress.md": "9bd7472d2efe14dd",
"gsd-core/workflows/progress.md": "449b7f4de1bef7ae",
"gsd-core/workflows/quick.md": "4a53899b69b8c801",
"gsd-core/workflows/reapply-patches.md": "8fd59e24b486f180",
"gsd-core/workflows/remove-phase.md": "e262654e319d1bc4",

View File

@@ -350,7 +350,7 @@
"gsd-core/workflows/plant-seed.md": "7bccd151ce7b69f2",
"gsd-core/workflows/pr-branch.md": "c67d90c65da47168",
"gsd-core/workflows/profile-user.md": "8c943983241260b5",
"gsd-core/workflows/progress.md": "a980339715a431f0",
"gsd-core/workflows/progress.md": "0f03aaf96f5abf7e",
"gsd-core/workflows/quick.md": "3b37350964cfe391",
"gsd-core/workflows/reapply-patches.md": "ba9406b60f2c4041",
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",

View File

@@ -279,7 +279,7 @@
"gsd-core/workflows/plant-seed.md": "f862f77fca983749",
"gsd-core/workflows/pr-branch.md": "ecabd55e4eabf229",
"gsd-core/workflows/profile-user.md": "de5030437226cf2c",
"gsd-core/workflows/progress.md": "2641aa5457ab784f",
"gsd-core/workflows/progress.md": "debe980226d9260d",
"gsd-core/workflows/quick.md": "01560a250a6040ea",
"gsd-core/workflows/reapply-patches.md": "158083a310859594",
"gsd-core/workflows/remove-phase.md": "fce799aae3ab2715",

View File

@@ -350,7 +350,7 @@
"gsd-core/workflows/plant-seed.md": "ffa5774304243649",
"gsd-core/workflows/pr-branch.md": "ab157cd8e49621dd",
"gsd-core/workflows/profile-user.md": "203ebe3f8f3876a8",
"gsd-core/workflows/progress.md": "fb1ea7476a839fd8",
"gsd-core/workflows/progress.md": "c2a8a79af6603e70",
"gsd-core/workflows/quick.md": "4ddd6d3214f57f57",
"gsd-core/workflows/reapply-patches.md": "becf9728cdb124c4",
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",

View File

@@ -343,7 +343,7 @@
"gsd-core/workflows/plant-seed.md": "10b92ae08a6fdede",
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
"gsd-core/workflows/profile-user.md": "5abfae83739fa978",
"gsd-core/workflows/progress.md": "281a77e800d11614",
"gsd-core/workflows/progress.md": "9457fb10437c9402",
"gsd-core/workflows/quick.md": "8dcda37c46954ebe",
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",

View File

@@ -350,7 +350,7 @@
"gsd-core/workflows/plant-seed.md": "b8dad652e31c2318",
"gsd-core/workflows/pr-branch.md": "929b7cb0c99c7b9e",
"gsd-core/workflows/profile-user.md": "248d59a31948e0ed",
"gsd-core/workflows/progress.md": "8e01238a8ac6b73d",
"gsd-core/workflows/progress.md": "352920a61ed4f4db",
"gsd-core/workflows/quick.md": "774d646560a74c93",
"gsd-core/workflows/reapply-patches.md": "a0e9b53f90abceb2",
"gsd-core/workflows/remove-phase.md": "dea4661e8f89596f",

View File

@@ -246,7 +246,7 @@
"gsd-core/workflows/plant-seed.md": "10b92ae08a6fdede",
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
"gsd-core/workflows/profile-user.md": "1bac7f69142801ef",
"gsd-core/workflows/progress.md": "281a77e800d11614",
"gsd-core/workflows/progress.md": "9457fb10437c9402",
"gsd-core/workflows/quick.md": "31787c1e8a867e3f",
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",

View File

@@ -279,7 +279,7 @@
"gsd-core/workflows/plant-seed.md": "0a92ba12993ac261",
"gsd-core/workflows/pr-branch.md": "cef0f65b16d500b4",
"gsd-core/workflows/profile-user.md": "263c0693563d98da",
"gsd-core/workflows/progress.md": "b1b3f88614d6b9cc",
"gsd-core/workflows/progress.md": "4ceb0f6f5e34c4a4",
"gsd-core/workflows/quick.md": "d0fffe516abaf47a",
"gsd-core/workflows/reapply-patches.md": "de0ee8acfe7245b2",
"gsd-core/workflows/remove-phase.md": "e8ae4fbbfac700f0",

View File

@@ -279,7 +279,7 @@
"gsd-core/workflows/plant-seed.md": "856ad565b2eb0c47",
"gsd-core/workflows/pr-branch.md": "79fd55b88ea2db9c",
"gsd-core/workflows/profile-user.md": "672821e6b1266645",
"gsd-core/workflows/progress.md": "e7d05715aff75881",
"gsd-core/workflows/progress.md": "00de1afbe907efa5",
"gsd-core/workflows/quick.md": "0529abcf9913ec04",
"gsd-core/workflows/reapply-patches.md": "21b38c374f19fd78",
"gsd-core/workflows/remove-phase.md": "a46c2fe853bf4e86",

View File

@@ -279,7 +279,7 @@
"gsd-core/workflows/plant-seed.md": "6cf61f540cdbd8b9",
"gsd-core/workflows/pr-branch.md": "acd59f915d018ad4",
"gsd-core/workflows/profile-user.md": "c4313672b81b5bcd",
"gsd-core/workflows/progress.md": "d4a4f96975419d09",
"gsd-core/workflows/progress.md": "7b6b9564b34fcabb",
"gsd-core/workflows/quick.md": "71fbcf38e2305de0",
"gsd-core/workflows/reapply-patches.md": "d449a23d3acf6379",
"gsd-core/workflows/remove-phase.md": "e7a6af429b36e77b",

View File

@@ -350,7 +350,7 @@
"gsd-core/workflows/plant-seed.md": "10b92ae08a6fdede",
"gsd-core/workflows/pr-branch.md": "c87db7ac8c28be1b",
"gsd-core/workflows/profile-user.md": "e23bea0a69c0bb4b",
"gsd-core/workflows/progress.md": "281a77e800d11614",
"gsd-core/workflows/progress.md": "9457fb10437c9402",
"gsd-core/workflows/quick.md": "7ad8d250c536c4dd",
"gsd-core/workflows/reapply-patches.md": "39050f72601aec89",
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",

View File

@@ -58,7 +58,7 @@
"plant-seed.md": 11785,
"pr-branch.md": 15963,
"profile-user.md": 21246,
"progress.md": 30628,
"progress.md": 31789,
"quick.md": 50699,
"reapply-patches.md": 20312,
"remove-phase.md": 8513,