You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Scaffold the mod in the plugin with its dev and test loop #202
Scaffold the mod inside plugins/trailhead and the dev loop around it, with no user-visible behaviour yet:
the module wired per the packaging finding (hooks/hooks.jsonmodules or the shape research settles), a register that loads and does nothing visible, typed against claude-code, its $.state contract in types/index.d.ts;
claude plugin validate plugins/trailhead clean, a first *.test.ts run by claude plugin test, wired into the repo's test command;
a documented local loop (claude --plugin-dir plugins/trailhead or CLAUDE_CODE_PLUGIN_DIRS) to try changes with hot reload;
the installer and Codex projection handle the module as research found (shipped for Claude, skipped for Codex), with their tests updated.
Done when a session loads the plugin with the module and the command hooks both active, and Codex installs unchanged.
Target branch: feat/mod (the map's integration branch, created from main). Tested build: Claude Code 2.1.296.
Steps
Contract + RED test. Add plugins/trailhead/types/index.d.ts: export type WorkState = { map: number | null; ticket: number | null; title: string | null; step: string | null; auto: boolean; session: string | null; updatedAt: string | null } (the Where does the mod read the work state from? #200 shape) and declare module 'claude-code' { interface PluginState { trailhead: { workState: WorkState | null } } }. Name it in plugin.json as "types": "./types/index.d.ts". Add plugins/trailhead/tests/mod.test.ts (kit claude-code/testing): the plugin loads with no skipped hook, trailhead.workState reads null by default, and a session.start pass leaves no status/toast. Run claude plugin test plugins/trailhead: RED (no module yet).
Module (GREEN).plugins/trailhead/hooks/mod/register.ts: export const register: Register = on => { ... } typed against claude-code, one pass-through hook (session.start -> next(e)) and the workState atom declared against the contract; nothing drawn, no status, no toast. Add "modules": ["./mod/register.ts"] to plugins/trailhead/hooks/hooks.json next to the unchanged hooks. claude plugin validate plugins/trailhead clean; claude plugin test GREEN. Type-check with tsc against the engine's laid types (a plugins/trailhead/tsconfig.json extending .claude-plugin/types/tsconfig.json, only if the engine does not supply one); gitignore plugins/trailhead/.claude-plugin/types/ (engine-generated at every --plugin-dir load).
Repo test command. The repo has none today (each *.test.js runs standalone). Add scripts/test.js (outside the npm files) + "scripts": { "test": "node scripts/test.js" } in package.json: runs every *.test.js under bin/ and plugins/trailhead/hooks/ (skipping .claude/worktrees), then claude plugin validate plugins/trailhead and claude plugin test plugins/trailhead; when claude is not on PATH the two mod checks are reported skipped, not failed. Exit non-zero on any failure.
Installer + Codex pinned by tests (no installer code change). Research Can the core plugin ship a mod alongside its command hooks? #199: neither channel reads the plugin's hooks.json; hook scripts are copied by allowlist. Add assertions: in bin/trailhead.test.js, a --codex install contains no register.ts anywhere under the codex dir and its hooks.json has no modules key; a --claude npm install copies no hooks/mod/ and writes no modules into settings.json (npm delivery of the mod is map fog item 14, out of this ticket). A source-side check that plugins/trailhead/hooks/hooks.json declares exactly one module whose file exists.
Dev loop doc.plugins/trailhead/hooks/mod/README.md (contributor note, not user docs): claude --plugin-dir plugins/trailhead (or CLAUDE_CODE_PLUGIN_DIRS) with hot reload on save, claude plugin validate, claude plugin test, npm test, claude --debug for the hooks module trailhead@inline loaded line, the 2.1.287 minimum and that command hooks stay the floor. User-facing READMEs/docs are fog item 13.
Done check. Headless claude -p --plugin-dir plugins/trailhead --debug-file <tmp> shows both hooks module trailhead@inline loaded and a trailhead command hook (trailhead-check-update.js) finishing; node bin/trailhead.js --codex --dir=<tmp> output unchanged vs main (same file set).
Seams (TDD: seams)
Module load + $.state contract: tests/mod.test.ts via claude plugin test.
Target branch: feat/mod (the map's integration branch, created from main). Tested build: Claude Code 2.1.296.
Steps
Contract + RED test. Add plugins/trailhead/types/index.d.ts: export type WorkState = { map: number | null; ticket: number | null; title: string | null; step: string | null; auto: boolean; session: string | null; updatedAt: string | null } (the Where does the mod read the work state from? #200 shape) and declare module 'claude-code' { interface PluginState { trailhead: { workState: WorkState | null } } }. Name it in plugin.json as "types": "./types/index.d.ts". Add plugins/trailhead/tests/mod.test.ts (kit claude-code/testing): the plugin loads with no skipped hook, (the state default is checked by validate, see Seams). Run claude plugin test plugins/trailhead: RED (no module yet).
Module (GREEN).plugins/trailhead/hooks/mod/register.ts: export const register: Register = on => { ... } typed against claude-code, one pass-through hook (session.start -> next(e)) and the workState atom declared against the contract; nothing drawn, no status, no toast. Add "modules": ["./mod/register.ts"] to plugins/trailhead/hooks/hooks.json next to the unchanged hooks. claude plugin validate plugins/trailhead clean; claude plugin test GREEN. The types manifest field and the PluginState augmentation are confirmed on 2.1.296 by a scratch spike: claude plugin validate reports types ./types/index.d.ts declares state: trailhead.workState and state reads: trailhead.workState. Typing gate = import type { Register } from 'claude-code' + validate holding every $.state key to the contract. A full tsc pass is not a test gate: the repo has zero npm dependencies and the engine lays .claude-plugin/types/ only in an interactive --plugin-dir session (a headless -p run lays nothing, verified); the dev-loop note documents it as an optional step (npx -p typescript@5.9.3 tsc -p plugins/trailhead after an interactive load, using the engine-supplied tsconfig). Gitignore plugins/trailhead/.claude-plugin/types/ and any engine-written plugins/trailhead/tsconfig.json. The pass-through session.start hook is kept on purpose: it is the observable proof of load (debug log events: session.start, and the test's bottom hook is reached through it); it changes nothing.
Repo test command. The repo has none today (each *.test.js runs standalone). Add scripts/test.js (outside the npm files) + "scripts": { "test": "node scripts/test.js" } in package.json: runs every *.test.js under bin/ and plugins/trailhead/hooks/ (skipping .claude/worktrees), then claude plugin validate plugins/trailhead and claude plugin test plugins/trailhead; when claude is not on PATH the run fails unless the explicit opt-out TRAILHEAD_SKIP_MOD_CHECKS=1 is set (then reported skipped). Exit non-zero on any failure. (CI runs no plugin tests today, per the codebase map; adding a CI job is out of this ticket.)
Installer + Codex pinned by tests (no installer code change). Research Can the core plugin ship a mod alongside its command hooks? #199: neither channel reads the plugin's hooks.json; hook scripts are copied by allowlist. Add assertions: in bin/trailhead.test.js, a --codex install contains no register.ts anywhere under the codex dir and its hooks.json has no modules key; a --claude npm install copies no hooks/mod/ and writes no modules into settings.json (npm delivery of the mod is map fog item 14, out of this ticket). A source-side check that plugins/trailhead/hooks/hooks.json declares exactly one module whose file exists.
Dev loop doc.plugins/trailhead/hooks/mod/README.md (contributor note, not user docs): claude --plugin-dir plugins/trailhead (or CLAUDE_CODE_PLUGIN_DIRS) with hot reload on save, claude plugin validate, claude plugin test, npm test, claude --debug for the hooks module trailhead@inline loaded line, the 2.1.287 minimum and that command hooks stay the floor. User-facing READMEs/docs are fog item 13.
Done check. Headless claude -p --plugin-dir plugins/trailhead --debug-file <tmp> shows both hooks module trailhead@inline loaded and a trailhead command hook (trailhead-check-update.js) finishing; node bin/trailhead.js --codex --dir=<tmp> tree identical to main's: baseline from a temporary git worktree of main installed into its own temp dir, compared on relative path, file type (file/symlink), mode bits and content hash.
Seams (TDD: seams)
Module load: tests/mod.test.ts via claude plugin test (the test $ has no state noun, verified, so the test raises session.start through $ and asserts the bottom hook receives the input unchanged and the result is echoed; verified in the spike that a module that fails to parse/load fails claude plugin test with hooks module did not load, so the test is a real load gate; the $.state contract is held by validate).
Reviewer: codex (codex exec -s read-only), 2 rounds. Final round: no blocking concerns.
Concern
Round 1 tag
Resolution
npm test passes silently when claude is absent
blocking
The run fails without claude unless TRAILHEAD_SKIP_MOD_CHECKS=1 is set explicitly. A CI job is out of this ticket (CI runs no plugin tests today).
tsc check not reproducible
blocking
Dropped as a gate: zero npm deps, and the engine lays .claude-plugin/types/ only in an interactive --plugin-dir session (headless lays nothing, verified). Typing gate = Register import + claude plugin validate holding $.state keys to the contract; tsc documented as an optional dev step.
types field / PluginState augmentation unconfirmed
blocking
Confirmed on 2.1.296 by a scratch spike (validate reports the declared state and the module's reads).
Pass-through session.start hook unnecessary
non-blocking
Kept on purpose: it is the observable load proof (debug log events: session.start; the test reaches its bottom hook through it). A module that fails to load fails claude plugin test (verified).
Codex file-set comparison weak
non-blocking
Compare path, file type, mode bits and content hash against a baseline installed from a temporary worktree of main.
validate criterion vs evidence
non-blocking (round 2)
Criterion aligned to the lines validate actually prints.
Tests / plan criteria.npm test: 22/22 (20 *.test.js suites, claude plugin validate, claude plugin test); installer suite 709 assertions. claude plugin validate plugins/trailhead reports ./mod/register.ts hooks: session.start and types ./types/index.d.ts declares state: trailhead.workState. Headless claude -p --plugin-dir plugins/trailhead (2.1.296) logs hooks module trailhead@inline loaded (...); events: session.start and trailhead-check-update.js (plugin trailhead@inline) finished with status 0 in the same session. Codex install from this branch vs a temporary worktree of main: 120 files each, identical on path, type, mode and sha256 (only hooks.json embeds the absolute install dir; identical once normalised), no modules key, no .ts.
W1 fixed (8dbd2af): the mod test cannot tell a passthrough hook from no hook; renamed to what it proves (the module loads, session.start unchanged). A module that fails to load fails it (verified).
W2 fixed (21044bb): npm test now fails when a test root yields no suites.
I1 fixed (78b5b61): dropped the vacuous "no modules in settings.json" assertion.
I2 fixed (78b5b61): pinned that hooks.json keeps its command hooks (PreToolUse, PostToolUse, SessionStart) beside the module.
I3 accepted: hooks/mod/, tests/, types/ ship in the npm tarball; harmless (installer copies by allowlist, verified) and needed for the later npm delivery (map fog 14).
I4 accepted: no CI runs npm test; CI was out of this ticket (CI runs no plugin tests today); belongs with the release work (map fog 13).
Goal-backward verification: status: met.
Module wired via hooks/hooks.jsonmodules: ["./mod/register.ts"], command hooks unchanged: met (22b1aae).
register loads, nothing visible (one session.start passthrough, no status/band/toast): met (22b1aae).
validate clean, first *.test.ts under claude plugin test, wired into npm test (fails without claude unless TRAILHEAD_SKIP_MOD_CHECKS=1): met (8f90437, 935f859, 21044bb).
Documented local loop with hot reload: met (96d3626, plugins/trailhead/hooks/mod/README.md).
Installer and Codex projection: no code change, pinned by tests: met (bd71a75, 78b5b61).
Done: a session loads module + command hooks together; Codex installs unchanged: met (evidence above).
Acceptance testing: not applicable (no user-facing change).
npm test (scripts/test.js, new): every *.test.js suite, then claude plugin validate and claude plugin test; fails without claude unless TRAILHEAD_SKIP_MOD_CHECKS=1, and fails when a test root yields no suites (935f859, 21044bb).
Installer and Codex projection unchanged, pinned by tests: no .ts/modules in a Codex install, no hooks/mod/ in a Claude npm install, command hooks kept beside the module (bd71a75, 78b5b61).
Dev loop documented in plugins/trailhead/hooks/mod/README.md (--plugin-dir hot reload, validate, test, debug log, optional tsc) (96d3626).
Verified on Claude Code 2.1.296: one headless session loads the module and runs the command hooks; Codex install identical to main. Review ledger and goal check: VERIFY.
Activity
ToRvaLDz commentedon Oct 10, 2026
PLAN
Target branch:
feat/mod(the map's integration branch, created frommain). Tested build: Claude Code 2.1.296.Steps
plugins/trailhead/types/index.d.ts:export type WorkState = { map: number | null; ticket: number | null; title: string | null; step: string | null; auto: boolean; session: string | null; updatedAt: string | null }(the Where does the mod read the work state from? #200 shape) anddeclare module 'claude-code' { interface PluginState { trailhead: { workState: WorkState | null } } }. Name it inplugin.jsonas"types": "./types/index.d.ts". Addplugins/trailhead/tests/mod.test.ts(kitclaude-code/testing): the plugin loads with no skipped hook,trailhead.workStatereadsnullby default, and asession.startpass leaves no status/toast. Runclaude plugin test plugins/trailhead: RED (no module yet).plugins/trailhead/hooks/mod/register.ts:export const register: Register = on => { ... }typed againstclaude-code, one pass-through hook (session.start->next(e)) and theworkStateatom declared against the contract; nothing drawn, no status, no toast. Add"modules": ["./mod/register.ts"]toplugins/trailhead/hooks/hooks.jsonnext to the unchangedhooks.claude plugin validate plugins/trailheadclean;claude plugin testGREEN. Type-check withtscagainst the engine's laid types (aplugins/trailhead/tsconfig.jsonextending.claude-plugin/types/tsconfig.json, only if the engine does not supply one); gitignoreplugins/trailhead/.claude-plugin/types/(engine-generated at every--plugin-dirload).*.test.jsruns standalone). Addscripts/test.js(outside the npmfiles) +"scripts": { "test": "node scripts/test.js" }inpackage.json: runs every*.test.jsunderbin/andplugins/trailhead/hooks/(skipping.claude/worktrees), thenclaude plugin validate plugins/trailheadandclaude plugin test plugins/trailhead; whenclaudeis not on PATH the two mod checks are reported skipped, not failed. Exit non-zero on any failure.hooks.json; hook scripts are copied by allowlist. Add assertions: inbin/trailhead.test.js, a--codexinstall contains noregister.tsanywhere under the codex dir and itshooks.jsonhas nomoduleskey; a--claudenpm install copies nohooks/mod/and writes nomodulesintosettings.json(npm delivery of the mod is map fog item 14, out of this ticket). A source-side check thatplugins/trailhead/hooks/hooks.jsondeclares exactly one module whose file exists.plugins/trailhead/hooks/mod/README.md(contributor note, not user docs):claude --plugin-dir plugins/trailhead(orCLAUDE_CODE_PLUGIN_DIRS) with hot reload on save,claude plugin validate,claude plugin test,npm test,claude --debugfor thehooks module trailhead@inline loadedline, the 2.1.287 minimum and that command hooks stay the floor. User-facing READMEs/docs are fog item 13.claude -p --plugin-dir plugins/trailhead --debug-file <tmp>shows bothhooks module trailhead@inline loadedand a trailhead command hook (trailhead-check-update.js) finishing;node bin/trailhead.js --codex --dir=<tmp>output unchanged vsmain(same file set).Seams (TDD:
seams)$.statecontract:tests/mod.test.tsviaclaude plugin test.bin/trailhead.test.jsassertions.Files touched
plugins/trailhead/hooks/hooks.json,plugins/trailhead/hooks/mod/register.ts,plugins/trailhead/hooks/mod/README.md,plugins/trailhead/types/index.d.ts,plugins/trailhead/tests/mod.test.ts,plugins/trailhead/.claude-plugin/plugin.json,package.json,scripts/test.js,bin/trailhead.test.js,.gitignore.Verification criteria
claude plugin validate plugins/trailheadpasses and lists the module's hooks.claude plugin test plugins/trailheadpasses;npm testpasses end to end.main; nomodules, no.tsin the Codex tree.ToRvaLDz commentedon Oct 10, 2026
PLAN (revised after cross-AI review)
Target branch:
feat/mod(the map's integration branch, created frommain). Tested build: Claude Code 2.1.296.Steps
plugins/trailhead/types/index.d.ts:export type WorkState = { map: number | null; ticket: number | null; title: string | null; step: string | null; auto: boolean; session: string | null; updatedAt: string | null }(the Where does the mod read the work state from? #200 shape) anddeclare module 'claude-code' { interface PluginState { trailhead: { workState: WorkState | null } } }. Name it inplugin.jsonas"types": "./types/index.d.ts". Addplugins/trailhead/tests/mod.test.ts(kitclaude-code/testing): the plugin loads with no skipped hook, (the state default is checked byvalidate, see Seams). Runclaude plugin test plugins/trailhead: RED (no module yet).plugins/trailhead/hooks/mod/register.ts:export const register: Register = on => { ... }typed againstclaude-code, one pass-through hook (session.start->next(e)) and theworkStateatom declared against the contract; nothing drawn, no status, no toast. Add"modules": ["./mod/register.ts"]toplugins/trailhead/hooks/hooks.jsonnext to the unchangedhooks.claude plugin validate plugins/trailheadclean;claude plugin testGREEN. Thetypesmanifest field and thePluginStateaugmentation are confirmed on 2.1.296 by a scratch spike:claude plugin validatereportstypes ./types/index.d.ts declares state: trailhead.workStateandstate reads: trailhead.workState. Typing gate =import type { Register } from 'claude-code'+validateholding every$.statekey to the contract. A fulltscpass is not a test gate: the repo has zero npm dependencies and the engine lays.claude-plugin/types/only in an interactive--plugin-dirsession (a headless-prun lays nothing, verified); the dev-loop note documents it as an optional step (npx -p typescript@5.9.3 tsc -p plugins/trailheadafter an interactive load, using the engine-supplied tsconfig). Gitignoreplugins/trailhead/.claude-plugin/types/and any engine-writtenplugins/trailhead/tsconfig.json. The pass-throughsession.starthook is kept on purpose: it is the observable proof of load (debug logevents: session.start, and the test's bottom hook is reached through it); it changes nothing.*.test.jsruns standalone). Addscripts/test.js(outside the npmfiles) +"scripts": { "test": "node scripts/test.js" }inpackage.json: runs every*.test.jsunderbin/andplugins/trailhead/hooks/(skipping.claude/worktrees), thenclaude plugin validate plugins/trailheadandclaude plugin test plugins/trailhead; whenclaudeis not on PATH the run fails unless the explicit opt-outTRAILHEAD_SKIP_MOD_CHECKS=1is set (then reported skipped). Exit non-zero on any failure. (CI runs no plugin tests today, per the codebase map; adding a CI job is out of this ticket.)hooks.json; hook scripts are copied by allowlist. Add assertions: inbin/trailhead.test.js, a--codexinstall contains noregister.tsanywhere under the codex dir and itshooks.jsonhas nomoduleskey; a--claudenpm install copies nohooks/mod/and writes nomodulesintosettings.json(npm delivery of the mod is map fog item 14, out of this ticket). A source-side check thatplugins/trailhead/hooks/hooks.jsondeclares exactly one module whose file exists.plugins/trailhead/hooks/mod/README.md(contributor note, not user docs):claude --plugin-dir plugins/trailhead(orCLAUDE_CODE_PLUGIN_DIRS) with hot reload on save,claude plugin validate,claude plugin test,npm test,claude --debugfor thehooks module trailhead@inline loadedline, the 2.1.287 minimum and that command hooks stay the floor. User-facing READMEs/docs are fog item 13.claude -p --plugin-dir plugins/trailhead --debug-file <tmp>shows bothhooks module trailhead@inline loadedand a trailhead command hook (trailhead-check-update.js) finishing;node bin/trailhead.js --codex --dir=<tmp>tree identical tomain's: baseline from a temporarygit worktreeofmaininstalled into its own temp dir, compared on relative path, file type (file/symlink), mode bits and content hash.Seams (TDD:
seams)tests/mod.test.tsviaclaude plugin test(the test$has nostatenoun, verified, so the test raisessession.startthrough$and asserts the bottom hook receives the input unchanged and the result is echoed; verified in the spike that a module that fails to parse/load failsclaude plugin testwithhooks module did not load, so the test is a real load gate; the$.statecontract is held byvalidate).bin/trailhead.test.jsassertions.Files touched
plugins/trailhead/hooks/hooks.json,plugins/trailhead/hooks/mod/register.ts,plugins/trailhead/hooks/mod/README.md,plugins/trailhead/types/index.d.ts,plugins/trailhead/tests/mod.test.ts,plugins/trailhead/.claude-plugin/plugin.json,package.json,scripts/test.js,bin/trailhead.test.js,.gitignore.Verification criteria
claude plugin validate plugins/trailheadpasses and reports./mod/register.ts hooks: session.startandtypes ... declares state: trailhead.workState.claude plugin test plugins/trailheadpasses;npm testpasses end to end.main(paths + content hashes); nomodules, no.tsin the Codex tree.ToRvaLDz commentedon Oct 10, 2026
PLAN-REVIEW
Reviewer: codex (
codex exec -s read-only), 2 rounds. Final round: no blocking concerns.npm testpasses silently whenclaudeis absentclaudeunlessTRAILHEAD_SKIP_MOD_CHECKS=1is set explicitly. A CI job is out of this ticket (CI runs no plugin tests today).tsccheck not reproducible.claude-plugin/types/only in an interactive--plugin-dirsession (headless lays nothing, verified). Typing gate =Registerimport +claude plugin validateholding$.statekeys to the contract;tscdocumented as an optional dev step.typesfield /PluginStateaugmentation unconfirmedvalidatereports the declared state and the module's reads).session.starthook unnecessaryevents: session.start; the test reaches its bottom hook through it). A module that fails to load failsclaude plugin test(verified).main.validatecriterion vs evidencevalidateactually prints.ToRvaLDz commentedon Oct 10, 2026
status: clean
critical: 0 · warning: 2 · info: 4
VERIFY
Tests / plan criteria.
npm test: 22/22 (20*.test.jssuites,claude plugin validate,claude plugin test); installer suite 709 assertions.claude plugin validate plugins/trailheadreports./mod/register.ts hooks: session.startandtypes ./types/index.d.ts declares state: trailhead.workState. Headlessclaude -p --plugin-dir plugins/trailhead(2.1.296) logshooks module trailhead@inline loaded (...); events: session.startandtrailhead-check-update.js (plugin trailhead@inline) finished with status 0in the same session. Codex install from this branch vs a temporary worktree ofmain: 120 files each, identical on path, type, mode and sha256 (onlyhooks.jsonembeds the absolute install dir; identical once normalised), nomoduleskey, no.ts.Code review (ticket commits 8f90437, 22b1aae, 935f859, bd71a75, 96d3626, 21044bb, 78b5b61, 8dbd2af).
Ledger:
session.startunchanged). A module that fails to load fails it (verified).npm testnow fails when a test root yields no suites.modulesin settings.json" assertion.hooks.jsonkeeps its command hooks (PreToolUse, PostToolUse, SessionStart) beside the module.hooks/mod/,tests/,types/ship in the npm tarball; harmless (installer copies by allowlist, verified) and needed for the later npm delivery (map fog 14).npm test; CI was out of this ticket (CI runs no plugin tests today); belongs with the release work (map fog 13).Goal-backward verification:
status: met.hooks/hooks.jsonmodules: ["./mod/register.ts"], command hooks unchanged: met (22b1aae).registerloads, nothing visible (onesession.startpassthrough, no status/band/toast): met (22b1aae).claude-code(Register),$.statecontract intypes/index.d.tswith the Where does the mod read the work state from? #200 shape, named inplugin.jsontypes: met (8f90437).validateclean, first*.test.tsunderclaude plugin test, wired intonpm test(fails withoutclaudeunlessTRAILHEAD_SKIP_MOD_CHECKS=1): met (8f90437, 935f859, 21044bb).plugins/trailhead/hooks/mod/README.md).Acceptance testing: not applicable (no user-facing change).
ToRvaLDz commentedon Oct 10, 2026
Resolution. The mod scaffold is in the core plugin on
feat/mod, with no user-visible behaviour.plugins/trailhead/hooks/hooks.jsondeclaresmodules: ["./mod/register.ts"]beside the unchanged command hooks;hooks/mod/register.tsregisters asession.startpassthrough, typed againstclaude-code(8f90437, 22b1aae).$.statecontracttrailhead.workState(the Where does the mod read the work state from? #200 shape) inplugins/trailhead/types/index.d.ts, named inplugin.jsontypes(8f90437).npm test(scripts/test.js, new): every*.test.jssuite, thenclaude plugin validateandclaude plugin test; fails withoutclaudeunlessTRAILHEAD_SKIP_MOD_CHECKS=1, and fails when a test root yields no suites (935f859, 21044bb)..ts/modulesin a Codex install, nohooks/mod/in a Claude npm install, command hooks kept beside the module (bd71a75, 78b5b61).plugins/trailhead/hooks/mod/README.md(--plugin-dirhot reload, validate, test, debug log, optionaltsc) (96d3626).Verified on Claude Code 2.1.296: one headless session loads the module and runs the command hooks; Codex install identical to
main. Review ledger and goal check: VERIFY.