diff --git a/scripts/live-config-guard.cjs b/scripts/live-config-guard.cjs index 3577b329c..7290371b8 100644 --- a/scripts/live-config-guard.cjs +++ b/scripts/live-config-guard.cjs @@ -142,22 +142,26 @@ function resolveLiveConfigRoots(deps = {}) { * `root x GSD_OWNED_ENTRIES`. * * #2665 round 3: resolveLiveConfigRoots enumerates getGlobalConfigDir per registry - * runtime plus grok. Two live write surfaces are invisible to that shape, so a leak - * on either passed through this guard — the PR's own safety net — silently: + * runtime plus grok. A live write surface that is not a config ROOT is invisible to + * that shape, so a leak on one passed through this guard — the PR's own safety net — + * silently. There are THREE today ($GSD_HOME/.gsd, plus one config.toml per entry in + * NON_REGISTRY_CONFIG_HOME_DESCRIPTORS, which #2755 took from one entry to two): * * $GSD_HOME/.gsd — GSD's user-owned store (consent.json, defaults.json, capability * overlays). Watched WHOLESALE: unlike ~/.claude this root is * exclusively ours, so the shared-root false-positive trap in * SCOPE above does not apply and an ownership filter would only * narrow the guard for nothing. - * /config.toml — the file GSD writes its native [[hooks]] block into - * (resolveKimiHooksTomlDir, KIMI_SHARE_DIR). The INVERSE case: - * ~/.kimi belongs to Kimi CLI, so only the one file GSD writes is + * /config.toml — the file GSD writes its native [[hooks]] block + * into, one per NON_REGISTRY_CONFIG_HOME_DESCRIPTORS entry: Kimi + * CLI's ~/.kimi (KIMI_SHARE_DIR) and, since #2755, Kimi Code's + * ~/.kimi-code (KIMI_CODE_HOME). The INVERSE case: those roots + * belong to their products, so only the one file GSD writes is * watched, never the root. This is the KNOWN GAP above accepted - * deliberately in one direction — GSD demonstrably writes this - * file (bin/install.js calls resolveKimiHooksTomlDir at two sites), - * so a concurrent Kimi CLI write is the only false positive, and - * Kimi is not running during the suite. + * deliberately in one direction — GSD demonstrably writes these + * files (bin/install.js resolves the hooks-toml dir at two sites), + * so a concurrent write by those products is the only false + * positive, and neither runs during the suite. * * @returns {string[]} absolute paths; empty if the built lib is absent. */ @@ -175,7 +179,7 @@ function resolveExtraWatchTargets(deps = {}) { } = require(path.join(libDir, 'runtime-homes.cjs')); // ITERATE the descriptor array rather than naming one resolver. Calling - // resolveKimiHooksTomlDir directly would cover today's only entry and + // resolveKimiHooksTomlDir directly would cover one of today's two entries and // silently miss tomorrow's — the same partial-enumeration defect that put // KIMI_SHARE_DIR outside the scrub set in the first place, reintroduced one // layer over. TEST_ENV_BASE derives its keys from this array; deriving the diff --git a/scripts/run-tests.cjs b/scripts/run-tests.cjs index 0fabccd13..24d12767f 100644 --- a/scripts/run-tests.cjs +++ b/scripts/run-tests.cjs @@ -975,15 +975,17 @@ function main() { // #2665: snapshot GSD's install footprint in every LIVE runtime config dir // before a single test runs. The suite must not write there; the check after // the chunk loop is what makes a violation loud instead of silent. See - // scripts/lib/live-config-guard.cjs for why the scope is narrow. + // scripts/live-config-guard.cjs for why the scope is narrow (it is deliberately + // NOT under scripts/lib/, which the installer copies to users wholesale). const liveConfigGuardEnabled = process.env.GSD_SKIP_LIVE_CONFIG_GUARD !== '1'; let liveConfigRoots = []; let liveConfigExtras = []; let liveConfigBefore = null; if (liveConfigGuardEnabled) { liveConfigRoots = resolveLiveConfigRoots(); - // #2665 round 3: $GSD_HOME/.gsd and kimi's native config.toml are live write - // surfaces that are not runtime config ROOTS, so they are invisible to the + // #2665 round 3: $GSD_HOME/.gsd, and one native config.toml per non-registry + // config-home descriptor (Kimi CLI's and, since #2755, Kimi Code's), are live + // write surfaces that are not runtime config ROOTS, so they are invisible to the // line above. Watched independently — and note the OR: the extras alone are // reason enough to snapshot, so an unbuilt tree that yields zero roots no // longer silently disables the whole guard. diff --git a/tests/live-config-guard.test.cjs b/tests/live-config-guard.test.cjs index d54f836cc..1f8e1a1a5 100644 --- a/tests/live-config-guard.test.cjs +++ b/tests/live-config-guard.test.cjs @@ -172,7 +172,7 @@ describe('#2665: live-config hermeticity guard', () => { }); }); -// ── Round 3: the two write surfaces that are not runtime config ROOTS ──────── +// ── Round 3: the write surfaces that are not runtime config ROOTS ─────────── describe('#2665: guard watches non-root write surfaces', () => { test('resolveExtraWatchTargets covers $GSD_HOME/.gsd and kimi config.toml', () => { const home = tmpRoot(); @@ -207,7 +207,7 @@ describe('#2665: guard watches non-root write surfaces', () => { const targets = resolveExtraWatchTargets({ env, os: { homedir: () => home } }); // Every descriptor in the array must contribute a target. Calling one - // named resolver instead would cover today's single entry and silently + // named resolver instead would cover one of today's two entries and silently // miss tomorrow's — the same partial-enumeration defect that put // KIMI_SHARE_DIR outside the scrub set, one layer over. for (const d of NON_REGISTRY_CONFIG_HOME_DESCRIPTORS) {