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
Bun cross-runtime parity is supposed to be part of the definition of done (webjs runs on Node 24+ AND Bun, #508), but the guardrail is too weak, so it slips to an afterthought. Concretely, during #659 (the Origin/Sec-Fetch-Site CSRF change, which sits squarely on the request path) NOTHING prompted Bun verification: the CSRF check, SSR response building, and action dispatch are runtime-sensitive (the Bun.serve shell must preserve the headers the check reads), yet the change shipped, was reviewed, and was marked ready before Bun was run or the Bun tests updated. The user had to ask for it explicitly.
Two gaps in .claude/hooks/require-tests-with-src.sh:
It only emits a SOFT, non-blocking additionalContext reminder for runtime-sensitive source; nothing blocks a commit that changes a runtime-sensitive surface without touching a test/bun/* script.
Its runtime-sensitive regex is serialize|file-storage|listener-core|listener-bun|ts-strip|action-stream|render-server|websocket|node-version, which MISSES the request-handling path: csrf, actions, ssr, the dev handler, auth, session, cors. So a request-path change does not even trigger the soft reminder.
Design / approach
Make Bun parity a first-class, blocking gate, matching the existing require-tests-with-src.sh / require-docs-with-src.sh model (block + named env escape hatch).
Add a new PreToolUse Bash hook .claude/hooks/require-bun-parity-with-runtime-src.sh: on a git commit, if the staged diff changes framework source on a runtime-sensitive surface AND stages no test/bun/** file, BLOCK (exit 2). The message tells the author to add/update a test/bun/<feature>.mjs cross-runtime assertion, OR re-run with WEBJS_BUN_VERIFIED=1 to acknowledge an existing test/bun script already covers it AND was run under Bun (node scripts/run-bun-tests.js + the relevant test/bun/*.mjs).
Broaden the runtime-sensitive surface list (shared shape with the existing hook's regex) to add: csrf, actions, ssr, dev (the request handler), request, auth, session, cors alongside the existing serializer / listener / stream / crypto / ts-strip / websocket / node-version entries.
Keep require-tests-with-src.sh's reminder but widen its regex to match, so the soft nudge and the hard gate agree.
Elevate Bun parity in the webjs-start-work skill: today it is buried inside the test-layer bullet (SKILL.md ~L114). Add a distinct, prominent Definition-of-done line ("Bun parity is required for any runtime-sensitive change: run the Bun matrix and add/update test/bun") and mention the new hook.
Update root AGENTS.md (the "Code workflow (mandatory)" / testing area) and agent-docs/testing.md so running the Bun matrix and adding/updating test/bun is stated as part of the definition of done for a runtime-sensitive change, not an optional extra.
Alternatives considered: extending the existing hook to block instead of adding a new one. Rejected to keep the repo's one-hook-per-concern shape (block-prose, block-raw-htmlelement, require-tests, require-docs) and so the Bun gate has its own escape hatch and its own test.
Implementation notes (for the implementing agent)
Where to edit:
New hook: .claude/hooks/require-bun-parity-with-runtime-src.sh (model it on .claude/hooks/require-tests-with-src.sh: read the git commit Bash payload via jq -r '.tool_input.command', word-match git commit, inspect git diff --cached --name-only, gate packages/([^/]+/src|editors/[^/]+/src|cli/lib)/). Make executable (chmod +x).
Widen the regex in .claude/hooks/require-tests-with-src.shruntime_sensitive=... to include the request-path tokens.
Hooks test: add test/hooks/require-bun-parity.test.mjs (the repo already has test/hooks/route-skills.test.mjs; follow its harness shape, invoking the hook script with a crafted payload + a temp git index, asserting exit 2 when runtime-sensitive src is staged with no test/bun, exit 0 when a test/bun file is staged or WEBJS_BUN_VERIFIED=1).
Skill: .claude/skills/webjs-start-work/SKILL.md (the Definition-of-done section).
The hook fires only on git commit Bash calls; --no-verify and the env escape hatch must both be honored (mirror require-tests-with-src.sh's wording).
Do NOT block a pure docs / release / non-src commit (the packages/*/src gate handles that). A test/bun change alone, or a non-runtime-sensitive src change, must pass.
Bun must NOT be required to RUN for the hook itself (the hook is static file analysis); the hook only checks that a test/bun file is staged or the ack flag is set. Running Bun is the author's job, prompted by the message.
A git commit that stages a runtime-sensitive packages/*/src change with no test/bun/** file is BLOCKED (exit 2) with an actionable message naming the Bun matrix + WEBJS_BUN_VERIFIED=1 escape hatch.
The same commit passes when a test/bun/** file is staged, or with WEBJS_BUN_VERIFIED=1.
A non-runtime-sensitive src change, and a docs/test-only commit, are NOT blocked.
The runtime-sensitive surface list now matches the request path (csrf / actions / ssr / dev / auth / session / cors) so a CSRF-style change trips the gate.
test/hooks/require-bun-parity.test.mjs covers block + both pass paths (a counterfactual that the gate fires).
webjs-start-work skill + root AGENTS.md + agent-docs/testing.md state Bun parity as part of the definition of done.
The hook is wired in .claude/settings.json and is executable.
Problem
Bun cross-runtime parity is supposed to be part of the definition of done (webjs runs on Node 24+ AND Bun, #508), but the guardrail is too weak, so it slips to an afterthought. Concretely, during #659 (the Origin/Sec-Fetch-Site CSRF change, which sits squarely on the request path) NOTHING prompted Bun verification: the CSRF check, SSR response building, and action dispatch are runtime-sensitive (the
Bun.serveshell must preserve the headers the check reads), yet the change shipped, was reviewed, and was marked ready before Bun was run or the Bun tests updated. The user had to ask for it explicitly.Two gaps in
.claude/hooks/require-tests-with-src.sh:additionalContextreminder for runtime-sensitive source; nothing blocks a commit that changes a runtime-sensitive surface without touching atest/bun/*script.serialize|file-storage|listener-core|listener-bun|ts-strip|action-stream|render-server|websocket|node-version, which MISSES the request-handling path:csrf,actions,ssr, thedevhandler,auth,session,cors. So a request-path change does not even trigger the soft reminder.Design / approach
Make Bun parity a first-class, blocking gate, matching the existing
require-tests-with-src.sh/require-docs-with-src.shmodel (block + named env escape hatch).PreToolUseBash hook.claude/hooks/require-bun-parity-with-runtime-src.sh: on agit commit, if the staged diff changes framework source on a runtime-sensitive surface AND stages notest/bun/**file, BLOCK (exit 2). The message tells the author to add/update atest/bun/<feature>.mjscross-runtime assertion, OR re-run withWEBJS_BUN_VERIFIED=1to acknowledge an existingtest/bunscript already covers it AND was run under Bun (node scripts/run-bun-tests.js+ the relevanttest/bun/*.mjs).csrf,actions,ssr,dev(the request handler),request,auth,session,corsalongside the existing serializer / listener / stream / crypto / ts-strip / websocket / node-version entries.require-tests-with-src.sh's reminder but widen its regex to match, so the soft nudge and the hard gate agree.webjs-start-workskill: today it is buried inside the test-layer bullet (SKILL.md ~L114). Add a distinct, prominent Definition-of-done line ("Bun parity is required for any runtime-sensitive change: run the Bun matrix and add/update test/bun") and mention the new hook.AGENTS.md(the "Code workflow (mandatory)" / testing area) andagent-docs/testing.mdso running the Bun matrix and adding/updatingtest/bunis stated as part of the definition of done for a runtime-sensitive change, not an optional extra.Alternatives considered: extending the existing hook to block instead of adding a new one. Rejected to keep the repo's one-hook-per-concern shape (block-prose, block-raw-htmlelement, require-tests, require-docs) and so the Bun gate has its own escape hatch and its own test.
Implementation notes (for the implementing agent)
.claude/hooks/require-bun-parity-with-runtime-src.sh(model it on.claude/hooks/require-tests-with-src.sh: read thegit commitBash payload viajq -r '.tool_input.command', word-matchgit commit, inspectgit diff --cached --name-only, gatepackages/([^/]+/src|editors/[^/]+/src|cli/lib)/). Make executable (chmod +x)..claude/settings.jsonPreToolUsematcher: "Bash"array (alongside require-tests-with-src.sh / require-docs-with-src.sh)..claude/hooks/require-tests-with-src.shruntime_sensitive=...to include the request-path tokens.test/hooks/require-bun-parity.test.mjs(the repo already hastest/hooks/route-skills.test.mjs; follow its harness shape, invoking the hook script with a crafted payload + a temp git index, asserting exit 2 when runtime-sensitive src is staged with no test/bun, exit 0 when a test/bun file is staged or WEBJS_BUN_VERIFIED=1)..claude/skills/webjs-start-work/SKILL.md(the Definition-of-done section).AGENTS.md("Code workflow (mandatory)") +agent-docs/testing.md.git commitBash calls;--no-verifyand the env escape hatch must both be honored (mirror require-tests-with-src.sh's wording).packages/*/srcgate handles that). Atest/bunchange alone, or a non-runtime-sensitive src change, must pass.test/bunfile is staged or the ack flag is set. Running Bun is the author's job, prompted by the message.Acceptance criteria
git committhat stages a runtime-sensitivepackages/*/srcchange with notest/bun/**file is BLOCKED (exit 2) with an actionable message naming the Bun matrix +WEBJS_BUN_VERIFIED=1escape hatch.test/bun/**file is staged, or withWEBJS_BUN_VERIFIED=1.test/hooks/require-bun-parity.test.mjscovers block + both pass paths (a counterfactual that the gate fires)..claude/settings.jsonand is executable.