Merge branch 'next' into fix/1477-surface-source-marker

This commit is contained in:
Joe
2026-06-27 11:27:38 -05:00
committed by GitHub
33 changed files with 1486 additions and 18 deletions

View File

@@ -0,0 +1,245 @@
'use strict';
/**
* E2E capability-wiring tests for the assumption-delta checkpoint (#1561).
*
* Drives the real `loop render-hooks plan:pre` CLI subprocess against temp
* projects with different config values and asserts on the typed envelope's
* activeHooks — proving the capability contribution activates/deactivates by
* config (acceptance criteria #4 non-blocking advisory + #6 capability hook).
*
* CONTENT/E2E only: every test drives a real CLI subprocess. No readFileSync
* source-grep. Genuine assertions: each case asserts the SPECIFIC differing
* value (capId presence/absence), not a count (plan:pre carries other
* default-on contributions owned by other capabilities).
*/
const { describe, test, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { execFileSync } = require('node:child_process');
const { cleanup } = require('./helpers.cjs');
const TOOLS_PATH = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
const TEST_ENV_BASE = {
GSD_SESSION_KEY: '',
CODEX_THREAD_ID: '',
CLAUDE_SESSION_ID: '',
CLAUDE_CODE_SSE_PORT: '',
OPENCODE_SESSION_ID: '',
GEMINI_SESSION_ID: '',
CURSOR_SESSION_ID: '',
WINDSURF_SESSION_ID: '',
TERM_SESSION_ID: '',
WT_SESSION: '',
TMUX_PANE: '',
ZELLIJ_SESSION_NAME: '',
TTY: '',
SSH_TTY: '',
};
function runTools(args, cwd) {
const argv = Array.isArray(args)
? args
: (args.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || [])
.map((t) => t.replace(/"([^"]*)"/g, '$1').replace(/'([^']*)'/g, '$1'));
try {
const stdout = execFileSync(process.execPath, [TOOLS_PATH, ...argv], {
cwd,
encoding: 'utf-8',
env: { ...process.env, ...TEST_ENV_BASE },
timeout: 60000,
});
return { success: true, output: stdout.trim(), exitCode: 0, error: '' };
} catch (err) {
return {
success: false,
output: err.stdout?.toString().trim() || '',
error: err.stderr?.toString().trim() || err.message,
exitCode: err.status ?? 1,
};
}
}
function makeProject(config) {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-adelta-'));
fs.mkdirSync(path.join(tmpDir, '.planning'), { recursive: true });
fs.writeFileSync(
path.join(tmpDir, '.planning', 'config.json'),
JSON.stringify(config),
'utf8'
);
return tmpDir;
}
function planPreHooks(cwd) {
const result = runTools('loop render-hooks plan:pre --raw', cwd);
assert.ok(result.success, `render-hooks plan:pre should succeed. stderr: ${result.error}`);
const envelope = JSON.parse(result.output);
assert.strictEqual(envelope.point, 'plan:pre', 'point field must be plan:pre');
assert.ok(Array.isArray(envelope.activeHooks), 'activeHooks must be an array');
return envelope;
}
function findCap(envelope, capId) {
return envelope.activeHooks.find((h) => h.capId === capId) || null;
}
// ─── ROADMAP fixture for the scan query ──────────────────────────────────────
const ROADMAP = [
'# Roadmap',
'',
'## v1.0.0',
'',
'### Phase 1: Add a second auth method alongside passwords',
'**Goal:** Users can authenticate via SSO in addition to passwords',
'**Success Criteria**:',
'1. SSO login works',
'2. Password login still works',
'',
'### Phase 2: Refactor the parser for readability',
'**Goal:** Smaller functions, no behavior change',
'**Success Criteria**:',
'1. All existing tests still pass',
'',
].join('\n');
function makeRoadmapProject() {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-adelta-rm-'));
fs.mkdirSync(path.join(tmpDir, '.planning'), { recursive: true });
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), ROADMAP, 'utf8');
return tmpDir;
}
function scanQuery(cwd, phase, extra) {
const argv = ['query', 'assumption-delta', 'scan', String(phase)];
if (extra) argv.push(...extra);
return runTools(argv, cwd);
}
describe('assumption-delta scan query — phase-section detection (#1561)', () => {
let tmpDir;
afterEach(() => { if (tmpDir) { cleanup(tmpDir); tmpDir = null; } });
test('phase with a pluralization signal → detected:true', () => {
tmpDir = makeRoadmapProject();
const r = scanQuery(tmpDir, 1, ['--json']);
assert.ok(r.success, `scan should succeed. stderr: ${r.error}`);
const parsed = JSON.parse(r.output);
assert.strictEqual(parsed.detected, true);
assert.ok(parsed.signals.some((s) => s.kind === 'pluralization'), 'phase 1 must trip pluralization');
});
test('phase with no signal → detected:false (low false-positive)', () => {
tmpDir = makeRoadmapProject();
const r = scanQuery(tmpDir, 2, ['--json']);
assert.ok(r.success, `scan should succeed. stderr: ${r.error}`);
const parsed = JSON.parse(r.output);
assert.strictEqual(parsed.detected, false);
assert.deepStrictEqual(parsed.signals, []);
});
test('unknown phase → detected:false, no throw (graceful)', () => {
tmpDir = makeRoadmapProject();
const r = scanQuery(tmpDir, 999, ['--json']);
assert.ok(r.success, `scan should succeed on unknown phase. stderr: ${r.error}`);
assert.strictEqual(JSON.parse(r.output).detected, false);
});
test('--terms override narrows the vocabulary (custom cue fires, default cue does not)', () => {
tmpDir = makeRoadmapProject();
// phase 1 trips "second" by default; override to "xyzzy" → must NOT fire
const r = scanQuery(tmpDir, 1, ['--json', '--terms', 'xyzzy']);
assert.ok(r.success, `scan should succeed. stderr: ${r.error}`);
assert.strictEqual(JSON.parse(r.output).detected, false);
});
test('missing phase arg → non-zero exit (usage error)', () => {
tmpDir = makeRoadmapProject();
const r = runTools(['query', 'assumption-delta', 'scan'], tmpDir);
assert.notStrictEqual(r.exitCode, 0, 'scan with no phase must exit non-zero');
});
// ── Hardening (Codex Step-4 review): malformed args ──────────────────────
test('flag-shaped phase (scan --json) → non-zero exit, not treated as phase', () => {
tmpDir = makeRoadmapProject();
const r = runTools(['query', 'assumption-delta', 'scan', '--json'], tmpDir);
assert.notStrictEqual(r.exitCode, 0, '"--json" must not be accepted as a phase number');
});
test('empty --terms restores curated defaults (detected, not disabled)', () => {
tmpDir = makeRoadmapProject();
const r = scanQuery(tmpDir, 1, ['--terms', '', '--json']);
assert.ok(r.success, `scan should succeed. stderr: ${r.error}`);
const parsed = JSON.parse(r.output);
assert.strictEqual(parsed.detected, true, 'phase 1 must still trip defaults under empty --terms');
assert.ok(parsed.terms.pluralization.includes('second'));
});
test('flag-shaped --terms value (--terms --json) falls back to defaults, not treated as a term', () => {
tmpDir = makeRoadmapProject();
const r = scanQuery(tmpDir, 1, ['--terms', '--json']);
assert.ok(r.success, `scan should succeed. stderr: ${r.error}`);
const parsed = JSON.parse(r.output);
// 'json' must NOT have been consumed as a pluralization cue; defaults apply.
assert.ok(!parsed.terms.pluralization.includes('json'), '"--json" must not become a trigger term');
assert.ok(parsed.terms.pluralization.includes('second'), 'defaults restored');
});
});
describe('assumption-delta capability — plan:pre render-hooks wiring (#1561)', () => {
let tmpDir;
afterEach(() => { if (tmpDir) { cleanup(tmpDir); tmpDir = null; } });
test('default config (no toggle): assumption-delta contribution is ACTIVE', () => {
tmpDir = makeProject({});
const env = planPreHooks(tmpDir);
const hook = findCap(env, 'assumption-delta');
assert.ok(hook, 'assumption-delta contribution must be active under default config');
assert.strictEqual(hook.kind, 'contribution', 'must be a non-blocking contribution, not a gate');
assert.strictEqual(hook.into, 'planner', 'contribution injects into the planner role');
assert.strictEqual(hook.when, 'workflow.assumption_delta', 'when must gate on workflow.assumption_delta');
assert.strictEqual(hook.onError, 'skip', 'onError must be skip (advisory, never halts)');
// The fragment body must be inlined so the planner receives concrete prose.
const frag = hook.fragment && hook.fragment.inline ? hook.fragment.inline : hook.fragment;
assert.ok(typeof frag === 'string' && frag.length > 0, 'fragment must be inlined as non-empty text');
assert.ok(
/Assumption-Delta Architecture Checkpoint/i.test(frag),
'inlined fragment must carry the checkpoint heading'
);
});
test('workflow.assumption_delta=true: contribution is ACTIVE', () => {
tmpDir = makeProject({ workflow: { assumption_delta: true } });
const env = planPreHooks(tmpDir);
assert.ok(findCap(env, 'assumption-delta'), 'must be active when explicitly true');
});
test('workflow.assumption_delta=false: contribution is INACTIVE (absent from activeHooks)', () => {
tmpDir = makeProject({ workflow: { assumption_delta: false } });
const env = planPreHooks(tmpDir);
assert.strictEqual(
findCap(env, 'assumption-delta'),
null,
'must NOT appear in activeHooks when disabled — other default-on plan:pre hooks may still be present'
);
});
test('non-blocking guarantee: no assumption-delta entry is ever a blocking gate', () => {
// Advisory contract (acceptance #4): the checkpoint informs; it never blocks.
// Across both on/off states the capability must never surface as kind=gate
// with blocking=true.
tmpDir = makeProject({ workflow: { assumption_delta: true } });
const env = planPreHooks(tmpDir);
const gates = env.activeHooks.filter((h) => h.kind === 'gate');
assert.ok(
gates.every((g) => g.capId !== 'assumption-delta'),
'assumption-delta must never register a blocking gate at plan:pre'
);
});
});

