docs: trim generated prose

This commit is contained in:
Tianyi Cui
2026-07-12 03:36:43 +08:00
parent 3dca90261c
commit 75838e10b5
323 changed files with 2857 additions and 11833 deletions
@@ -302,12 +302,8 @@ describe('hooks-claude bridge — SubagentStart / SubagentStop (observe)', () =>
await waitFor(() => existsSync(startMarker) && existsSync(stopMarker))
expect(existsSync(startMarker)).toBe(true)
expect(existsSync(stopMarker)).toBe(true)
// The markers prove the hook PROCESSES ran, not that the detached `.then`
// continuations did (`touch` lands before the process exits). Dispose drains
// them, so the no-context arm of the SubagentStart continuation — covered
// only here — executes before this file's coverage snapshot instead of
// racing it (the arm went uncovered on a loaded CI runner and failed the
// per-file 100% branch gate).
// The markers prove the hook PROCESSES ran, not that the detached `.then` continuations did
// (`touch` lands before the process exits).
await hooks.dispose()
})
@@ -317,10 +313,8 @@ describe('hooks-claude bridge — SubagentStart / SubagentStop (observe)', () =>
const pidFile = join(dir, 'pid')
const marker = join(dir, 'started')
const slowHook = join(dir, 'slow.sh')
// Record the hook shell's PID and touch the marker FIRST so the test can
// tell "the hook is genuinely mid-run", then sleep far past the suite
// timeout. Dispose must KILL the process (the tracker's abort signal), not
// await its exit or its 10-minute default hook timeout.
// Record the hook shell's PID and touch the marker FIRST so the test can tell "the hook is
// genuinely mid-run", then sleep far past the suite timeout.
writeFileSync(slowHook, `#!/usr/bin/env bash\necho $$ > "${pidFile}"\ntouch "${marker}"\nsleep 30\n`)
chmodSync(slowHook, 0o755)
writeFileSync(join(dir, 'hooks.json'), JSON.stringify({ hooks: {
@@ -334,11 +328,9 @@ describe('hooks-claude bridge — SubagentStart / SubagentStop (observe)', () =>
await waitFor(() => existsSync(marker))
const pid = Number(readFileSync(pidFile, 'utf8').trim())
await hooks.dispose()
// Quiescence, not just promptness: the drain resolves only after the run
// settled, and the run settles only after the killed process was reaped —
// so by the time dispose returns, the PID must be GONE (kill(pid, 0)
// throws ESRCH). An untracked fire-and-forget regression would leave the
// process alive (or unreaped) and fail this deterministically.
// Quiescence, not just promptness: the drain resolves only after the run settled, and the
// run settles only after the killed process was reaped — so by the time dispose returns,
// the PID must be GONE (kill(pid, 0) throws ESRCH).
expect(() => process.kill(pid, 0)).toThrow()
// The aborted run resolves as a non-blocking error (runHook never rejects),
// so the drained continuation must NOT have logged a failure.
@@ -367,11 +359,8 @@ describe('hooks-claude bridge — load resilience', () => {
})
it('disposing the bridge fiber removes its listeners (HMR safety)', async () => {
// A BLOCKING UserPromptSubmit hook: if the listener leaked past dispose it
// would veto the prompt (0 model requests) and log a hook/invoked. Build the
// ctx WITHOUT the harness's own bridge mount so this is the ONLY mount, then
// dispose it — a leaked listener fails the test (a no-op `true` hook would
// pass even leaked, so it proved nothing).
// A BLOCKING UserPromptSubmit hook: if the listener leaked past dispose it would veto the
// prompt (0 model requests) and log a hook/invoked.
const dir = writeConfig({ UserPromptSubmit: [{ hooks: [{ type: 'command', command: 'exit 2' }] }] })
const adapter = new MockAdapter([textResponse('ok')])
const ctx = new Context()
@@ -393,10 +382,7 @@ describe('hooks-claude bridge — load resilience', () => {
})
it('has the namespace-plugin export shape (no stray default) so the Loader keeps name/inject/apply', () => {
// Postmortem 0001 guard: this plugin HAS `inject = ['bash']`, so a stray
// `export default apply` would collapse the module via `unwrapExports`
// (`exports.default ?? exports`), DROP `inject`, and crash at load with
// "cannot get property … without inject". Guard the shape directly.
// Loader must retain this namespace's injection metadata.
expect('default' in HooksClaude).toBe(false)
expect(HooksClaude.name).toBe('hooks-claude')
expect(HooksClaude.inject).toEqual(['bash'])
@@ -183,9 +183,9 @@ describe('hooks-claude coverage — Stop continuation + subagent inject/catch',
})
it('a Stop hook that blocks with EMPTY stderr still forces continuation (no reason required)', async () => {
// Regression: a blocking Stop hook (exit 2) with no stderr yields decision
// 'deny' + reason undefined; the turn must STILL force-continue (the block is
// what matters), not silently stop. Self-limit to one block so it can't loop.
// Regression: a blocking Stop hook (exit 2) with no stderr yields decision 'deny' + reason
// undefined; the turn must STILL force-continue (the block is what matters), not silently
// stop.
const d = dir()
const marker = join(d, 'fired')
const s = sh(d, 'stop.sh', `#!/usr/bin/env bash\nif [ -e "${marker}" ]; then exit 0; fi\ntouch "${marker}"\nexit 2\n`)
@@ -382,10 +382,8 @@ describe('hooks-claude coverage — schema-bypass apply + unspawnable hook', ()
describe('hooks-claude coverage — continue:false, context arm, no-cwd', () => {
it('a {"continue":false} hook is RECORDED as decision "stop" but does not halt the run (TODO(hook-continue-false))', async () => {
// Honoring `continue:false` (hard-halt the whole run) is deferred — there is
// no such primitive on the interception seams yet. So this asserts the LOG
// faithfully records the halt request (decision "stop"), AND that the run is
// NOT actually halted: the tool still runs and the turn completes normally.
// Honoring `continue:false` (hard-halt the whole run) is deferred — there is no such
// primitive on the interception seams yet.
const d = dir()
const s = sh(d, 'stop.sh', '#!/usr/bin/env bash\necho \'{"continue":false,"stopReason":"halt"}\'\n')
const path = hooks(d, { PreToolUse: [{ hooks: [{ type: 'command', command: s }] }] })
@@ -457,9 +455,8 @@ describe('hooks-claude coverage — continue:false, context arm, no-cwd', () =>
})
it('a context-only UserPromptSubmit hook DELEGATES so a later listener can still block', async () => {
// A hook that only adds context must NOT short-circuit the waterfall: a
// downstream agent/prompt-submit listener (a policy plugin) must still get to
// block the prompt. The bridge delegates via next() and folds its context.
// A hook that only adds context must not short-circuit the waterfall: a downstream
// agent/prompt-submit listener (a policy plugin) must still get to block the prompt.
const d = dir()
const s = sh(d, 'ctx.sh', '#!/usr/bin/env bash\necho \'{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"bridge ctx"}}\'\n')
const path = hooks(d, { UserPromptSubmit: [{ hooks: [{ type: 'command', command: s }] }] })
@@ -589,10 +586,8 @@ describe('hooks-claude coverage — detached-listener catch handlers', () => {
describe('hooks-claude coverage — hook runs in the session cwd, not the server cwd', () => {
it('runs an agent-scoped hook in the session workspace even when the executor default differs', async () => {
// The bug: the bridge passed no workdir, so hooks ran in the executor default
// (the server launch dir), not session/new.cwd. Here the executor default and
// the session cwd are DIFFERENT temp dirs; a PreToolUse hook writes `pwd` to a
// marker and we assert it ran in the SESSION cwd.
// The bug: the bridge passed no workdir, so hooks ran in the executor default (the server
// launch dir), not session/new.cwd.
const serverDir = dir()
const sessionDir = dir()
const marker = join(sessionDir, 'where')
@@ -626,11 +621,8 @@ describe('hooks-claude coverage — hook runs in the session cwd, not the server
})
it('runs a SubagentStop hook in the CHILD session workspace, not the server cwd', async () => {
// SubagentStop looks the child up (recoverable at subagent/end) and runs the
// hook in the CHILD's session cwd, not the executor default. Here the executor
// default and the child session cwd are DIFFERENT dirs; a SubagentStop hook
// writes `pwd` to a relative marker and we assert it landed in the CHILD dir —
// which only holds if the listener threaded the child agent into runPoint.
// SubagentStop looks the child up (recoverable at subagent/end) and runs the hook in the
// CHILD's session cwd, not the executor default.
const serverDir = dir()
const childDir = dir()
const marker = join(childDir, 'stopwhere')
@@ -681,11 +673,8 @@ describe('hooks-claude coverage — systemMessage is warned, not surfaced', () =
describe('hooks-claude coverage — SessionStart timing is best-effort (no-wait)', () => {
it('does NOT crash or block when the prompt is sent immediately (context is best-effort, may miss the first request)', async () => {
// Regression for the documented downgrade: session-start injection is
// detached, so a prompt sent immediately need not observe it. This asserts
// the SAFE properties (no crash, the turn still runs) WITHOUT waiting for the
// inject first — it documents the best-effort timing rather than masking it
// by pre-waiting for context/message (which the guaranteed-timing tests do).
// Regression for the documented downgrade: session-start injection is detached, so a prompt
// sent immediately need not observe it.
const d = dir()
const s = sh(d, 'start.sh', '#!/usr/bin/env bash\necho \'{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"late ctx"}}\'\n')
const path = hooks(d, { SessionStart: [{ hooks: [{ type: 'command', command: s }] }] })