test(hooks): snapshot the CC + Codex hook matrix end-to-end
Load both hook bridges in the ACP example (dsh-hooks-claude → ./hooks.json, dsh-hooks-codex → ./codex-hooks.json) so the full-transcript snapshot tier can exercise each dialect against the real app. An absent config file is a silent no-op, so a scenario carries only the file it needs and the other bridge vanishes — verified byte-identical against every pre-existing snapshot. Add a scenario per hook point × its headline Decision outcome, both dialects: UserPromptSubmit block (authored, keyless) + context-fold, PreToolUse deny/ask, PostToolUse block/context, Stop force-continue. The mid-turn scenarios are recorded against the real API with the hook active, so the model's reaction to a denied/blocked/force-continued turn is part of the replayed transcript. SessionStart and SubagentStart are deliberately excluded (detached best-effort inject races the log position — a recorded golden fails 10/10 on its own replay), as is SubagentStop (observe-only, zero transcript footprint — a golden could never be proven to fail). Both stay on the bridges' unit coverage. See docs/rfc/implemented/testing/2026-07-04-hook-snapshot-matrix.md.
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"steps": [
|
||||
{ "op": "initialize" },
|
||||
{ "op": "newSession" },
|
||||
{ "op": "prompt", "text": "Delete everything in the repo." }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{"type":"session","version":0,"id":"{{sessionId}}","createdAt":0,"cwd":"{{cwd}}"}
|
||||
{"type":"turn/start","seq":0,"time":0,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"hook/invoked","seq":1,"time":0,"data":{"turn":1,"point":"UserPromptSubmit","dialect":"claude","handlerId":"claude:UserPromptSubmit:1"}}
|
||||
{"type":"hook/result","seq":2,"time":0,"data":{"turn":1,"point":"UserPromptSubmit","handlerId":"claude:UserPromptSubmit:1","decision":"block","exitCode":2,"stderrSummary":"blocked by policy hook","durationMs":0}}
|
||||
{"type":"prompt/blocked","seq":3,"time":0,"data":{"content":[{"type":"text","text":"Delete everything in the repo."}],"source":{"kind":"user"},"reason":"blocked by policy hook"}}
|
||||
{"type":"turn/end","seq":4,"time":0,"data":{"turn":1,"reason":{"kind":"rejected","reason":"blocked by policy hook"}}}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
|
||||
{"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}"}}
|
||||
{"jsonrpc":"2.0","id":3,"result":{"stopReason":"cancelled"}}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"hooks": {
|
||||
"UserPromptSubmit": [
|
||||
{
|
||||
"hooks": [
|
||||
{ "type": "command", "command": "echo 'blocked by policy hook' >&2; exit 2" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user