View File

@@ -0,0 +1,297 @@
/**
* Tests for the assumption-delta detector (#1561).
*
* The detector is a pure function over phase-scope text that returns a typed
* IR ({ detected, signals, terms }). Tests assert on the IR — never on
* rendered prose — per RULESET.TESTS (no raw text matching on outputs).
*
* The detector mirrors ui-safety-gate.cts: a pure function plus a STDIN-reading
* CLI (exit 0 = signal detected, 1 = none, 2 = usage error).
*/
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const { spawnSync } = require('node:child_process');
const path = require('node:path');
const MODULE_PATH = path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'assumption-delta.cjs');
describe('detectAssumptionDelta — pure detector (#1561)', () => {
let mod;
try {
mod = require(MODULE_PATH);
} catch (err) {
// Surface a clear failure if build:lib has not run yet.
throw new Error(
`Could not require ${MODULE_PATH}. Run "npm run build:lib" first. Underlying: ${err.message}`
);
}
const { detectAssumptionDelta, DEFAULT_ASSUMPTION_DELTA_TERMS } = mod;
test('result shape — always carries detected, signals[], terms', () => {
const r = detectAssumptionDelta('refactor the login function');
assert.strictEqual(r.detected, false);
assert(Array.isArray(r.signals));
assert.strictEqual(r.signals.length, 0);
assert.ok(r.terms && Array.isArray(r.terms.pluralization));
assert.ok(Array.isArray(r.terms.optional));
assert.ok(Array.isArray(r.terms.chosen));
});
test('terms echo is the effective term set actually used', () => {
const r = detectAssumptionDelta('nothing here');
assert.deepStrictEqual(r.terms.pluralization, [...DEFAULT_ASSUMPTION_DELTA_TERMS.pluralization]);
assert.deepStrictEqual(r.terms.optional, [...DEFAULT_ASSUMPTION_DELTA_TERMS.optional]);
assert.deepStrictEqual(r.terms.chosen, [...DEFAULT_ASSUMPTION_DELTA_TERMS.chosen]);
});
// ── Primary trigger: pluralization ───────────────────────────────────────
for (const cue of ['second auth method', 'alternative platform', 'fallback provider', 'also support a second region', 'an additional source of truth']) {
test(`pluralization fires on: "${cue}"`, () => {
const r = detectAssumptionDelta(cue);
assert.strictEqual(r.detected, true, `expected detection for: ${cue}`);
assert.ok(r.signals.some((s) => s.kind === 'pluralization'), `expected a pluralization signal for: ${cue}`);
});
}
// ── Secondary trigger: required → optional ───────────────────────────────
for (const cue of ['the field becomes optional', 'optionally omitted', 'may be optional now']) {
test(`optional fires on: "${cue}"`, () => {
const r = detectAssumptionDelta(cue);
assert.strictEqual(r.detected, true, `expected detection for: ${cue}`);
assert.ok(r.signals.some((s) => s.kind === 'optional'), `expected an optional signal for: ${cue}`);
});
}
// ── Secondary trigger: derived → chosen / constant → parameter ───────────
for (const cue of ['value is chosen by the caller', 'now configurable per tenant', 'parameterized at runtime', 'selectable in settings']) {
test(`chosen fires on: "${cue}"`, () => {
const r = detectAssumptionDelta(cue);
assert.strictEqual(r.detected, true, `expected detection for: ${cue}`);
assert.ok(r.signals.some((s) => s.kind === 'chosen'), `expected a chosen signal for: ${cue}`);
});
}
// ── No-signal phases do NOT fire (acceptance criterion #2 — low FP) ───────
for (const clean of ['refactor the login function', 'add a unit test for the parser', 'fix the off-by-one in the loop', 'update the README install steps']) {
test(`no-signal phase does NOT fire: "${clean}"`, () => {
const r = detectAssumptionDelta(clean);
assert.strictEqual(r.detected, false, `false positive on: ${clean}`);
assert.strictEqual(r.signals.length, 0);
});
}
// ── FALSE-POSITIVE GUARD: bare "or" in prose must NOT fire ────────────────
// The issue lists "or" as a tell, but bare "or" is extremely common in
// English prose and would make the gate fire constantly. The default term
// set intentionally excludes bare "or"; pluralization requires a stronger
// second-case cue (second/alternative/fallback/also/additional/...).
test('FALSE-POSITIVE GUARD: bare "or" in normal prose does NOT fire', () => {
const r = detectAssumptionDelta('refactor or rewrite the module to be cleaner');
assert.strictEqual(r.detected, false, 'bare "or" must not fire — it would make every English sentence trip the gate');
});
// ── FALSE-POSITIVE GUARD: trigger term inside a fenced code block ─────────
// A code snippet mentioning "fallback" is not a pluralization of an
// architectural concept. Fenced blocks are stripped before scanning.
test('FALSE-POSITIVE GUARD: trigger term inside a fenced code block does NOT fire', () => {
const scope = [
'Add a retry helper to the client.',
'',
'```js',
'const fallback = () => retry(); // internal var name',
'```',
'',
'No architectural change here.',
].join('\n');
const r = detectAssumptionDelta(scope);
assert.strictEqual(r.detected, false, 'a trigger term appearing only inside a fenced code block must not fire');
});
// ── A real signal in prose still fires even when a code block is present ──
test('signal in prose fires even when an unrelated fenced block is present', () => {
const scope = [
'This phase adds a second platform alongside the existing one.',
'',
'```js',
'const x = 1;',
'```',
].join('\n');
const r = detectAssumptionDelta(scope);
assert.strictEqual(r.detected, true);
assert.ok(r.signals.some((s) => s.kind === 'pluralization'));
});
// ── signal carries a usable context snippet ──────────────────────────────
test('each signal carries a non-empty snippet with context', () => {
const r = detectAssumptionDelta('This phase introduces a second authentication method.');
assert.strictEqual(r.detected, true);
const sig = r.signals[0];
assert.ok(typeof sig.snippet === 'string' && sig.snippet.length > 0);
assert.ok(sig.snippet.toLowerCase().includes(sig.term), 'snippet should contain the matched term');
});
// ── CRLF resilience ───────────────────────────────────────────────────────
test('CRLF line endings are handled identically to LF', () => {
const lf = detectAssumptionDelta('adds a second region\r\nalso configurable');
const crlf = detectAssumptionDelta('adds a second region\nalso configurable');
assert.strictEqual(lf.detected, true);
assert.strictEqual(crlf.detected, true);
assert.strictEqual(lf.signals.length, crlf.signals.length);
});
// ── empty / whitespace / non-string inputs degrade to detected:false ──────
test('empty string → detected:false', () => {
assert.strictEqual(detectAssumptionDelta('').detected, false);
});
test('whitespace-only → detected:false', () => {
assert.strictEqual(detectAssumptionDelta(' \n\t ').detected, false);
});
test('non-string (null/undefined/number) → detected:false, no throw', () => {
assert.strictEqual(detectAssumptionDelta(null).detected, false);
assert.strictEqual(detectAssumptionDelta(undefined).detected, false);
assert.strictEqual(detectAssumptionDelta(42).detected, false);
});
// ── custom term set overrides defaults (config-tunable vocabulary) ────────
test('custom term set overrides defaults', () => {
const custom = { pluralization: ['xyzzy'], optional: [], chosen: [] };
const r = detectAssumptionDelta('this phase adds a second platform', custom);
assert.strictEqual(r.detected, false, 'default cue "second" must not fire when defaults are overridden');
assert.deepStrictEqual(r.terms.pluralization, ['xyzzy']);
const r2 = detectAssumptionDelta('introduces an xyzzy adapter', custom);
assert.strictEqual(r2.detected, true);
assert.ok(r2.signals.some((s) => s.term === 'xyzzy'));
});
test('partial custom term set merges over defaults per-kind (absent kinds keep defaults)', () => {
const partial = { pluralization: ['second'] };
const r = detectAssumptionDelta('now optional', partial);
assert.strictEqual(r.detected, true, 'optional defaults still apply when only pluralization was overridden');
assert.ok(r.signals.some((s) => s.kind === 'optional'));
});
});
describe('assumption-delta CLI — STDIN exit codes (mirrors ui-safety-gate)', () => {
// Exit code contract: 0 = signal detected, 1 = none, 2 = usage/startup error.
function runCli(stdin) {
const res = spawnSync(process.execPath, [MODULE_PATH], {
input: stdin,
encoding: 'utf-8',
timeout: 15000,
});
return { status: res.status, stdout: res.stdout ?? '', stderr: res.stderr ?? '' };
}
test('exit 0 when a pluralization signal is present', () => {
const r = runCli('This phase adds a second platform alongside the existing one.');
assert.strictEqual(r.status, 0);
});
test('exit 1 when no signal is present', () => {
const r = runCli('Refactor the login function to be smaller.');
assert.strictEqual(r.status, 1);
});
test('exit 1 on empty stdin (no signal)', () => {
const r = runCli('');
assert.strictEqual(r.status, 1);
});
test('--json emits typed IR with detected field on stdout (exit 0)', () => {
const res = spawnSync(process.execPath, [MODULE_PATH, '--json'], {
input: 'introduces a configurable retry policy',
encoding: 'utf-8',
timeout: 15000,
});
assert.strictEqual(res.status, 0);
const parsed = JSON.parse(res.stdout);
assert.strictEqual(parsed.detected, true);
assert.ok(Array.isArray(parsed.signals));
assert.ok(parsed.signals.some((s) => s.kind === 'chosen'));
});
test('--json emits detected:false on stdout (exit 1) for no-signal input', () => {
const res = spawnSync(process.execPath, [MODULE_PATH, '--json'], {
input: 'just a routine refactor',
encoding: 'utf-8',
timeout: 15000,
});
assert.strictEqual(res.status, 1);
const parsed = JSON.parse(res.stdout);
assert.strictEqual(parsed.detected, false);
assert.deepStrictEqual(parsed.signals, []);
});
// ── --terms config override (config-tunable vocabulary) ───────────────────
test('--terms overrides the pluralization cues (custom term fires, default cue does not)', () => {
// default cue "second" present, but overridden to "xyzzy" → must NOT fire
const noFire = spawnSync(process.execPath, [MODULE_PATH, '--terms', 'xyzzy', '--json'], {
input: 'adds a second platform',
encoding: 'utf-8',
timeout: 15000,
});
assert.strictEqual(noFire.status, 1);
assert.strictEqual(JSON.parse(noFire.stdout).detected, false);
const fire = spawnSync(process.execPath, [MODULE_PATH, '--terms', 'xyzzy', '--json'], {
input: 'introduces an xyzzy adapter',
encoding: 'utf-8',
timeout: 15000,
});
assert.strictEqual(fire.status, 0);
const parsed = JSON.parse(fire.stdout);
assert.strictEqual(parsed.detected, true);
assert.ok(parsed.signals.some((s) => s.term === 'xyzzy' && s.kind === 'pluralization'));
// optional/chosen defaults are retained by the partial override
assert.ok(parsed.terms.optional.length > 0, 'optional defaults retained under --terms override');
});
});
// ─── Hardening (Codex Step-4 review fixes) ────────────────────────────────────
describe('assumption-delta hardening (Codex review)', () => {
const { detectAssumptionDelta } = require(MODULE_PATH);
test('normalizeTerms: punctuation-only / empty / dupe terms filtered; lowercased', () => {
const r = detectAssumptionDelta('adds a second platform', {
pluralization: ['second', 'second', '-', '', 'XYZZY'],
optional: [],
chosen: [],
});
// '-' (punct-only) and '' dropped; dupe 'second' collapsed; 'XYZZY'→'xyzzy'
assert.deepStrictEqual(r.terms.pluralization, ['second', 'xyzzy']);
// 'second' survived → detected
assert.strictEqual(r.detected, true);
});
test('normalizeTerms: cap guards a huge/hostile term list (no giant regex / echo)', () => {
const huge = Array.from({ length: 250 }, (_, i) => `cue${i}`);
const r = detectAssumptionDelta('routine refactor', { pluralization: huge, optional: [], chosen: [] });
assert.ok(r.terms.pluralization.length <= 200, `capped to <=200, got ${r.terms.pluralization.length}`);
assert.strictEqual(r.detected, false);
});
test('punctuation-only term does NOT match prose punctuation as a signal', () => {
// '-' as a term must not fire on "a - b" prose
const r = detectAssumptionDelta('refactor the parser - keep behavior', {
pluralization: ['-'],
optional: [],
chosen: [],
});
assert.strictEqual(r.detected, false, 'punctuation-only term must not produce a signal');
assert.deepStrictEqual(r.terms.pluralization, []);
});
test('CLI --terms "" (empty) restores curated defaults (does NOT disable pluralization)', () => {
const res = spawnSync(process.execPath, [MODULE_PATH, '--terms', '', '--json'], {
input: 'adds a second platform',
encoding: 'utf-8',
timeout: 15000,
});
assert.strictEqual(res.status, 0, 'empty --terms must fall back to defaults → detected');
const parsed = JSON.parse(res.stdout);
assert.strictEqual(parsed.detected, true);
assert.ok(parsed.terms.pluralization.includes('second'), 'default pluralization cues restored');
});
});

