fix(#2665): watch kimi-code's config.toml, and pin it by name

The rebase onto next brought in #2755, which added a SECOND Kimi config
home — kimi-code's `~/.kimi-code`, overridden by KIMI_CODE_HOME — declared
as an inline object literal inside resolveKimiHooksTomlDir's body. That is
the resolvable-but-not-enumerable shape round 3 hoisted KIMI_SHARE_DIR out
of, so the hoist is extended to cover both descriptors rather than reverting
#2755's parameterization.

The scrub set was already complete: KIMI_CODE_HOME is declared in
capabilities/kimi-code/capability.json, so the registry rung covered it and
CONFIG_LOCATION_ENV_KEYS is 28 keys both before and after the rebase. What
was NOT covered is the guard — resolveExtraWatchTargets iterates
NON_REGISTRY_CONFIG_HOME_DESCRIPTORS, so with only one entry it watched Kimi
CLI's config.toml and never Kimi Code's. Targets go 2 -> 3.

The existing 'extra targets are DERIVED from the descriptor array' test
cannot catch this: it builds its expectation FROM the array, so removing an
entry shrinks the expectation with it. Verified — with the kimi-code
descriptor removed that test still passes while the new named test fails.
This is the enumeration-relative scope boundary the suite already documents
one layer down, biting one layer up.

Also rewrites NON_REGISTRY_OWNED_FILE's docblock, which asserted "today's
only such descriptor is kimi's ~/.kimi". There are now two, and its named
residual is load-bearing rather than vacuous.
This commit is contained in:
0xdhx
2026-08-03 18:24:10 -05:00
parent 111733a057
commit d1c8b32689
2 changed files with 34 additions and 7 deletions

View File

@@ -80,13 +80,15 @@ const GSD_ARTIFACT_PREFIX = 'gsd-';
/**
* The file GSD writes into a NON-REGISTRY config home.
*
* Today's only such descriptor is kimi's `~/.kimi` (KIMI_SHARE_DIR), where GSD
* writes its native `[[hooks]]` block into `config.toml`. NAMED RESIDUAL: this
* assumes every non-registry descriptor is written the same way. A future
* descriptor whose owned file differs needs a per-descriptor mapping here — the
* consequence of getting it wrong is under-watching (a missed leak), not a false
* positive, so it fails in the quiet direction and is called out rather than
* left to be discovered.
* Both current descriptors are Kimi's — Kimi CLI's `~/.kimi` (KIMI_SHARE_DIR) and
* Kimi Code's `~/.kimi-code` (KIMI_CODE_HOME) — and GSD writes its native
* `[[hooks]]` block into `config.toml` in each, so the single filename below holds
* for both. NAMED RESIDUAL: this assumes every non-registry descriptor is written
* the same way. That assumption is now load-bearing rather than vacuous — it is
* carrying two descriptors, not one — and a future descriptor whose owned file
* differs needs a per-descriptor mapping here. The consequence of getting it wrong
* is under-watching (a missed leak), not a false positive, so it fails in the quiet
* direction and is called out rather than left to be discovered.
*/
const NON_REGISTRY_OWNED_FILE = 'config.toml';

View File

@@ -229,6 +229,31 @@ describe('#2665: guard watches non-root write surfaces', () => {
}
});
test('#2755: kimi-code config.toml is watched — named, not enumeration-relative', () => {
// The test above derives its expectation FROM the descriptor array, so it
// passes for whatever that array happens to contain and cannot see a
// descriptor that was never added — the enumeration-relative scope boundary
// this suite already calls out one layer down. #2755 landed kimi-code's
// `~/.kimi-code` (KIMI_CODE_HOME) on `next` as an inline literal inside
// resolveKimiHooksTomlDir's body; until it was hoisted into
// NON_REGISTRY_CONFIG_HOME_DESCRIPTORS the guard watched Kimi CLI's
// config.toml and not Kimi Code's. Naming the path is what makes dropping
// the descriptor fail loudly instead of quietly shrinking the expectation.
const home = tmpRoot();
const codeHome = tmpRoot();
try {
const env = { GSD_HOME: home, KIMI_CODE_HOME: codeHome };
const targets = resolveExtraWatchTargets({ env, os: { homedir: () => home } });
assert.ok(
targets.includes(path.resolve(path.join(codeHome, 'config.toml'))),
`expected kimi-code config.toml in ${JSON.stringify(targets)}`,
);
} finally {
cleanup(home);
cleanup(codeHome);
}
});
test('GSD_HOME falls back to homedir when unset', () => {
const home = tmpRoot();
try {