test(#1977): consolidate 22 misc + repo-invariant regression tests

Final epic-#1969 batch. Fold 22 issue-named files: the 4 genuine repo-wide invariant
scans (551-eslint-bin-lib-coverage, bug-3054 stale /gsd-next, bug-3810 no-gsd-sdk-runtime-refs,
feat-3593 cli-negative-universal) into a NEW shared repo-invariants.test.cjs; the other 18 as
singletons into their nearest module suite (model-resolver, codex-config, runtime-converters,
security, state-transition, worktree-safety, roadmap-parser, etc.). Verbatim block-scoped
describe wrappers; 334 subtests conserved 1:1.

Host-env pre-check (B2+B6): the 6 CLI folds into GSD_TEST_MODE-setting hosts (model-resolver/
codex-config/runtime-converters) are benign — each origin independently sets GSD_TEST_MODE=1
itself (idempotent), unlike the B6 real-install case.

Regenerates regression-name allowlist (222->213), ratchets file-count allowlist (state 17->16),
makes 7 relocated allow-test-rule exemptions issue-ref-compliant (ADR-456; prunes stale ids).
Repoints 2 tests/ refs in docs/TESTING-SUITES.md. lint:ci green.

Part of epic #1969. Closes #1977.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-07-03 03:33:07 -04:00
parent 4c77b46e3d
commit 697cbb1f05
41 changed files with 5972 additions and 5767 deletions

View File

@@ -888,3 +888,643 @@ describe('bug #2950: stale deleted-command references removed from workflow file
});
});
}
// ────────────────────────────────────────────────────────────────────────
// Folded from tests/feat-2840-issue-driven-orchestration-guide.test.cjs — consolidation epic #1969 (B8 #1977)
// ────────────────────────────────────────────────────────────────────────
{
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:feat-2840-issue-driven-orchestration-guide (consolidation epic #1969 B8 #1977)", () => {
/**
* Tests for docs/issue-driven-orchestration.md (#2840).
*
* Structural-IR assertions per CONTRIBUTING.md "Prohibited: Raw Text Matching
* on Test Outputs": parse the guide into a typed record and assert on
* semantic flags, not regex on prose. The guide is rebuildable as long as
* the structural invariants survive — section-level rewording is fine.
*
* Acceptance criteria from issue #2840:
* - One guide explaining issue-driven orchestration using existing GSD
* commands.
* - Concrete end-to-end issue → workspace → plan/execute → verify/review
* → PR flow.
* - Explicitly documents safety boundaries: isolated worktrees, explicit
* human review, no automatic public posting by default.
* - Adds no runtime dependencies / no new command, daemon, or tracker
* integration. (Test-enforced via concept-mapping audit.)
*/
// allow-test-rule: structural-IR parser for a docs guide. The .includes() (see #2840)
// calls below build a typed record (commandsPresent flags, conceptPairs
// flags, nonGoalFlags, safetyFlags); assertions run on those booleans, not
// on raw text. This is the documented escape hatch in
// scripts/lint-no-source-grep.cjs for doc-shape tests.
'use strict';
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const GUIDE_PATH = path.join(__dirname, '..', 'docs', 'issue-driven-orchestration.md');
// ─── Helpers ────────────────────────────────────────────────────────────────
/**
* Extract a section starting at a given heading. Returns the body up to (but
* not including) the next heading at the same or shallower depth, or null if
* the heading isn't found.
*/
function extractSection(content, heading) {
const lines = content.split('\n');
const headingRe = new RegExp(`^(#+)\\s+${heading.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\$&')}\\s*$`);
let start = -1;
let depth = 0;
for (let i = 0; i < lines.length; i++) {
const m = lines[i].match(headingRe);
if (m) {
start = i + 1;
depth = m[1].length;
break;
}
}
if (start < 0) return null;
let end = lines.length;
for (let i = start; i < lines.length; i++) {
const m = lines[i].match(/^(#+)\s+/);
if (m && m[1].length <= depth) {
end = i;
break;
}
}
return lines.slice(start, end).join('\n');
}
/**
* Parse the guide into a typed record. Returns null when the guide is
* missing so the file-presence test can name the actual problem instead of
* cascading TypeErrors.
*/
function parseGuide() {
if (!fs.existsSync(GUIDE_PATH)) return null;
const content = fs.readFileSync(GUIDE_PATH, 'utf8');
// Strip inline emphasis but NOT underscores (snake_case identifiers like
// gsd-new-workspace, .planning/, etc. must survive).
const stripped = content.replace(/\*{1,3}|~{2}/g, '');
// Concept-mapping table: rows that pair a Symphony-style concept with a
// GSD primitive. Test asserts on presence of each required pair, not on
// exact prose ordering.
const conceptMappingSection = extractSection(content, 'Concept mapping');
const endToEndSection = extractSection(content, 'End-to-end flow') ||
extractSection(content, 'End-to-end issue → PR flow') ||
extractSection(content, 'End-to-end orchestration loop');
const safetySection = extractSection(content, 'Safety boundaries') ||
extractSection(content, 'Safety');
const nonGoalsSection = extractSection(content, 'Non-goals') ||
extractSection(content, 'What this guide does not do');
// Track which referenced commands appear at least once anywhere in the
// guide. This prevents drift if /gsd-* command names are renamed.
const requiredCommands = [
'/gsd-workspace --new',
'/gsd-manager',
'/gsd-autonomous',
'/gsd-discuss-phase',
'/gsd-plan-phase',
'/gsd-execute-phase',
'/gsd-verify-work',
'/gsd-review',
'/gsd-ship',
];
const commandsPresent = Object.fromEntries(
requiredCommands.map((c) => [c, content.includes(c)])
);
// Concept-mapping invariants — keys are concept slugs, values are the
// GSD primitive that must appear in the same paragraph/row of the
// concept-mapping section.
const conceptPairs = conceptMappingSection
? {
roadmap: /ROADMAP\.md/.test(conceptMappingSection),
statemd: /STATE\.md/.test(conceptMappingSection),
contextmd: /CONTEXT\.md/.test(conceptMappingSection),
planmd: /PLAN\.md/.test(conceptMappingSection),
workspaceCommand: /\/gsd-workspace\s+--new/.test(conceptMappingSection),
executionCommand:
/\/gsd-manager/.test(conceptMappingSection) ||
/\/gsd-autonomous/.test(conceptMappingSection),
verifyCommand: /\/gsd-verify-work/.test(conceptMappingSection),
reviewCommand: /\/gsd-review/.test(conceptMappingSection),
shipCommand: /\/gsd-ship/.test(conceptMappingSection),
}
: null;
// Non-goals required by the issue: must explicitly disclaim all four.
const nonGoalFlags = nonGoalsSection
? {
noVendoring: /vendor|copy/i.test(nonGoalsSection),
noDaemon: /daemon|polling/i.test(nonGoalsSection),
noTrackerDependency: /tracker.*depend|mandatory.*track/i.test(nonGoalsSection),
noBypassReview: /bypass|review|verification|human.*decision|human gate/i.test(nonGoalsSection),
}
: null;
// Safety boundaries — required disclaimers about how the loop stays safe.
const safetyFlags = safetySection
? {
isolatedWorktrees: /worktree|isolated/i.test(safetySection),
explicitReview: /review|human.*gate|human.*approval/i.test(safetySection),
noAutoPosting: /not.*automatic|no.*auto|explicit.*confirm|user.*confirm|human.*confirm/i.test(safetySection),
}
: null;
// End-to-end flow must enumerate at least the seven step sequence the
// acceptance criteria call out. We assert on numbered list items so the
// narrative can be reworded freely.
const numberedSteps = endToEndSection
? (endToEndSection.match(/^\s*\d+\.\s+/gm) || []).length
: 0;
// Strip markdown emphasis when checking for snake_case-sensitive content
// in section bodies (per the markdown-aware matching pattern).
const strippedConceptMapping = conceptMappingSection
? conceptMappingSection.replace(/\*{1,3}|~{2}/g, '')
: null;
return {
raw: content,
stripped,
conceptMappingSection,
strippedConceptMapping,
endToEndSection,
safetySection,
nonGoalsSection,
commandsPresent,
conceptPairs,
nonGoalFlags,
safetyFlags,
numberedSteps,
};
}
// ─── Tests ──────────────────────────────────────────────────────────────────
describe('issue-driven-orchestration guide (#2840)', () => {
test('docs/issue-driven-orchestration.md exists', () => {
assert.ok(
fs.existsSync(GUIDE_PATH),
`Guide must live at docs/issue-driven-orchestration.md per #2840`
);
});
test('every required GSD command is referenced at least once', () => {
const ir = parseGuide();
assert.ok(ir, 'parseGuide returned null — guide is missing');
for (const [cmd, present] of Object.entries(ir.commandsPresent)) {
assert.ok(present, `guide must reference ${cmd}`);
}
});
test('concept mapping section exists and pairs Symphony concepts with GSD primitives', () => {
const ir = parseGuide();
assert.ok(ir, 'guide must be present');
assert.ok(
ir.conceptMappingSection,
'guide must contain a "Concept mapping" section'
);
const expected = {
roadmap: 'ROADMAP.md must appear in the concept mapping',
statemd: 'STATE.md must appear in the concept mapping',
contextmd: 'CONTEXT.md must appear in the concept mapping',
planmd: 'PLAN.md must appear in the concept mapping',
workspaceCommand: '/gsd-workspace --new must appear in the concept mapping',
executionCommand:
'/gsd-manager or /gsd-autonomous must appear in the concept mapping',
verifyCommand: '/gsd-verify-work must appear in the concept mapping',
reviewCommand: '/gsd-review must appear in the concept mapping',
shipCommand: '/gsd-ship must appear in the concept mapping',
};
for (const [flag, msg] of Object.entries(expected)) {
assert.equal(ir.conceptPairs[flag], true, msg);
}
});
test('safety boundaries section names isolation, review, and non-auto-posting', () => {
const ir = parseGuide();
assert.ok(ir, 'guide must be present');
assert.ok(
ir.safetySection,
'guide must contain a "Safety boundaries" or "Safety" section'
);
assert.equal(
ir.safetyFlags.isolatedWorktrees,
true,
'safety section must mention isolated worktrees'
);
assert.equal(
ir.safetyFlags.explicitReview,
true,
'safety section must require explicit human review'
);
assert.equal(
ir.safetyFlags.noAutoPosting,
true,
'safety section must disclaim automatic public posting'
);
});
test('non-goals section disclaims vendoring, daemon, tracker dependency, and gate-bypass', () => {
const ir = parseGuide();
assert.ok(ir, 'guide must be present');
assert.ok(
ir.nonGoalsSection,
'guide must contain a "Non-goals" section'
);
const expected = {
noVendoring: 'must disclaim copying/vendoring Symphony',
noDaemon: 'must disclaim a long-running daemon',
noTrackerDependency: 'must disclaim mandatory tracker dependency',
noBypassReview: 'must disclaim bypassing review/verification gates',
};
for (const [flag, msg] of Object.entries(expected)) {
assert.equal(ir.nonGoalFlags[flag], true, msg);
}
});
test('end-to-end flow enumerates at least 7 numbered steps (per acceptance criteria)', () => {
const ir = parseGuide();
assert.ok(ir, 'guide must be present');
assert.ok(
ir.endToEndSection,
'guide must contain an "End-to-end flow" (or equivalent) section'
);
assert.ok(
ir.numberedSteps >= 7,
`end-to-end section must enumerate ≥7 numbered steps; found ${ir.numberedSteps}`
);
});
test('every fenced code block has a language tag (markdownlint MD040)', () => {
const ir = parseGuide();
assert.ok(ir, 'guide must be present');
// Pair fence opens; flag any opener with no language tag.
const fences = ir.raw.match(/^```.*$/gm) || [];
const openers = [];
for (let i = 0; i < fences.length; i++) {
// Even index = opener, odd = closer. An opener with empty trailing
// text is MD040.
if (i % 2 === 0) openers.push(fences[i]);
}
const bare = openers.filter((f) => /^```\s*$/.test(f));
assert.equal(
bare.length,
0,
`MD040: ${bare.length} fenced block(s) lack a language tag`
);
});
test('cross-linked from docs/README.md', () => {
const readme = path.join(__dirname, '..', 'docs', 'README.md');
if (!fs.existsSync(readme)) {
// docs/README.md is the discovery surface. Without a cross-link, the
// guide is invisible to users browsing docs/.
return; // tolerate absence; test below ensures FEATURES.md anchor.
}
const txt = fs.readFileSync(readme, 'utf8');
assert.ok(
/issue-driven-orchestration/.test(txt),
'docs/README.md must link to the new guide'
);
});
test('cross-linked from docs/USER-GUIDE.md', () => {
const guide = path.join(__dirname, '..', 'docs', 'USER-GUIDE.md');
// Mirror the null-guard pattern from the README test above: a missing
// file must produce a meaningful assertion message, not a cryptic
// ENOENT stack trace. (CR #3036.)
assert.ok(
fs.existsSync(guide),
'docs/USER-GUIDE.md must exist for cross-link validation'
);
const txt = fs.readFileSync(guide, 'utf8');
assert.ok(
/issue-driven-orchestration/.test(txt),
'docs/USER-GUIDE.md must link to the new guide'
);
});
});
});
}
// ────────────────────────────────────────────────────────────────────────
// Folded from tests/feat-3025-mcp-token-budget-docs.test.cjs — consolidation epic #1969 (B8 #1977)
// ────────────────────────────────────────────────────────────────────────
{
const { describe: __foldDescribe } = require('node:test');
__foldDescribe("folded:feat-3025-mcp-token-budget-docs (consolidation epic #1969 B8 #1977)", () => {
/**
* Documentation regression test for issue #3025 — MCP token-budget guidance.
*
* Verifies that gsd-core/references/context-budget.md contains the
* structural elements the issue requires:
*
* 1. A section explaining MCP/tool schemas as a context-budget concern
* 2. References to the harness-side toggles (enabledMcpjsonServers,
* disabledMcpjsonServers in .claude/settings.json)
* 3. A pre-phase audit checklist (browser/playwright, platform-specific,
* project-specific)
* 4. An explicit note that GSD does NOT manage MCP enablement — this is
* a Claude Code harness concern (with a cross-link)
* 5. Note the interaction with model_profile (compounding levers)
*
* Tests parse the doc into a typed section record (parseMcpSection) and
* assert on flag booleans, not raw text matches. Adheres to
* CONTRIBUTING.md "no-source-grep" — describes invariants, not wording,
* so the prose can be reworded freely as long as the semantics survive.
*
* Companion to docs/USER-GUIDE.md task section, which is exercised by the
* same parser shape (separate test below).
*/
'use strict';
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const ROOT = path.join(__dirname, '..');
const CONTEXT_BUDGET_MD = path.join(ROOT, 'gsd-core', 'references', 'context-budget.md');
const USER_GUIDE_MD = path.join(ROOT, 'docs', 'USER-GUIDE.md');
/**
* Extract the MCP-budget section from a markdown file by header text.
* Returns null if the section is missing. Section runs from the matching
* `## ` header up to the next `## ` header (or EOF).
*/
function extractSection(filePath, headerSubstring) {
const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split(/\r?\n/);
let inSection = false;
let startDepth = 0;
const collected = [];
for (const line of lines) {
const headerMatch = /^(#+)\s/.exec(line);
if (headerMatch) {
const depth = headerMatch[1].length;
if (inSection) {
// Section ends at a header at the same or shallower depth.
// Subsections at deeper depth are part of the section.
if (depth <= startDepth) break;
} else if (line.toLowerCase().includes(headerSubstring.toLowerCase())) {
inSection = true;
startDepth = depth;
}
}
if (inSection) collected.push(line);
}
return collected.length > 0 ? collected.join('\n') : null;
}
/**
* Parse the MCP-budget section into a typed semantic-flag record.
* Each flag answers a single behavioral question that #3025 requires
* the prose to encode.
*/
function parseMcpBudgetSection(section) {
if (!section || typeof section !== 'string') {
return {
ok: false,
sectionLength: 0,
explainsMcpAsBudgetConcern: false,
namesEnabledMcpjsonServers: false,
namesDisabledMcpjsonServers: false,
namesClaudeSettingsJson: false,
includesPrePhaseAudit: false,
auditMentionsBrowserOrPlaywright: false,
auditMentionsPlatformSpecific: false,
auditMentionsCrossProject: false,
explainsHarnessNotGsd: false,
mentionsModelProfileInteraction: false,
crossLinksContextBudget: false,
};
}
// CR follow-up: strip inline markdown emphasis (`**`, `*`, `~~`) and
// backticks before phrase-matching so e.g. "GSD does **not** manage"
// is caught by the primary `gsd does not manage` alternative below.
// WITHOUT this, the markdown-bold breaks the contiguous match and the
// test only passes via the fallback branch (silent dead code).
// Underscores are intentionally NOT stripped — `model_profile` and
// other snake_case identifiers must survive intact so the
// model_profile interaction check still finds them.
const stripped = section.replace(/\*{1,3}|~{2}|`/g, '');
// (1) Explains MCP as budget concern — must mention BOTH "MCP" / "tool
// schema" AND a token/cost framing.
const explainsMcpAsBudgetConcern =
/\bmcp\b|tool schema|tool schemas/i.test(stripped) &&
/\btoken|context budget|per[- ]turn|cost\b/i.test(stripped);
// (2) Names the harness keys verbatim
const namesEnabledMcpjsonServers = /enabledMcpjsonServers/.test(stripped);
const namesDisabledMcpjsonServers = /disabledMcpjsonServers/.test(stripped);
// (3) Names the settings file location
const namesClaudeSettingsJson = /\.claude\/settings\.json/.test(stripped);
// (4) Audit checklist — must mention all three classes the issue
// calls out, plus a "before this phase / pre-phase" framing
const includesPrePhaseAudit =
/audit|checklist|review (your )?mcp|before (starting|beginning) (a |the )?phase/i.test(stripped);
const auditMentionsBrowserOrPlaywright = /\bbrowser\b|playwright/i.test(stripped);
const auditMentionsPlatformSpecific = /platform[- ]specific|mac[- ]?tools|windows[- ]?tools|os[- ]specific/i.test(stripped);
const auditMentionsCrossProject = /(other|different|cross[- ])\s*project|stale (project )?mcp/i.test(stripped);
// (5) Harness vs GSD distinction — must explicitly state GSD doesn't
// own this knob and point at the harness
const explainsHarnessNotGsd =
/(gsd does(?:n[''’]t| not) (own|manage|control)|harness (concern|setting|controlled)|not a gsd (setting|knob))/i.test(stripped);
// (6) Compounding with model_profile
const mentionsModelProfileInteraction =
/model[_ ]profile/i.test(stripped) &&
/compound|multiplier|stack|every[- ]turn|regardless of (which )?model|in addition/i.test(stripped);
// (7) Cross-link to the canonical reference doc — task-guide section
// must point readers at context-budget.md for the full audit. Encoded
// as a named flag (CR follow-up) so the assertion sits alongside the
// other parsed invariants rather than as a one-off inline regex.
const crossLinksContextBudget = /context-budget/i.test(stripped);
return {
ok: true,
sectionLength: section.length,
explainsMcpAsBudgetConcern,
namesEnabledMcpjsonServers,
namesDisabledMcpjsonServers,
namesClaudeSettingsJson,
includesPrePhaseAudit,
auditMentionsBrowserOrPlaywright,
auditMentionsPlatformSpecific,
auditMentionsCrossProject,
explainsHarnessNotGsd,
mentionsModelProfileInteraction,
crossLinksContextBudget,
};
}
// ─── context-budget.md ──────────────────────────────────────────────────────
describe('#3025 context-budget.md: MCP token-budget section exists with required content', () => {
test('the file exists', () => {
assert.ok(fs.existsSync(CONTEXT_BUDGET_MD), `expected file at ${CONTEXT_BUDGET_MD}`);
});
test('has a section header that mentions MCP', () => {
const section = extractSection(CONTEXT_BUDGET_MD, 'mcp');
assert.ok(section, 'must have a `## ...MCP...` heading; section was not found');
});
test('explains MCP/tool schemas as a context-budget concern (#3025 requirement 1)', () => {
const section = extractSection(CONTEXT_BUDGET_MD, 'mcp');
const parsed = parseMcpBudgetSection(section);
assert.equal(parsed.explainsMcpAsBudgetConcern, true,
`must explain MCP/tool schemas as a token/context-budget concern; section was:\n${section}`);
});
test('names enabledMcpjsonServers and disabledMcpjsonServers (#3025 requirement 2)', () => {
const section = extractSection(CONTEXT_BUDGET_MD, 'mcp');
const parsed = parseMcpBudgetSection(section);
assert.equal(parsed.namesEnabledMcpjsonServers, true,
'section must reference `enabledMcpjsonServers` so users know the exact key');
assert.equal(parsed.namesDisabledMcpjsonServers, true,
'section must reference `disabledMcpjsonServers` for parity');
assert.equal(parsed.namesClaudeSettingsJson, true,
'section must name `.claude/settings.json` as the location of the toggle');
});
test('includes a pre-phase audit checklist with all three classes (#3025 requirement 3)', () => {
const section = extractSection(CONTEXT_BUDGET_MD, 'mcp');
const parsed = parseMcpBudgetSection(section);
assert.equal(parsed.includesPrePhaseAudit, true,
'section must include audit/checklist framing for pre-phase MCP review');
assert.equal(parsed.auditMentionsBrowserOrPlaywright, true,
'audit must mention browser/playwright tools as a candidate for disabling');
assert.equal(parsed.auditMentionsPlatformSpecific, true,
'audit must mention platform-specific tools (Mac/Windows/OS-specific)');
assert.equal(parsed.auditMentionsCrossProject, true,
'audit must mention stale/cross-project MCPs from other projects');
});
test('explains GSD does not own MCP enablement — harness concern (#3025 requirement 4)', () => {
const section = extractSection(CONTEXT_BUDGET_MD, 'mcp');
const parsed = parseMcpBudgetSection(section);
assert.equal(parsed.explainsHarnessNotGsd, true,
'section must explicitly state GSD does not manage MCP enablement (harness concern)');
});
test('notes interaction with model_profile (compounding levers) (#3025 requirement 5)', () => {
const section = extractSection(CONTEXT_BUDGET_MD, 'mcp');
const parsed = parseMcpBudgetSection(section);
assert.equal(parsed.mentionsModelProfileInteraction, true,
'section must note that trimming MCPs compounds with model_profile choice');
});
test('full semantic record matches the #3025 contract — typed snapshot', () => {
const section = extractSection(CONTEXT_BUDGET_MD, 'mcp');
const parsed = parseMcpBudgetSection(section);
const contract = {
ok: parsed.ok,
explainsMcpAsBudgetConcern: parsed.explainsMcpAsBudgetConcern,
namesEnabledMcpjsonServers: parsed.namesEnabledMcpjsonServers,
namesDisabledMcpjsonServers: parsed.namesDisabledMcpjsonServers,
namesClaudeSettingsJson: parsed.namesClaudeSettingsJson,
includesPrePhaseAudit: parsed.includesPrePhaseAudit,
auditMentionsBrowserOrPlaywright: parsed.auditMentionsBrowserOrPlaywright,
auditMentionsPlatformSpecific: parsed.auditMentionsPlatformSpecific,
auditMentionsCrossProject: parsed.auditMentionsCrossProject,
explainsHarnessNotGsd: parsed.explainsHarnessNotGsd,
mentionsModelProfileInteraction: parsed.mentionsModelProfileInteraction,
};
assert.deepStrictEqual(contract, {
ok: true,
explainsMcpAsBudgetConcern: true,
namesEnabledMcpjsonServers: true,
namesDisabledMcpjsonServers: true,
namesClaudeSettingsJson: true,
includesPrePhaseAudit: true,
auditMentionsBrowserOrPlaywright: true,
auditMentionsPlatformSpecific: true,
auditMentionsCrossProject: true,
explainsHarnessNotGsd: true,
mentionsModelProfileInteraction: true,
}, 'context-budget.md MCP section contract violated');
});
});
// ─── docs/USER-GUIDE.md task section ────────────────────────────────────────
describe('#3025 docs/USER-GUIDE.md: companion task section exists', () => {
test('USER-GUIDE.md has an MCP-trimming task section', () => {
const section = extractSection(USER_GUIDE_MD, 'mcp');
assert.ok(section,
'USER-GUIDE.md must have a `### ...MCP...` task section so users find it via the guide');
});
test('USER-GUIDE.md task section names the harness key and cross-links the reference', () => {
const section = extractSection(USER_GUIDE_MD, 'mcp');
const parsed = parseMcpBudgetSection(section);
assert.equal(parsed.namesEnabledMcpjsonServers, true,
'task section must mention the harness key by name');
// Cross-link to the reference doc — assert on the parsed flag so
// the invariant lives alongside the other named flags (CR follow-up
// on the no-source-grep standard).
assert.equal(parsed.crossLinksContextBudget, true,
'task section must cross-link to context-budget.md');
});
});
// ─── markdownlint pre-flight (per bundle-docs-with-code skill) ──────────────
describe('#3025 markdownlint pre-flight: MD040 + MD056', () => {
test('every fenced code block in the new MCP section has a language tag (MD040)', () => {
const section = extractSection(CONTEXT_BUDGET_MD, 'mcp');
// Guard: extractSection returns null when the section is missing.
// Without this, `section.match(...)` would throw a TypeError instead
// of producing a meaningful assertion failure (CR follow-up).
assert.ok(section, 'MCP section not found in context-budget.md — cannot check MD040');
const fences = (section.match(/^```([a-zA-Z0-9_+-]*)?\s*$/gm) || []);
// Pairs of fences open/close; odd-indexed ones close blocks. Every
// OPENING fence must have a language tag. Closing fences are bare ```.
// Walk pairs: even index = opener, odd = closer.
const openers = fences.filter((_, i) => i % 2 === 0);
const missing = openers.filter((line) => /^```\s*$/.test(line));
assert.deepStrictEqual(missing, [],
`every fenced code block opener must have a language tag (MD040). Missing: ${JSON.stringify(missing)}`);
});
test('every markdown table row in the new MCP section has the same column count as its header (MD056)', () => {
const section = extractSection(CONTEXT_BUDGET_MD, 'mcp');
// Guard: same null-section concern as MD040 above (CR follow-up).
assert.ok(section, 'MCP section not found in context-budget.md — cannot check MD056');
const lines = section.split(/\r?\n/);
// Walk through and detect tables: header row followed by a separator
// (--- pattern) followed by data rows. Count `|` per line.
const issues = [];
for (let i = 0; i < lines.length - 1; i += 1) {
const header = lines[i];
const sep = lines[i + 1];
if (!/^\s*\|.*\|\s*$/.test(header)) continue;
if (!/^\s*\|[\s\-:|]+\|\s*$/.test(sep)) continue;
const headerCols = (header.match(/\|/g) || []).length;
// Walk data rows
for (let j = i + 2; j < lines.length; j += 1) {
const row = lines[j];
if (!/^\s*\|.*\|\s*$/.test(row)) break;
const rowCols = (row.match(/\|/g) || []).length;
if (rowCols !== headerCols) {
issues.push({ line: j, expected: headerCols, actual: rowCols, row });
}
}
}
assert.deepStrictEqual(issues, [],
`table rows must match header column count (MD056). Issues: ${JSON.stringify(issues, null, 2)}`);
});
});
});
}