#2551/#3182/#2361 are pre-migration get-shit-done-redux issue numbers with no equivalent in open-gsd/gsd-core; they mislead triage and manufacture phantom blockers. Repoint to real successors (#717 byte-budget rework, #720) or rewrite as prose referencing the discuss-phase/modes progressive-disclosure split. Correct co-located 'line budget'/'<500 lines' framing to the byte-based reality (#717). Add a CI guard (tests/no-phantom-issue-refs.test.cjs) that fails if a phantom ref is reintroduced. SSH-key patterns (id_ed25519) left untouched. No user-facing runtime behavior change. Closes #1073
This commit is contained in:
@@ -386,7 +386,7 @@ describe('VERIFY: data-flow trace, environment audit, and behavioral spot-checks
|
||||
|
||||
describe('DISCUSS: discussion log generation', () => {
|
||||
test('discuss-phase workflow references DISCUSSION-LOG.md generation', () => {
|
||||
// After #2551 progressive-disclosure refactor, the DISCUSSION-LOG.md template
|
||||
// After the discuss-phase progressive-disclosure split (#717), the DISCUSSION-LOG.md template
|
||||
// body lives in workflows/discuss-phase/templates/discussion-log.md and is
|
||||
// read at the git_commit step. Both files together must satisfy the
|
||||
// documentation contract.
|
||||
@@ -402,7 +402,7 @@ describe('DISCUSS: discussion log generation', () => {
|
||||
);
|
||||
assert.ok(
|
||||
content.includes('Audit trail only'),
|
||||
'discuss-phase (or its discussion-log template after #2551) must mark discussion log as audit-only'
|
||||
'discuss-phase (or its discussion-log template after the discuss-phase/modes split) must mark discussion log as audit-only'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -21,14 +21,14 @@ const path = require('node:path');
|
||||
const DISCUSS_PHASE = path.join(
|
||||
__dirname, '..', 'gsd-core', 'workflows', 'discuss-phase.md',
|
||||
);
|
||||
// After #2551 progressive-disclosure refactor, the scout_codebase phase-type
|
||||
// After the discuss-phase progressive-disclosure split (#717), the scout_codebase phase-type
|
||||
// table and split-reads warning live in references/scout-codebase.md.
|
||||
const SCOUT_REF = path.join(
|
||||
__dirname, '..', 'gsd-core', 'references', 'scout-codebase.md',
|
||||
);
|
||||
|
||||
function readDiscussContext() {
|
||||
// Both files are required after #2551 — fail loudly if either is missing
|
||||
// Both files are required after the discuss-phase/modes split — fail loudly if either is missing
|
||||
// rather than silently weakening the regression coverage.
|
||||
for (const p of [DISCUSS_PHASE, SCOUT_REF]) {
|
||||
assert.ok(fs.existsSync(p), `Required discuss-phase context source missing: ${p}`);
|
||||
@@ -42,7 +42,7 @@ describe('discuss-phase context fixes (#2549, #2550, #2552)', () => {
|
||||
assert.ok(fs.existsSync(DISCUSS_PHASE), 'discuss-phase.md must exist');
|
||||
assert.ok(
|
||||
fs.existsSync(SCOUT_REF),
|
||||
'references/scout-codebase.md must exist after #2551 extraction',
|
||||
'references/scout-codebase.md must exist after the discuss-phase/modes progressive-disclosure split',
|
||||
);
|
||||
src = readDiscussContext();
|
||||
});
|
||||
|
||||
@@ -21,13 +21,13 @@ const path = require('path');
|
||||
describe('plan-phase chain flag preservation (#1620)', () => {
|
||||
const planPath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'plan-phase.md');
|
||||
const discussPath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'discuss-phase.md');
|
||||
// After #2551, discuss-phase chain logic moved to modes/chain.md.
|
||||
// After the discuss-phase/modes split (#717), discuss-phase chain logic moved to modes/chain.md.
|
||||
const discussChainPath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'discuss-phase', 'modes', 'chain.md');
|
||||
const readDiscuss = () => {
|
||||
// Fail loudly if either source is missing — silent filtering would let a
|
||||
// regression that deletes modes/chain.md pass this whole suite.
|
||||
assert.ok(fs.existsSync(discussPath), `discuss-phase.md missing: ${discussPath}`);
|
||||
assert.ok(fs.existsSync(discussChainPath), `discuss-phase/modes/chain.md missing after #2551 split: ${discussChainPath}`);
|
||||
assert.ok(fs.existsSync(discussChainPath), `discuss-phase/modes/chain.md missing after discuss-phase/modes split: ${discussChainPath}`);
|
||||
return [discussPath, discussChainPath].map(p => fs.readFileSync(p, 'utf8')).join('\n');
|
||||
};
|
||||
|
||||
@@ -61,7 +61,7 @@ describe('plan-phase chain flag preservation (#1620)', () => {
|
||||
);
|
||||
assert.ok(
|
||||
discussContent.includes(guardPattern),
|
||||
'discuss-phase (or discuss-phase/modes/chain.md after #2551 split) should use the dual-flag guard pattern'
|
||||
'discuss-phase (or discuss-phase/modes/chain.md after the discuss-phase/modes split) should use the dual-flag guard pattern'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ const path = require('path');
|
||||
|
||||
describe('discuss-phase incremental checkpoint saves (#1485)', () => {
|
||||
const workflowPath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'discuss-phase.md');
|
||||
// After #2551 progressive-disclosure refactor, checkpoint logic lives in the
|
||||
// After the discuss-phase progressive-disclosure split (#717), checkpoint logic lives in the
|
||||
// default mode file and the JSON schema lives in the templates directory.
|
||||
const defaultModePath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'discuss-phase', 'modes', 'default.md');
|
||||
const checkpointTplPath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'discuss-phase', 'templates', 'checkpoint.json');
|
||||
|
||||
@@ -42,7 +42,7 @@ describe('discuss-phase power user mode (#1513)', () => {
|
||||
|
||||
describe('main workflow file (discuss-phase.md)', () => {
|
||||
test('has power_user_mode section or references discuss-phase-power.md', () => {
|
||||
// After #2551, the power dispatch lives in discuss-phase/modes/power.md and
|
||||
// After the discuss-phase/modes split (#717), the power dispatch lives in discuss-phase/modes/power.md and
|
||||
// the parent references it via the dispatch table.
|
||||
const parentContent = fs.readFileSync(workflowPath, 'utf8');
|
||||
const powerModePath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'discuss-phase', 'modes', 'power.md');
|
||||
@@ -52,7 +52,7 @@ describe('discuss-phase power user mode (#1513)', () => {
|
||||
const hasReference = content.includes('discuss-phase-power');
|
||||
assert.ok(
|
||||
hasPowerSection || hasReference,
|
||||
'discuss-phase.md (or modes/power.md after #2551) should have power_user_mode section or reference discuss-phase-power.md'
|
||||
'discuss-phase.md (or modes/power.md after the discuss-phase/modes split) should have power_user_mode section or reference discuss-phase-power.md'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
58
tests/no-phantom-issue-refs.test.cjs
Normal file
58
tests/no-phantom-issue-refs.test.cjs
Normal file
@@ -0,0 +1,58 @@
|
||||
// allow-test-rule: runtime-contract-is-the-product (see #1073) — this guard asserts the
|
||||
// ABSENCE of phantom pre-migration issue references in repo text (docs, tests,
|
||||
// workflows). The file *content* is the product surface here (#1073): dangling
|
||||
// refs like #2551/#3182 that don't exist in open-gsd/gsd-core (highest real
|
||||
// issue is in the low thousands of the redux repo, not here) mislead triage and
|
||||
// manufacture phantom blockers. This test fails CI if such a ref is reintroduced.
|
||||
|
||||
'use strict';
|
||||
|
||||
const { test } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
|
||||
// Phantom pre-migration (get-shit-done-redux) issue numbers with NO equivalent
|
||||
// in open-gsd/gsd-core. Matched only with a leading '#' or in an issues/ URL so
|
||||
// SSH key patterns like `id_ed25519` (which contain the digits "2551") are NOT
|
||||
// false-positives.
|
||||
const PHANTOM = ['2551', '3182', '2361'];
|
||||
const REF_RE = new RegExp(
|
||||
'(?:#(?:' + PHANTOM.join('|') + ')\\b)|(?:issues/(?:' + PHANTOM.join('|') + ')\\b)',
|
||||
);
|
||||
|
||||
const SCAN_EXT = new Set(['.md', '.cjs', '.js', '.cts', '.ts']);
|
||||
const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'coverage', '.changeset']);
|
||||
// This guard file itself names the phantom numbers (by necessity); exclude it.
|
||||
const SELF = path.relative(ROOT, __filename);
|
||||
|
||||
function walk(dir, acc) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
if (entry.isDirectory()) {
|
||||
if (!SKIP_DIRS.has(entry.name)) walk(path.join(dir, entry.name), acc);
|
||||
} else if (SCAN_EXT.has(path.extname(entry.name))) {
|
||||
acc.push(path.join(dir, entry.name));
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
test('no phantom pre-migration issue references remain in repo text (#1073)', () => {
|
||||
const offenders = [];
|
||||
for (const file of walk(ROOT, [])) {
|
||||
const rel = path.relative(ROOT, file);
|
||||
if (rel === SELF) continue;
|
||||
const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/);
|
||||
lines.forEach((line, i) => {
|
||||
if (REF_RE.test(line)) offenders.push(`${rel}:${i + 1}: ${line.trim().slice(0, 120)}`);
|
||||
});
|
||||
}
|
||||
assert.strictEqual(
|
||||
offenders.length,
|
||||
0,
|
||||
`Phantom issue refs (${PHANTOM.map((n) => '#' + n).join('/')}) found — repoint to a real ` +
|
||||
`successor (#717/#720) or rewrite as prose (see #1073):\n` + offenders.join('\n'),
|
||||
);
|
||||
});
|
||||
@@ -93,7 +93,7 @@ describe('Thinking Partner Integration (#1726)', () => {
|
||||
});
|
||||
|
||||
// Workflow integration tests
|
||||
// After #2551 progressive-disclosure refactor, the thinking-partner block
|
||||
// After the discuss-phase progressive-disclosure split (#717), the thinking-partner block
|
||||
// moved into the per-mode files (default.md, advisor.md) since the prompt
|
||||
// is mode-specific (only fires inside discuss_areas, after a user answer).
|
||||
describe('Discuss-phase integration', () => {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"discovery-phase.md": 8651,
|
||||
"discuss-phase-assumptions.md": 26984,
|
||||
"discuss-phase-power.md": 11273,
|
||||
"discuss-phase.md": 31965,
|
||||
"discuss-phase.md": 31990,
|
||||
"do.md": 10068,
|
||||
"docs-update.md": 55662,
|
||||
"edit-phase.md": 12883,
|
||||
|
||||
@@ -102,7 +102,7 @@ const DEFAULT_CAP = 40960; // 40 KiB
|
||||
const NEW_FILE_CAP = 32768; // 32 KiB
|
||||
|
||||
// Top-level orchestrators that own end-to-end multi-phase rubrics.
|
||||
// Grandfathered at current sizes — see PR #2551 for the progressive-disclosure
|
||||
// Grandfathered at current sizes — see the discuss-phase/modes split (#717) for the progressive-disclosure
|
||||
// pattern that future shrinks should follow. Byte counts noted for reference.
|
||||
const XL_WORKFLOWS = new Set([
|
||||
'execute-phase', // 92880 bytes (grew in #381 CLAUDE_ENV_FILE persist clause)
|
||||
@@ -210,8 +210,8 @@ describe('SIZE: per-file workflow baseline (issue #1074)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('SIZE: discuss-phase progressive disclosure (issue #2551)', () => {
|
||||
// Issue #2551 targets discuss-phase.md as a thin dispatcher, separate from
|
||||
describe('SIZE: discuss-phase progressive disclosure (#717 byte budget)', () => {
|
||||
// The discuss-phase progressive-disclosure split (#717) targets discuss-phase.md as a thin dispatcher, separate from
|
||||
// the per-tier grandfathered budgets above. Originally expressed as <500
|
||||
// lines; re-based to bytes for #717 (500 lines ≈ 28 KB at these files'
|
||||
// density; set to 30 KB to preserve the thin-dispatcher intent with modest
|
||||
@@ -221,12 +221,12 @@ describe('SIZE: discuss-phase progressive disclosure (issue #2551)', () => {
|
||||
// Target raised from 30000 to 32000 in #891 (launcher shim expansion added 17 runtime home arms,
|
||||
// adding ~960 bytes to the preamble; the thin-dispatcher intent is preserved — actual=30935).
|
||||
const DISCUSS_PHASE_TARGET = 32000;
|
||||
test(`discuss-phase.md is under ${DISCUSS_PHASE_TARGET} bytes (issue #2551 target)`, () => {
|
||||
test(`discuss-phase.md is under ${DISCUSS_PHASE_TARGET} bytes (#717 byte budget)`, () => {
|
||||
const filePath = path.join(WORKFLOWS_DIR, 'discuss-phase.md');
|
||||
const bytes = byteCount(filePath);
|
||||
assert.ok(
|
||||
bytes < DISCUSS_PHASE_TARGET,
|
||||
`discuss-phase.md is ${bytes} bytes — must be under ${DISCUSS_PHASE_TARGET} per #2551. ` +
|
||||
`discuss-phase.md is ${bytes} bytes — must be under ${DISCUSS_PHASE_TARGET} per #717. ` +
|
||||
`Per-mode logic belongs in workflows/discuss-phase/modes/<mode>.md, ` +
|
||||
`templates in workflows/discuss-phase/templates/.`
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user