* test(#3645): failing-first agent tracked-source contract rows * fix(#3645): tracked-source rule for planner and pattern-mapper paths * Revert "fix(#3645): tracked-source rule for planner and pattern-mapper paths" This reverts commit 61f05e947bbdaf3b4897240c3819d349215744fb. * fix(#3645): tracked-source rule at the spawn seam and mapper gate * fix(#3645): review fixes - bounded block, ack merge assertion, git wording * fix(#3645): fit the tracked-source block under the 1168 ceiling * chore(#3645): backfill changeset pr number --------- Co-authored-by: sim <sim@local>
This commit is contained in:
5
.changeset/calm-deer-hum.md
Normal file
5
.changeset/calm-deer-hum.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 3728
|
||||
---
|
||||
**`/gsd:plan-phase` no longer writes gitignored install-mirror paths into plans** — `files_modified` and artifact paths are now verified against `git ls-files` and resolved to tracked source (e.g. a plugin's own tree) instead of a runtime mirror under `.gsd/capabilities/`, whose edits died on every capability sync; paths inherited from PATTERNS.md are re-verified so one mirror path can no longer self-propagate across phases. (#3645)
|
||||
@@ -116,6 +116,17 @@ Grep("router\.(get|post|put|delete)", type: "ts")
|
||||
3. Different role, same data flow — partial match
|
||||
4. Most recently modified — prefer current patterns over legacy
|
||||
|
||||
**Tracked-source gate (#3645):** every analog path you name must be git-TRACKED
|
||||
source, never a gitignored install/runtime mirror (e.g. `<root>/.gsd/capabilities/
|
||||
<id>/...` synced from a plugin's tracked tree). Before naming an analog whose
|
||||
file exists on disk, verify `git ls-files -- <path>` prints it (non-empty =
|
||||
tracked); if the closest analog is a gitignored mirror, substitute its tracked
|
||||
origin (e.g. `plugins/*/.gsd/capabilities/<id>/...`, or root
|
||||
`capabilities/<id>/...`). PATTERNS.md must never emit mirror paths — the
|
||||
planner builds later phases on your output, so one mirror path self-propagates
|
||||
across phases and the executor's edits die on the next capability sync. For
|
||||
files inside a nested submodule, run the check from within the submodule.
|
||||
|
||||
## Step 4: Extract Patterns from Analogs
|
||||
|
||||
**Never re-read the same range.** For small files (≤ 2,000 lines), one `Read` call is enough — extract everything in that pass. For large files, multiple non-overlapping targeted reads are fine; what is forbidden is re-reading a range already in context.
|
||||
|
||||
@@ -759,6 +759,20 @@ Historical findings already incorporated, explicitly deferred/rejected in PLAN.m
|
||||
|
||||
**Phase requirement IDs (every ID MUST appear in a plan's `requirements` field):** {phase_req_ids}
|
||||
|
||||
<tracked_source_paths>
|
||||
**Tracked-source paths (#3645):** Every path you write into PLAN.md —
|
||||
`files_modified`, `must_haves.artifacts`, action paths, and paths inherited
|
||||
from `{PATTERNS_PATH}` or prior-phase plans — must name git-tracked source,
|
||||
never a gitignored install/runtime mirror (e.g. `<root>/.gsd/capabilities/<id>/...`
|
||||
synced from a plugin's tracked tree; executor edits to a mirror die on the
|
||||
next capability sync). Verify existing-file paths with `git ls-files -- <path>`
|
||||
(non-empty = tracked); resolve a gitignored hit to its tracked origin
|
||||
(`plugins/*/.gsd/capabilities/<id>/...`, root `capabilities/<id>/...`). A
|
||||
not-yet-existing path is a new file — keep the intended path. Re-verify
|
||||
inherited paths: fix a mirror path, never inherit. Submodule files: check
|
||||
from within the submodule.
|
||||
</tracked_source_paths>
|
||||
|
||||
**Project instructions:** Read ./CLAUDE.md or ./.claude/CLAUDE.md if either exists — follow project-specific guidelines
|
||||
**Project skills:** Check .claude/skills/ or .agents/skills/ directory (if either exists) — read SKILL.md files, plans should account for project skill rules
|
||||
|
||||
|
||||
98
tests/agent-tracked-source-rule.test.cjs
Normal file
98
tests/agent-tracked-source-rule.test.cjs
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* #3645: gsd-planner and gsd-pattern-mapper must write only git-TRACKED
|
||||
* source paths into PLAN.md / PATTERNS.md — never a gitignored install/
|
||||
* runtime mirror (e.g. <root>/.gsd/capabilities/<id>/... synced from a
|
||||
* plugin's tracked tree). Executors that trust a mirror path edit a copy
|
||||
* whose changes die on the next sync; the wrong path also self-propagates
|
||||
* across phases because pattern-mapper builds on prior phases' docs.
|
||||
*
|
||||
* Enforcement points: gsd-pattern-mapper.md carries the gate inline (its
|
||||
* size tier has headroom); the PLANNER agent file is frozen under a
|
||||
* 49152-LF-char cap asserted by four other suites, so the planner-side rule
|
||||
* is projected onto its spawn contract in gsd-core/workflows/plan-phase.md
|
||||
* (the #3297 precedent for requirements the spawned agent must honor).
|
||||
*
|
||||
* Shipped-content contract rows: the agent/workflow text IS the product the
|
||||
* runtime loads, so asserting its contract lines tests the deployed behavior.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const AGENTS_DIR = path.join(__dirname, '..', 'agents');
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||
|
||||
describe('#3645 — agents write only git-tracked source paths', () => {
|
||||
// allow-test-rule: source-text-is-the-product (#3645)
|
||||
// The workflow prompt block below IS the runtime instruction shipped to
|
||||
// every plan-phase run; testing its content tests the deployed contract.
|
||||
const planPhase = fs.readFileSync(path.join(WORKFLOWS_DIR, 'plan-phase.md'), 'utf8');
|
||||
|
||||
// allow-test-rule: source-text-is-the-product (#3645)
|
||||
// The agent gate text IS the runtime instruction; testing it tests the
|
||||
// deployed contract — if the tracked-source gate is absent, the agent
|
||||
// does not enforce it.
|
||||
const mapper = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-pattern-mapper.md'), 'utf8');
|
||||
|
||||
test('planner spawn contract carries the #3645 tracked-source rule', () => {
|
||||
const block = planPhase.split('<tracked_source_paths>')[1]?.split('</tracked_source_paths>')[0];
|
||||
assert.ok(block, 'plan-phase.md must carry the <tracked_source_paths> block in the planner spawn prompt (#3645)');
|
||||
assert.ok(/files_modified/.test(block) && /must_haves/.test(block),
|
||||
'the block must govern files_modified and must_haves paths');
|
||||
assert.ok(block.includes('git ls-files'),
|
||||
'the block must instruct git ls-files verification (#3645)');
|
||||
assert.ok(/GSD_SOURCE_MIRROR_SENTINEL|\.gsd\/capabilities/.test(block),
|
||||
'the block must name the gitignored install-mirror shape it rejects');
|
||||
assert.ok(/plugins\//.test(block),
|
||||
'the block must point at tracked plugin-source fallback locations');
|
||||
});
|
||||
|
||||
test('planner spawn contract re-verifies inherited PATTERNS.md paths (#3645)', () => {
|
||||
const block = planPhase.split('<tracked_source_paths>')[1]?.split('</tracked_source_paths>')[0];
|
||||
assert.ok(/PATTERNS_PATH/.test(block) && /inherit/.test(block),
|
||||
'the block must cover paths inherited from {PATTERNS_PATH} and prior phases');
|
||||
});
|
||||
|
||||
test('gsd-pattern-mapper emits only tracked analog paths (#3645)', () => {
|
||||
assert.ok(mapper.includes('git ls-files'),
|
||||
'gsd-pattern-mapper.md must verify analog paths via git ls-files (#3645)');
|
||||
assert.ok(mapper.includes('gitignored install/runtime mirror'),
|
||||
'the mapper must name the gitignored-mirror rejection explicitly (#3645)');
|
||||
assert.ok(mapper.includes('never emit mirror paths'),
|
||||
'PATTERNS.md output must be required to never carry mirror paths (#3645)');
|
||||
assert.ok(/plugins\//.test(mapper) && /capabilities\//.test(mapper),
|
||||
'the mapper must name tracked-origin fallback locations (#3645)');
|
||||
});
|
||||
|
||||
test('the frozen planner agent file is untouched by #3645', () => {
|
||||
// The planner is pinned under a 49152-LF-char cap by four suites; the
|
||||
// rule lives in its spawn contract instead. Guard the freeze: #3645
|
||||
// must not have grown the agent file past its baseline.
|
||||
const src = fs.readFileSync(path.join(AGENTS_DIR, 'gsd-planner.md'), 'utf8');
|
||||
const lf = src.replace(/\r\n/g, '\n').length;
|
||||
assert.ok(lf < 49152, `gsd-planner.md is ${lf} LF chars — must stay < 49152 (#3645 keeps the planner frozen; enforcement lives in plan-phase.md)`);
|
||||
assert.ok(!src.includes('Tracked-source'),
|
||||
'the rule belongs in the spawn contract, not the frozen agent file (#3645)');
|
||||
});
|
||||
|
||||
test('growth ack recorded for the grown shipped files (#3645)', () => {
|
||||
const acksDir = path.join(__dirname, 'emitted-drift-acks');
|
||||
const readAck = (name) => fs.readFileSync(path.join(acksDir, name), 'utf8');
|
||||
const mine = fs.readdirSync(acksDir).find((f) => f.includes('3645'));
|
||||
assert.ok(mine, 'an emitted-drift-acks fragment for #3645 must exist');
|
||||
const mineSrc = readAck(mine);
|
||||
assert.ok(mineSrc.includes('"gsd-pattern-mapper.md"'),
|
||||
'the #3645 fragment must acknowledge the grown mapper by its ack key');
|
||||
assert.ok(!mineSrc.includes('"gsd-planner.md"'),
|
||||
'gsd-planner.md is unchanged — no ack entry for it');
|
||||
// plan-phase.md's growth ack lives in the 3409 fragment (two ack sources
|
||||
// may never name the same path — the merge precedent).
|
||||
const planPhaseAck = readAck('3409-unreachable-guard-arms.json');
|
||||
assert.ok(planPhaseAck.includes('"plan-phase.md"') && planPhaseAck.includes('#3645 append'),
|
||||
'the plan-phase.md growth reason must be appended to the 3409 fragment (#3645)');
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"paths": {
|
||||
"gsd-phase-researcher.md": "#3409: guarded `cat \"$phase_dir\"/*-CONTEXT.md` against nullglob wiping the pattern to zero operands when no CONTEXT.md exists \u2014 a bare `cat` with no operands blocks reading stdin (hangs the agent) instead of the `2>/dev/null` guard ever firing, since a stalled read is not a failing exit. Now checks `${_CTX[0]}` is a real path before invoking cat. Growth is the array-guard idiom itself (+43 bytes). \u2014 #2951 append (merged into this fragment because two ack sources may never name the same path; the #3409 entry above is at the base and therefore spent, and a second fragment naming this path is a hard failure in scripts/lint-emitted-drift-ack.cjs): +2352 bytes, 44250 -> 46602 (LARGE tier, cap 49152, 2550 bytes headroom). Adds the absent-evidence provenance rule to the claim-provenance section \u2014 the third member of the family alongside the package-name and in-repo-value rules. A compatibility claim resting on MISSING metadata (no python_requires, no engines field, no per-version classifier, no changelog entry, no matching support-matrix row) no longer earns [VERIFIED] however authoritative the source consulted; the only route from an absence to [VERIFIED] is a positive falsification attempt with its failing output pasted, and everything short of that is [ASSUMED], which the file already routes to \"needs user confirmation before becoming a locked decision\". Growth is inline prose in the agent body, deliberately NOT relocated into an eagerly @-imported reference, which ADR-1610 Decision 4 names as gaming the size proxy.",
|
||||
"gsd-verifier.md": "#3409: same nullglob-hang fix as gsd-phase-researcher.md, applied to `cat \"$PHASE_DIR\"/*-VERIFICATION.md` in Step 0 \u2014 an absent VERIFICATION.md previously left a zero-operand `cat` blocking on stdin instead of falling through to first-verification mode. Growth is the array-guard idiom (+49 bytes). \u2014 #3206 append (merged into this fragment because two ack sources may never name the same path): +52 bytes, 49098 -> 49150 (2 under the LARGE cap). The growth is the literal fix for the term 5b used undefined: the compressed explicit-evidence definition inlined at 5b (+34 net on the rewritten line \u2014 the trailing honest-verifier cite there is dropped as superseded by the inline definition; honest-verifier.md stays cited at 5c) plus gsd-core/ path-prefix repairs on the two 404ing bare references/ cites at 5c (honest-verifier.md) and the MVP-mode section (verify-mvp-mode.md) (+9 each). Lazy extraction remains untakeable in this change: the large extractable blocks are content-pinned by tests that read the agent file directly (tests/verifier-behavior-unverified.test.cjs, tests/verification-overrides.test.cjs), so extraction is its own coordinated change.",
|
||||
"complete-milestone.md": "#3409: guarded `cat .planning/phases/*-*/*-SUMMARY.md` \u2014 with `shopt -s nullglob` active in this block's preamble (#2962), zero matching phase summaries collapses the glob to nothing and a bare `cat` blocks reading stdin rather than producing empty output, wedging the milestone-completion review. Growth is the array-existence-check idiom (+73 bytes, two glob segments makes this longer than the single-glob sites). \u2014 #2142 append (merged into this fragment because two ack sources may never name the same path): +1605 bytes, 40498 -> 42103. The `archive_milestone` step now documents the opt-in `--archive-quick` quick-task archival flag (default OFF, deliberately NOT symmetrical with phase archival's default-ON posture), folds the AskUserQuestion decision for it into the SAME `milestone.complete` invocation (avoiding a redundant second call), and states the known bucket-all provenance limit.",
|
||||
"discuss-phase-assumptions.md": "#3409: replaced the unreachable `AUTO_MODE=$(gsd_run query check auto-mode --pick active 2>/dev/null || echo \"false\")` \u2014 `||` never fires because the query exits 0 with empty stdout when the field is absent, not a failure, so AUTO_MODE silently ended up empty rather than \"false\" \u2014 with a two-line capture-then-default (`AUTO_MODE=\"${AUTO_MODE:-false}\"`) that actually reaches the fallback. Growth is the extra default-assignment line (+19 bytes).",
|
||||
"plan-phase.md": "#3409: three sites. `AUTO_CHAIN` and `PHASE_REQ_IDS` get the same unreachable-`||`-fallback fix as discuss-phase-assumptions.md (empty-but-successful `gsd_run query` output never triggered `|| echo`, now uses `${VAR:-default}`); `PRIOR_SUMMARIES` additionally swapped `gsd_run query phases.list --pick summaries_total` for `--type summaries --pick count` since the old pick key produced the same unreachable-fallback failure mode for the walking-skeleton check. Net growth across the three sites is +39 bytes. \u2014 #3576 append: bare `references/<name>.md` cites repaired to the canonical `gsd-core/references/<name>.md` form (+36 bytes, 4 cite(s) \u00d7 9). Dead-pointer fix; no content change. #3559: the generic gate-dispatch arm gained the in-context validation contract for manifest-supplied check values. gates[].check is not one of the four executable surfaces the install consent prompt discloses, so a capability consented to as declarative-only could still reach a shell through an unvalidated check.query interpolated into a command substitution. The reference (references/loop-hook-dispatch.md) stated this requirement for step -> ref.command and omitted it for gate; that omission is the root cause and is now closed at the reference plus all four dispatch sites. Growth is one validation paragraph per site. 90516 -> 90627 bytes (+111). \u2014 #2401 append (merged into this fragment because two ack sources may never name the same path): plan-phase.md now dispatches the deterministic `gsd_run check verify-command-paths` probe before spawning the plan-check pass and interpolates its result into the verification prompt as {VERIFY_PATHS} inside a new <verify_command_path_probe> block, plus a <proven_verify_commands> block carrying prior_verify_commands into planning context so the planner can reuse a proven path instead of re-deriving one. 90627 -> 92807 bytes (+2180). Deliberate runtime-loaded workflow text for the new feature, not converter drift. \u2014 #3606 append: plan:pre gains generic contribution dispatch (deferral covering every `into` target, not just planner); plan:post gains generic step + contribution dispatch and its skip condition no longer keys on the gap-analysis gate\u2019s absence (that skip silently dropped every other registered hook at the point).",
|
||||
"session-report.md": "#3409: guarded `ls -la .planning/reports/SESSION_REPORT*.md 2>/dev/null || echo \"No previous reports\"` \u2014 with nullglob active, zero prior reports collapses the pattern to nothing and `ls -la` with no operands lists the current directory (a successful exit, wrong output) instead of failing into the `|| echo` fallback, so the report-existence check silently printed a directory listing. Replaced with an array-existence check that only lists when a real report file is present. Growth is the guard idiom (+58 bytes).",
|
||||
"transition.md": "#3409: guarded `cat .planning/phases/XX-current/*-SUMMARY.md` \u2014 same nullglob-hang defect as complete-milestone.md's phase-summary read: zero summaries left a bare `cat` blocking on stdin instead of proceeding with no summary content during PROJECT.md evolution. Growth is the array-existence-check idiom (+73 bytes)."
|
||||
"gsd-phase-researcher.md": "#3409: guarded `cat \"$phase_dir\"/*-CONTEXT.md` against nullglob wiping the pattern to zero operands when no CONTEXT.md exists — a bare `cat` with no operands blocks reading stdin (hangs the agent) instead of the `2>/dev/null` guard ever firing, since a stalled read is not a failing exit. Now checks `${_CTX[0]}` is a real path before invoking cat. Growth is the array-guard idiom itself (+43 bytes). — #2951 append (merged into this fragment because two ack sources may never name the same path; the #3409 entry above is at the base and therefore spent, and a second fragment naming this path is a hard failure in scripts/lint-emitted-drift-ack.cjs): +2352 bytes, 44250 -> 46602 (LARGE tier, cap 49152, 2550 bytes headroom). Adds the absent-evidence provenance rule to the claim-provenance section — the third member of the family alongside the package-name and in-repo-value rules. A compatibility claim resting on MISSING metadata (no python_requires, no engines field, no per-version classifier, no changelog entry, no matching support-matrix row) no longer earns [VERIFIED] however authoritative the source consulted; the only route from an absence to [VERIFIED] is a positive falsification attempt with its failing output pasted, and everything short of that is [ASSUMED], which the file already routes to \"needs user confirmation before becoming a locked decision\". Growth is inline prose in the agent body, deliberately NOT relocated into an eagerly @-imported reference, which ADR-1610 Decision 4 names as gaming the size proxy.",
|
||||
"gsd-verifier.md": "#3409: same nullglob-hang fix as gsd-phase-researcher.md, applied to `cat \"$PHASE_DIR\"/*-VERIFICATION.md` in Step 0 — an absent VERIFICATION.md previously left a zero-operand `cat` blocking on stdin instead of falling through to first-verification mode. Growth is the array-guard idiom (+49 bytes). — #3206 append (merged into this fragment because two ack sources may never name the same path): +52 bytes, 49098 -> 49150 (2 under the LARGE cap). The growth is the literal fix for the term 5b used undefined: the compressed explicit-evidence definition inlined at 5b (+34 net on the rewritten line — the trailing honest-verifier cite there is dropped as superseded by the inline definition; honest-verifier.md stays cited at 5c) plus gsd-core/ path-prefix repairs on the two 404ing bare references/ cites at 5c (honest-verifier.md) and the MVP-mode section (verify-mvp-mode.md) (+9 each). Lazy extraction remains untakeable in this change: the large extractable blocks are content-pinned by tests that read the agent file directly (tests/verifier-behavior-unverified.test.cjs, tests/verification-overrides.test.cjs), so extraction is its own coordinated change.",
|
||||
"complete-milestone.md": "#3409: guarded `cat .planning/phases/*-*/*-SUMMARY.md` — with `shopt -s nullglob` active in this block's preamble (#2962), zero matching phase summaries collapses the glob to nothing and a bare `cat` blocks reading stdin rather than producing empty output, wedging the milestone-completion review. Growth is the array-existence-check idiom (+73 bytes, two glob segments makes this longer than the single-glob sites). — #2142 append (merged into this fragment because two ack sources may never name the same path): +1605 bytes, 40498 -> 42103. The `archive_milestone` step now documents the opt-in `--archive-quick` quick-task archival flag (default OFF, deliberately NOT symmetrical with phase archival's default-ON posture), folds the AskUserQuestion decision for it into the SAME `milestone.complete` invocation (avoiding a redundant second call), and states the known bucket-all provenance limit.",
|
||||
"discuss-phase-assumptions.md": "#3409: replaced the unreachable `AUTO_MODE=$(gsd_run query check auto-mode --pick active 2>/dev/null || echo \"false\")` — `||` never fires because the query exits 0 with empty stdout when the field is absent, not a failure, so AUTO_MODE silently ended up empty rather than \"false\" — with a two-line capture-then-default (`AUTO_MODE=\"${AUTO_MODE:-false}\"`) that actually reaches the fallback. Growth is the extra default-assignment line (+19 bytes).",
|
||||
"plan-phase.md": "#3409: three sites. `AUTO_CHAIN` and `PHASE_REQ_IDS` get the same unreachable-`||`-fallback fix as discuss-phase-assumptions.md (empty-but-successful `gsd_run query` output never triggered `|| echo`, now uses `${VAR:-default}`); `PRIOR_SUMMARIES` additionally swapped `gsd_run query phases.list --pick summaries_total` for `--type summaries --pick count` since the old pick key produced the same unreachable-fallback failure mode for the walking-skeleton check. Net growth across the three sites is +39 bytes. — #3576 append: bare `references/<name>.md` cites repaired to the canonical `gsd-core/references/<name>.md` form (+36 bytes, 4 cite(s) × 9). Dead-pointer fix; no content change. #3559: the generic gate-dispatch arm gained the in-context validation contract for manifest-supplied check values. gates[].check is not one of the four executable surfaces the install consent prompt discloses, so a capability consented to as declarative-only could still reach a shell through an unvalidated check.query interpolated into a command substitution. The reference (references/loop-hook-dispatch.md) stated this requirement for step -> ref.command and omitted it for gate; that omission is the root cause and is now closed at the reference plus all four dispatch sites. Growth is one validation paragraph per site. 90516 -> 90627 bytes (+111). — #2401 append (merged into this fragment because two ack sources may never name the same path): plan-phase.md now dispatches the deterministic `gsd_run check verify-command-paths` probe before spawning the plan-check pass and interpolates its result into the verification prompt as {VERIFY_PATHS} inside a new <verify_command_path_probe> block, plus a <proven_verify_commands> block carrying prior_verify_commands into planning context so the planner can reuse a proven path instead of re-deriving one. 90627 -> 92807 bytes (+2180). Deliberate runtime-loaded workflow text for the new feature, not converter drift. — #3606 append: plan:pre gains generic contribution dispatch (deferral covering every `into` target, not just planner); plan:post gains generic step + contribution dispatch and its skip condition no longer keys on the gap-analysis gate’s absence (that skip silently dropped every other registered hook at the point). #3645 append (merged here because two ack sources may never name the same path): plan-phase.md also grew ~+1040 for the <tracked_source_paths> block in the gsd-planner spawn prompt — files_modified/must_haves/artifact/action/PATTERNS-inherited paths must be git-tracked source (git ls-files verification, tracked-origin fallback, re-verify inherited paths).",
|
||||
"session-report.md": "#3409: guarded `ls -la .planning/reports/SESSION_REPORT*.md 2>/dev/null || echo \"No previous reports\"` — with nullglob active, zero prior reports collapses the pattern to nothing and `ls -la` with no operands lists the current directory (a successful exit, wrong output) instead of failing into the `|| echo` fallback, so the report-existence check silently printed a directory listing. Replaced with an array-existence check that only lists when a real report file is present. Growth is the guard idiom (+58 bytes).",
|
||||
"transition.md": "#3409: guarded `cat .planning/phases/XX-current/*-SUMMARY.md` — same nullglob-hang defect as complete-milestone.md's phase-summary read: zero summaries left a bare `cat` blocking on stdin instead of proceeding with no summary content during PROJECT.md evolution. Growth is the array-existence-check idiom (+73 bytes)."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$comment": "Growth ack (#2914 fragment). Reason: #3645 adds the tracked-source rule (git ls-files verification, gitignored-mirror rejection, tracked-origin fallback, submodule note). gsd-pattern-mapper.md 12487 -> 13326 (+839, DEFAULT cap 24576). The planner-side rule lives in plan-phase.md (ack appended to the 3409 fragment — two ack sources may never name the same path). gsd-planner.md is UNCHANGED (frozen under 49152 LF chars by four suites).",
|
||||
"version": 1,
|
||||
"paths": {
|
||||
"gsd-pattern-mapper.md": "agents/gsd-pattern-mapper.md +839B: Tracked-source gate on analog selection; PATTERNS.md never emits mirror paths (#3645)"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user