View File

@@ -38,7 +38,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "46deb2174be356dd",
"gsd-core/bin/gsd-tools.cjs": "5cf26d5f9e588cf8",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -107,7 +107,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "b7968e3e3af00249",
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -37,7 +37,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "b7968e3e3af00249",
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -41,7 +41,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "b7968e3e3af00249",
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -107,7 +107,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "b7968e3e3af00249",
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -73,7 +73,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "b7968e3e3af00249",
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -39,7 +39,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "46deb2174be356dd",
"gsd-core/bin/gsd-tools.cjs": "5cf26d5f9e588cf8",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -107,7 +107,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "46deb2174be356dd",
"gsd-core/bin/gsd-tools.cjs": "5cf26d5f9e588cf8",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -107,7 +107,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "b7968e3e3af00249",
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -38,7 +38,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "b7968e3e3af00249",
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -107,7 +107,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "b7968e3e3af00249",
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -74,7 +74,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "b7968e3e3af00249",
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -107,7 +107,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "b7968e3e3af00249",
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -38,7 +38,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "b7968e3e3af00249",
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -38,7 +38,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "46deb2174be356dd",
"gsd-core/bin/gsd-tools.cjs": "5cf26d5f9e588cf8",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -38,7 +38,7 @@
"gsd-core/CHANGELOG.md": "e141e3fb369ff712",
"gsd-core/VERSION": "562368b20a64be95",
"gsd-core/bin/check-latest-version.cjs": "e4a224058c8f4d74",
"gsd-core/bin/gsd-tools.cjs": "46deb2174be356dd",
"gsd-core/bin/gsd-tools.cjs": "5cf26d5f9e588cf8",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",

View File

@@ -254,6 +254,7 @@ describe('plan:pre all-off — empty resolution', () => {
pattern_mapper: false,
schema_push_detection: false,
plan_drift_precheck: false,
assumption_delta: false,
},
intel: { enabled: false },
});