* feat(#2454): add kimi-code as an EoS capability (Node Kimi Code CLI)
PR 1 of N for #2454. Establishes the EoS descriptor foundation for splitting
GSD's kimi support into two distinct products per the user's directive:
- kimi (existing): Moonshot's Python kimi-cli (~/.kimi, runtime: python)
- kimi-code (new): Moonshot's Node Kimi Code CLI (~/.kimi-code,
runtime: node, KIMI_CODE_HOME env)
Per ADR-1239 EoS, runtime behavior is driven by capabilities/<id>/capability.json
descriptors, not hardcoded branches in install.js. The new descriptor uses
the existing primitives (dot-home configHome, skills artifactLayout, kimi-hooks-toml
hooksSurface — same TOML [[hooks]] format Kimi Code reads per its docs).
Critical Kimi Code constraint reflected in the descriptor:
hostIntegration.dispatch.namedDispatch: false
hostIntegration.dispatch.builtInSubagents: ['coder', 'explore', 'plan']
hostBehaviors.namedSubagentsSupported: false
Kimi Code's official docs confirm only 3 built-in subagents with NO custom-
subagent registration (the [subagent] table only has timeout_ms). The
kimi-agents YAML layout (used by Python kimi-cli) is therefore NOT in
kimi-code's artifactLayout.
Schema adjustments:
- subagentToolkit set to 'undocumented' (the existing escape hatch); the
schema enum (full/read-only) lacks a 'limited'/'built-in-only' value.
A follow-up PR can extend the schema enum to add 'built-in-only' as a
first-class axis value reflecting Kimi Code's documented model.
Registration:
- capabilities/kimi-code/capability.json (new descriptor, modeled on codex)
- bin/install.js: allRuntimes array + --all list + --kimi-code flag
- gsd-core/bin/shared/runtime-aliases.manifest.json: kimi-code aliases
(kimi-code, kimicode, kimi_code)
- src/runtime-name-policy.cts: FALLBACK_ALIASES map
- gsd-core/bin/lib/capability-registry.cjs: regenerated via
scripts/gen-capability-registry.cjs --write
Tests:
- tests/multi-runtime-select.test.cjs updated for the new runtime count (18)
+ new --kimi-code flag test + 'All' shortcut renumbered 18 → 19.
Out of scope for PR 1 (follow-up PRs in the sequence):
- Install-time decision logic (kimi vs kimi-code detection / prompt)
- agent-install-check semantics for kimi-code (verify Agent Skills presence)
- cmdAgentSkills fallback returning subagent prompt content
- Workflow template mapping (named agents → built-in coder/explore/plan)
- Migration guidance for users currently on 'kimi' who are actually on Kimi Code
- Schema enum extension for subagentToolkit: 'built-in-only'
Refs #2454, #2095 (EoS/kimi migration epic), ADR-1239 (EoS).
* fix(#2454): complete drift-guard registrations for kimi-code runtime
The drift guards caught every surface that pins runtime enumeration. Each
update is mechanical, driven by the guard's named failure mode:
- src/runtime-name-policy.cts RUNTIME_LABELS: 'Kimi Code' label for kimi-code
- src/runtime-name-policy.cts RUNTIME_FLAG_IDS: add kimi-code to the
isKimiCode predicate generator
- bin/install.js runtimeMap: option '11' → 'kimi-code', renumber downstream
entries (11..17 → 12..18), ALL_RUNTIMES_OPTION 18 → 19
- gsd-core/bin/shared/model-catalog.json runtimeTierDefaults: kimi-code entry
(null/null/null — same as kimi, no model tier defaults until configured)
- docs/reference/capability-matrix.md: regenerated via
scripts/gen-capability-matrix.cjs --write (kimi-code row added)
- tests/global-config-home-fragment.test.cjs GOLDEN_FRAGMENT_MAP:
kimi-code → '.kimi-code'
- tests/fixtures/golden-install-parity/*.json: regenerated via npm run gen:golden
(the runtime-aliases.manifest.json hash changed; all 17 runtime fixtures updated)
The capability-registry is already regenerated from the prior commit.
* test(#2454): update drift-guard tests for kimi-code runtime registration
Multiple drift guards pin runtime enumeration counts and option numbering.
Each update is mechanical, driven by the guard's named failure mode:
- tests/runtime-flags.test.cjs: EXPECTED_FLAGS gains isKimiCode (16 → 17);
'all 16 flags' → 'all 17 flags' in test names + messages.
- tests/multi-runtime-select.test.cjs: parseRuntimeInput option renumbering
cascade — kilo moves 11→12, opencode 12→13, pi 13→14, qwen 14→15,
trae 15→16, windsurf 16→17, zcode 17→18, All 18→19. New single-choice
test for kimi-code (option 11). Prompt test updated for new numbering.
- tests/host-integration-descriptors.test.cjs: EXPECTED_PROFILES gains
kimi-code → 'programmatic-cli' (terminal CLI per Kimi Code docs);
EXPECTED_FLATTEN gains kimi-code → false (backgroundDispatch:true per
docs, same as Python kimi/opencode).
- tests/global-config-home-fragment.test.cjs: table-count test renamed
13 → 14 table runtimes (kimi-code added to GOLDEN_FRAGMENT_MAP earlier).
* fix(#2454): empty artifactLayout for kimi-code (PR 1 scope)
The skills kind requires a converter (existing converters are per-runtime
like convertClaudeCommandToKimiSkill). PR 1 of this multi-PR sequence only
registers the descriptor; the actual Agent Skills converter (and a new
'convertClaudeCommandToKimiCodeSkill' function) lands in PR 2 alongside
the install-time decision logic. Empty artifactLayout.global is valid and
means 'nothing to install yet via the layout seam'.
Also: added kimi-code to RUNTIME_META in tests/helpers/install-shared.cjs
(localDir .kimi-code, globalSuffix .kimi-code), and added Kimi Code as
option 11 in install.js's buildRuntimePromptText (renumbered downstream
options 11..17 → 12..18, All 18 → 19).
* fix(#2454): camelCase runtimeFlags for hyphenated ids (kimi-code → isKimiCode)
The runtimeFlags generator previously produced 'isKimi-code' (hyphen preserved)
for the new kimi-code runtime id. Property names with hyphens are awkward for
consumers (flags['isKimi-code'] instead of flags.isKimiCode). The new
runtimeIdToFlagName helper folds -[a-z] boundaries to uppercase, producing
the conventional PascalCase flag name. The 16 prior single-word runtime ids
are unaffected (the regex finds no hyphens).
* fix(#2454): update remaining drift-guard tests + gen kimi-code fixtures
- tests/runtime-flags.test.cjs drift guard: use proper kebab-case
conversion (isKimiCode → kimi-code, not 'kimicode') so the registry
comparison doesn't false-positive on hyphenated runtime ids.
- tests/multi-runtime-select.test.cjs: fix kilo/opencode/pi/qwen/trae
single-choice tests for the renumbered options (kilo 11→12, opencode
12→13, pi 13→14, qwen 14→15, trae 15→16).
- tests/install.test.cjs: Kilo integration option 11→12, prompt test
regex updated.
- tests/fixtures/golden-install-parity/kimi-code.json + install-tree/
kimi-code.json: generated via UPDATE_GOLDEN=1 + UPDATE_INSTALL_TREE=1.
The kimi-code install produces the standard GSD install layout (skills,
contexts, references, etc.) — 436 paths, same shape as other runtimes
that have no custom converter yet.
* fix(#2454): add kimi-code install contract + global config home fragment
- src/runtime-name-policy.cts GLOBAL_CONFIG_HOME_FRAGMENTS: add kimi-code
→ '.kimi-code' so getGlobalConfigHomeFragment returns the correct path
instead of falling through to the default '.claude'.
- tests/installer-migration-install.integration.test.cjs
RUNTIME_INSTALL_CONTRACTS: kimi-code entry (same surface as kimi for
PR 1; PR 2 will specialize once the Agent Skills converter lands).
- tests/multi-runtime-select.test.cjs: fix space-separated-choices test
for the renumbered kilo option (11 → 12).
- tests/fixtures/golden-install-parity/kimi-code.json + install-tree/
kimi-code.json: regenerated after rebasing onto current next (new
planner-reversibility.md from #2471 etc. now included).
* test(#2454): skip kimi-code install contract until PR 2 ships install layout
The end-to-end install test (tests/installer-migration-install.integration
.test.cjs) asserts every allRuntimes entry installs a runtime-specific
artifact surface. PR 1 of #2454 registers kimi-code in allRuntimes + the
capability descriptor + flags + labels, but the install LAYOUT (Agent
Skills converter + global AGENTS.md at $KIMI_CODE_HOME/AGENTS.md) lands
in PR 2. The SKIP_INSTALL_CONTRACT set marks this exclusion explicit and
self-removing — PR 2 removes the entry alongside adding the install
surface, restoring the contract loop to full coverage.
* fix(#2454): restore compact model-catalog.json format (M1 review)
Per code-review M1: my prior 'fix(#2454): complete drift-guard registrations'
commit used python json.dump(indent=2) which inflated the file from 165→607
lines (every nested entry got expanded) and lost the trailing newline. The
semantic change was just a 3-line kimi-code entry. Restored the original
hybrid format (top-level indent=2 + inner entries' one-line style) and
added kimi-code in matching form.
Regenerated golden install parity + install tree fixtures since the
model-catalog.json hash changed.
* fix(#2454): update CONTEXT.md allRuntimes glossary (17 → 18, add kimi-code)
CI lint-tests job failed on the glossary drift guard
(scripts/check-glossary-refs.cjs --check):
✗ CONTEXT.md's allRuntimes enum-count sentence claims 17 values but
bin/install.js's allRuntimes array has 18.
✗ CONTEXT.md's allRuntimes member list has drifted from bin/install.js
(missing from CONTEXT.md's list: kimi-code).
Missed in the prior commits because gsd-test does not run the glossary
check (it's a CI lint-tests-only check). Updating CONTEXT.md's two claims
to 18 values + kimi-code in the member list.
* chore(#2505): regen capability-registry + stamp kimi-code version 1.8.0 (#2511)
* docs(changeset): Phase 1 kimi-code runtime Added (#2511)
* test(#2511): regen kimi-code golden parity fixture after Phase 0 guard normalization lands
* docs(changeset): backfill PR #2519 for Phase 1 (#2511)
Phase 2 of golden-parity redesign (epic #2264). Adds an install file-set snapshot (golden-install-tree) and a ci-test-scope rule selecting golden-parity whenever any installed-source path changes, closing the silent-staleness hole behind the #2266 red. ADR-2264 amended (the copy/transform split premise was unsound). Closes#2267.
Net-new EoS/pi installable runtime — purely additive (no prior runtime==='pi'
branches). pi is a bun-runtime programmatic-CLI whose /gsd command is registered
by a native ExtensionAPI extension and dispatches through the embedded engine.
Stage 1 (install plumbing):
- capabilities/pi/capability.json: full hostIntegration descriptor (imperative /
slash-programmatic / active-model / native-extension / bun) + hostBehaviors
{nativePlugin, pluginOnlyInstall}.
- --pi flag + interactive-menu renumber (All 17->18); pi added to RUNTIME_FLAG_IDS,
RUNTIME_LABELS, RUNTIME_META, allRuntimes/runtimeMap, model-catalog defaults.
- Install mirrors OpenCode: pi installs the gsd.cjs extension + the shared engine
payload (gsd-core + scripts + config markers) + the shared hooks bundle (spawned
by the extension at lifecycle events, like OpenCode's plugin). pluginOnlyInstall
EXCLUDES declarative command/agent/skill markdown, which pi has no host-read
surface for (its /gsd is programmatic). _installNativePluginIfDeclared (extracted
from the opencode-family path) copies pi/gsd.cjs -> ~/.pi/agent/extensions/gsd.cjs
(global) / .pi/extensions/ (local). pi added to package.json files.
- Golden: new pi.json (320 files: extension + engine + 27-file hooks bundle, no
markdown); the 16 other fixtures + claude-local change only by the shared
model-catalog hash line.
Stage 2 (real dispatch + upgrades):
- Shared dispatchGsdCommand() (shell-command-projection): bounded, no-throw
subprocess-shim to gsd-tools.cjs (the only full-surface dispatch path; no
in-process full-hub factory exists). Fixes pi/gsd.cjs's createHub()-no-args bug
(every dispatch was UnknownCommand) AND the identical bug in mcp-server.cts's
gsd_invoke_command, which a vacuous unknown-family-only test had masked (now has
a real dispatch regression test).
- pi/gsd.cjs: /gsd handler now (args, ctx) - tokenizes (quote-aware, via the
shipped hooks/lib/git-cmd.js) + dispatches real family/subcommand (not hardcoded
query/help); gsd_invoke gets a TypeBox (JSON-schema-fallback) parameters schema +
consumes params; getArgumentCompletions; before_provider_request active-model
steering (fail-open on null resolution); functional session_start /
before_agent_start / session_before_compact hook bridges (spawn the shipped GSD
hook scripts).
- EXTENSION_EVENT_SURFACES.pi expanded from ['tool_call'] to the full 30-event
vocabulary.
Docs (host-integration matrix + how-to) + changeset (Added).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drive Codex install/uninstall through the descriptor-driven Host-Integration
Interface (declarative embedding adapter → engine surface dispatch) and fold
every positive `runtime === 'codex'` / `isCodex` projection into descriptor-driven
`runtime.hostBehaviors`. Install/uninstall output stays byte-parity-gated
(tests/fixtures/golden-install-parity/codex.json); no other runtime changes.
Three Context7-verified upgrades, each with a test on the user-reachable surface:
- Skill root → canonical $HOME/.agents/skills via a skills-kind `home` override,
with pre-move migration cleanup (stale ~/.codex/skills/gsd-* removed on install
and uninstall; user content preserved). Fixes getGlobalSkillsBase, writeManifest,
and the skill-manifest inventory to honor the override so --skills-root /
sync-skills / the manifest report the real location.
- Six new hooks.json lifecycle events (PreToolUse, PermissionRequest, PreCompact,
PostCompact, SubagentStop, UserPromptSubmit) shared by install + uninstall;
extendedHookEvents reconciled [] -> the schema-valid wired subset.
- Explicit `[agents] max_depth = 1` in the managed config.toml block, pinning the
negotiated dispatch.maxDepth:1 axis. validateCodexConfigSchema now permits a
known-scalar-only bare `[agents]` AgentsToml table (still rejects [[agents]] and
unknown-key break-forms, #2760); mergeCodexConfig preserves the user's own
AgentsToml scalars (max_threads etc.) instead of dropping them.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add ZCode as a first-party runtime via a declarative capability descriptor
(capabilities/zcode/capability.json) with zero hardcoded runtime === 'zcode'
branches — exercising the de-hardcoded, data-driven runtime path that 1.7.0
(ADR-1016 / ADR-1239) enables.
Descriptor (all axes sourced verbatim from zcode.z.ai docs):
- configHome ~/.zcode; nested skills + flat commands/agents; profile-marker install
- Claude-shaped skill format reuses convertClaudeCommandToClaudeSkill (no new converter)
- hostIntegration: declarative / slash-file / mcp / electron; dispatch background=false
(foreground-only per docs); nested+maxDepth undocumented; passive model mode
Installer registration (data, not branches): --zcode flag, allRuntimes, runtimeMap,
interactive menu, --all list. getGlobalConfigDir + resolveRuntimeArtifactLayout +
ALLOWED_CONFIG_RUNTIMES + resolveInstallPlan all derive from the descriptor.
Revamped the brittle per-runtime golden-master tests to be count-agnostic,
descriptor-derived property tests (1.7.0 makes runtimes pluggable data, so pinning
frozen '15 runtime' snapshots is the wrong invariant): getdirname, label-policy,
config-adapter-registry (intent + install-plan golden master), capability-registry,
host-integration-descriptors (counts derive from curated maps). Adding a runtime
descriptor now extends coverage with zero edits to those suites.
Welcome banner, --zcode help, supported-runtimes how-to, and the host-integration
capability matrix (every axis cited) updated.
* feat(#1928): remove sunset gemini cli runtime, redirect to antigravity
Google sunset Gemini CLI on 2026-06-18; Antigravity CLI is its official successor (already a first-class GSD runtime). Remove the gemini runtime from the enum (16->15), aliases, labels, config-home fragment, install path, converters (convertClaudeToGemini{Markdown,Toml,Agent}, convertSlashCommandsToGeminiMentions), capability descriptor, gemini-extension.json, RULESET.GEMINI.*, and the interactive menu (renumbered, no gap).
--gemini now prints an explicit deprecation notice citing the 2026-06-18 sunset and redirects to --antigravity (no silent alias, per the issue's Hyrum's-Law rejection). Antigravity is preserved throughout: its GEMINI.md contextFileName, .gemini/antigravity config home, the shared convertGeminiToolName/claudeToGeminiTools tool vocabulary, and the 'gemini' hookEvents dialect it declares. GEMINI.md retargeted as Antigravity's context file.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(#1928): backfill changeset PR number (#1996)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(#1928): drop Gemini CLI from issue templates (review nit)
Removes the sunset Gemini CLI runtime from the two GitHub issue-template
runtime lists that the removal PR missed, per @davesienkowski's review nit:
- feature_request.yml: 'Applicable runtimes' checkbox (a user could otherwise
request a feature for a runtime GSD no longer supports)
- bug_report.yml: 'Runtime' dropdown + the stale ~/.gemini/settings.json
retrieval-help line
Leaves the post-removal templates fully consistent with the Antigravity redirect.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(#997): ensure canonical ~/.claude/gsd-core path for plugin installs via SessionStart hook
Claude Code marketplace plugin installs unpack the package into the
version-pinned plugin cache and never run bin/install.js, so
~/.claude/gsd-core/ is never created. Agents, commands, and templates
markdown-@-include the canonical ~/.claude/gsd-core/... path (which
expands ~ but NOT ${CLAUDE_PLUGIN_ROOT}), so every include resolved to
nothing and agents (e.g. the executor) failed.
Add a SessionStart hook (hooks/gsd-ensure-canonical-path.js) that, on a
plugin install, symlinks the canonical path's immutable subdirs (bin,
contexts, references, templates, workflows) to the plugin's bundled
gsd-core/ tree. It changes zero @-references, is a no-op in classic
installs, preserves user-generated files (USER-PROFILE.md, STATE.md),
prunes stale links so it self-heals after `claude plugin update`, uses
Windows junctions, and rejects bundled/canonical paths that escape the
resolved plugin root (no traversal, no clobber).
Registered in HOOKS_TO_COPY (build-hooks), MANAGED_HOOKS, hooks.json
SessionStart (runs first, timeout 5), and BUNDLED_GSD_HOOK_FILES.
Behavioral regression tests folded into issue-766-plugin-manifest.test.cjs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(#997): backfill changeset PR number to #1207
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fresh windsurf/devin-desktop workspace installs write skills under .devin/ (legacy .windsurf/ recognized); global ~/.codeium/windsurf/ unchanged. Also threads real isGlobal through _applyRuntimeRewrites so global skill content references the codeium path. Closes#1085.
* feat(#770): register Claude Code lifecycle hooks (SubagentStop/Stop/PreCompact/FileChanged)
Wire three new context-tracking events (SubagentStop, Stop, PreCompact) to
gsd-context-monitor so context-headroom warnings surface at model-stop and
subagent-finalisation moments — not just on PostToolUse. Add a new
FileChanged hook (gsd-config-reload.js) that hot-reloads .planning/config.json
context mid-session when the user edits it, injecting a config summary as
hookSpecificOutput.additionalContext. Updates plugin manifest hooks.json,
managed-hooks-registry, installer-migration-report allowlist, and
shell-command-projection cleanup tables. Tests: 21 new assertions in
enh-770-claude-hook-events.test.cjs; enh-788 and issue-766 test suites updated.
Closes#770
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#770): document newly-registered Claude Code lifecycle hooks
Add a Hook coverage table to the Claude Code npm installer section of
docs/how-to/install-on-your-runtime.md describing SubagentStop, Stop,
PreCompact, and the new FileChanged (gsd-config-reload.js) hook that
hot-reloads .planning/config.json mid-session. Also fixes the changeset
frontmatter (adds type: Added + pr: 821) so docs-lint can consume the
fragment.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#770): add gsd-config-reload.js to INVENTORY.md and regenerate manifest
The feat commit added hooks/gsd-config-reload.js but did not bump the
Hooks count in docs/INVENTORY.md (14→15) or add the new row, and did not
regenerate docs/INVENTORY-MANIFEST.json. Both inventory-counts and
inventory-manifest-sync tests failed across the full CI matrix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#770): make lifecycle-hook tests deterministic on scoped runner
Replace the shared hooks/dist/ ensemble setup (ensureHooksDist /
teardownHooksDist) in the Claude hook tests with per-test isolation:
pre-populate each test's own tmpDir/.claude/hooks/ with stub files and
pass installerMigrations:[] to install() so the first-time-baseline
migration does not remove the stubs before the copy step can run.
Root cause: hooks/dist/ is gitignored and absent on a fresh npm ci.
ensureHooksDist() created it and teardownHooksDist() deleted it, but
with --test-concurrency=4 both test files ran concurrently as separate
Node.js worker processes sharing the same filesystem. One file's
afterEach teardown deleted hooks/dist/ while the other file's install()
was copying from it, producing an ENOENT (reproduced 2/10 runs locally).
The additional issue: even with pre-placed stubs surviving the copy race,
the 000-first-time-baseline migration classified hooks/gsd-*.js as
bundled-gsd-hook artifacts, auto-removed them, and the copy step never
re-ran (hooks/dist/ absent) — leaving contextMonitorFile missing and all
hook registrations silently skipped (the 'got: []' symptom).
Fix: pre-populate targetDir/hooks/ per-test (isolated temp dir) AND pass
installerMigrations:[] so the baseline scan is skipped. The Qwen suites
already used this pattern correctly; the Claude suites are aligned to it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#770): ship gsd-config-reload.js by adding it to build-hooks HOOKS_TO_COPY
The #770 feature added hooks/gsd-config-reload.js and registered it in
MANAGED_HOOKS, the installer, INVENTORY, and the test EXPECTED_ALL_HOOKS
list — but never added it to scripts/build-hooks.js HOOKS_TO_COPY. As a
result the hook was never copied into hooks/dist/ during the build, so:
- the hook would never ship to users (real production bug — the
FileChanged config-reload feature was dead-on-arrival), and
- install-minimal-hooks.test.cjs #1755 ("all expected hooks are copied
from hooks/dist/ to target", ".js hooks are executable after copy",
"manifest contains .js hook entries") failed on any environment with
a clean checkout (no pre-existing hooks/dist/): coverage, full test
macos-22/macos-24, test ubuntu-24.
The failures were masked locally only by a stale hooks/dist/ left from a
prior build (build-hooks copies into dist without clearing it). On CI's
fresh `npm ci` there is no dist, so the omission surfaced.
Fix: add 'gsd-config-reload.js' to HOOKS_TO_COPY so build-hooks stages it
into hooks/dist/ alongside the other JS hooks. Verified by removing
hooks/dist/ and rerunning the full suite green (0 fail).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#770): make config prototype-pollution beforeEach deterministic on scoped runner
Root cause: the #663 and alert-#26 prototype-pollution describe blocks
seeded .planning/config.json in beforeEach via a bare
runGsdTools('config-ensure-section') whose result was discarded. That
command runs in a spawned gsd-tools child; on the scoped CI lane
(--test-concurrency=4, config.test.cjs scheduled alongside the heavy
install/tarball suites that #770 pulled into the targeted set) the child
can be transiently killed under resource pressure (non-zero exit, empty
stderr — an OS-level kill, not an app error). The swallowed failure left
config.json absent, so the first subtest's readConfig() threw ENOENT
opening <tmp>/.planning/config.json. Only 1 of 4 subtests failed,
confirming a per-invocation transient, not a deterministic miss; the full
suite schedules files differently so config.test.cjs did not collide with
those heavy neighbors → passed there.
Fix: add ensureConfigReady(tmpDir) which retries config-ensure-section on
ANY failure or missing file and throws a clear diagnostic if it still
cannot create config.json, then use it in both prototype-pollution
beforeEach blocks. Setup is now deterministic under load; the #663/alert-#26
security assertions are unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
- Resolve Kimi global skills under the generic agents path
- Add empty Kimi layout placeholder and local install no-op guard
- Keep selection/path tests aligned without Kimi skill conversion
* chore(tests): lint rule — cap test files per production module at 2
Adds scripts/lint-test-file-count.cjs with a ratcheted allowlist
(scripts/lint-test-file-count.allowlist.json) capturing today's
30 violating clusters as a ceiling. New entries blocked at PR time;
reductions ratchet automatically.
Wires into .github/workflows/test.yml as a new step in lint-tests.
Refs #3737
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(tests): add docs-exempt to changeset fragment
Internal CI lint rule — no user-facing docs impact.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(tests): consolidate Installer Module — 11 files → 2
Closes#3758
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(changeset): replace invalid type 'Chore' with 'Changed' — ALLOWED_TYPES only accepts Added|Changed|Deprecated|Removed|Fixed|Security
Fragment already had a docs-exempt marker; only the type value was wrong.
Refs #3758
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(tests): split install.test.cjs along section seams — keep files ≤ 800 LOC
1828-LOC monolith split into 3 files by semantic boundary:
install.test.cjs (602 LOC) — S1-5: dir resolution, install/uninstall spot-checks, Kilo
install-runtime-artifacts.test.cjs (347 LOC) — S6-8+12: layout loop, Contract 6, legacy migrations
install-minimal-hooks.test.cjs (782 LOC) — S9-11+13: profiles, minimal E2E, hooks copy
Shared constants and helpers extracted to tests/helpers/install-shared.cjs (216 LOC).
Total test count unchanged: 210 tests (68+37+105).
Refs #3758
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(tests): ratchet state test-file ceiling to 9 — actual count at baseline
The allowlist was initialised with state=8 but the repo already had 9 files
matching the 'state' prefix at the time the lint rule was created, causing
lint-test-file-count.test.cjs to fail on the real repo immediately.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>