Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3d3b334
Clarify review evidence and preserve required test artifacts
BigSimmo Oct 17, 2026
48ca7a4
test: isolate Cloud shell fixtures and await lazy on-call panel
BigSimmo Oct 17, 2026
f2559a5
Add portable advisory Codex project hook supplement
BigSimmo Oct 17, 2026
9a0c6a5
docs: align approved shared memory scope
BigSimmo Oct 17, 2026
cbb9370
Fix synthetic Cloud fixture environment types
BigSimmo Oct 18, 2026
033dc3e
fix: 2 findings — Exclude hyphenated report merges from reuse; Allow mem
qodo-code-review[bot] Oct 18, 2026
3bcfa77
fix: 3 findings — Pin the hook adapter to the trusted project; Dispatch
qodo-code-review[bot] Oct 18, 2026
850901b
fix: Align ordinary PR formatting guidance
qodo-code-review[bot] Oct 18, 2026
964682c
fix: 2 findings — Name the approved shared memory namespace; Remove dupl
qodo-code-review[bot] Oct 18, 2026
92ee8b2
fix: Wire Codex adapter tests into verification
qodo-code-review[bot] Oct 18, 2026
7545756
Revert "fix: 3 findings — Pin the hook adapter to the trusted project…
grokbot Oct 18, 2026
b991bf9
Revert "fix: 2 findings — Exclude hyphenated report merges from reuse…
grokbot Oct 18, 2026
ad199a6
fix(hooks): redo Codex hook adapter and vitest reuse fixes, formatted
grokbot Oct 18, 2026
631d9a4
Merge remote-tracking branch 'origin/main' into sweep-3635
BigSimmo Oct 18, 2026
4235bc2
Merge branch 'main' into codex/agent-policy-delivery-20261017
BigSimmo Oct 18, 2026
c85579c
Merge remote-tracking branch 'origin/codex/agent-policy-delivery-2026…
grokbot Oct 18, 2026
e9ad394
fix(hooks): translate Codex edit aliases and apply_patch for the orga…
grokbot Oct 18, 2026
3fda2a3
Revert "fix: Wire Codex adapter tests into verification"
grokbot Oct 18, 2026
b3d6f32
chore: run Codex hook adapter tests in verify:cheap:internal
grokbot Oct 18, 2026
b9c7b1c
fix(hooks): match single-quoted and attached git -C values before push
grokbot Oct 18, 2026
83c5d7d
fix: keep installed-lock parity first in verify:cheap and test the re…
claude Oct 18, 2026
fd4831e
chore(tests): drop unused readFileSync import from gate-receipts test
claude Oct 18, 2026
0ee20b0
fix(hooks): recognise gh repository flags around pr create
claude Oct 18, 2026
e30a90e
fix(hooks): match gh pr create after substitution, quotes and paths
claude Oct 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/hooks/pr-handoff-stop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ post)
# without the URL check a failed create would mark this session as having an
# open PR with nothing to babysit.
created=1
if is_shell_tool && shell_input_matches 'gh[[:space:]]+pr[[:space:]]+create'; then
if is_shell_tool && shell_input_matches 'gh([[:space:]]+(-R[[:space:]]*[^[:space:]]+|--repo(=|[[:space:]]+)[^[:space:]]+|--?[[:alnum:]-]+(=[^[:space:]]*)?))*[[:space:]]+pr([[:space:]]+(-R[[:space:]]*[^[:space:]]+|--repo(=|[[:space:]]+)[^[:space:]]+|--?[[:alnum:]-]+(=[^[:space:]]*)?))*[[:space:]]+create'; then
created=0
fi
# End-anchor required: create_pull_request_review / _review_comment must NOT
Expand Down
4 changes: 2 additions & 2 deletions .claude/hooks/push-format-guard.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ IFS= read -r -d '' payload || true
# tool call. The test below uses shell builtins only.
#
# It is deliberately a SUPERSET of the real check further down
# (`git[[:space:]]+push`), so it can only ever let MORE through, never less: that
# (`git`, optional global options such as `-C <dir>`, then `push`), so it can only ever let MORE through, never less: that
# regex cannot match unless the bytes `git` appear before the bytes `push`,
# whether the command reaches it jq-decoded, grep-extracted, or as the raw
# payload. The `\u` arm covers the one theoretical gap in that argument — an
Expand Down Expand Up @@ -78,7 +78,7 @@ case "$tool_name" in
esac

# --- is this a push? ----------------------------------------------------------
printf '%s' "$command_text" | grep -Eq '(^|[;&|[:space:]])git[[:space:]]+push([[:space:]]|$)' || exit 0
printf '%s' "$command_text" | grep -Eq '(^|[;&|[:space:]])git([[:space:]]+(-[Cc][[:space:]]*("[^"]*"|'"'"'[^'"'"']*'"'"'|[^[:space:]]+)|--?[[:alnum:]-]+(=[^[:space:]]*)?))*[[:space:]]+push([[:space:]]|$)' || exit 0

# --- documented escape hatch (leading prefix only, not an incidental mention) --
printf '%s' "$command_text" \
Expand Down
54 changes: 54 additions & 0 deletions .codex/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "^(?:Bash|PowerShell|exec_command|functions\\.exec_command|Edit|Write|MultiEdit|NotebookEdit|edit_file|write_file|functions\\.edit_file|functions\\.write_file|apply_patch|functions\\.apply_patch|CronCreate)$",
"hooks": [
{
"type": "command",
"command": "node -e '(async()=>{const f=require(\"node:fs\"),p=require(\"node:path\"),u=require(\"node:url\");let s=\"\";for await(const b of process.stdin){s+=b;if(s.length>1048576)return;}let v;try{v=JSON.parse(s)}catch{return}if(!v||typeof v!==\"object\"||Array.isArray(v))return;let d=v.tool_input?.workdir??v.tool_input?.cwd??v.cwd??process.cwd();if(typeof d!==\"string\"||!p.isAbsolute(d))return;d=p.resolve(d);let t=p.resolve(process.cwd());while(!f.existsSync(p.join(t,\".git\"))){let q=p.dirname(t);if(q===t)return;t=q;}if([\"GIT_DIR\",\"GIT_COMMON_DIR\",\"GIT_WORK_TREE\",\"GIT_CEILING_DIRECTORIES\",\"GIT_DISCOVERY_ACROSS_FILESYSTEM\"].some(k=>process.env[k])){const g=require(\"node:child_process\").spawnSync(\"git\",[\"-C\",d,\"rev-parse\",\"--show-toplevel\"],{encoding:\"utf8\",windowsHide:true,timeout:5000});if(g.status!==0)return;d=p.resolve(g.stdout.trim())}else{if(f.existsSync(p.join(d,\"HEAD\"))&&f.existsSync(p.join(d,\"objects\"))&&f.existsSync(p.join(d,\"refs\")))return;while(!f.existsSync(p.join(d,\".git\"))){const q=p.dirname(d);if(q===d)return;d=q;}}if(d!==t)return;const a=p.join(d,\".codex/hooks/dispatch.mjs\");if(!f.existsSync(a))return;const m=await import(u.pathToFileURL(a).href),r=await m.run(process.argv[1],v,d);process.stdout.write(r.stdout);process.stderr.write(r.stderr);process.exitCode=r.code;})().catch(()=>{process.stderr.write(\"Codex project hook adapter unavailable; no advisory decision was produced.\\n\")})' PreToolUse",
"commandWindows": "& $(if (Test-Path \"C:/Program Files/nodejs/node.exe\") { \"C:/Program Files/nodejs/node.exe\" } else { \"node\" }) -e \"(async()=>{const f=require('node:fs'),p=require('node:path'),u=require('node:url');let s='';for await(const b of process.stdin){s+=b;if(s.length>1048576)return;}let v;try{v=JSON.parse(s)}catch{return}if(!v||typeof v!=='object'||Array.isArray(v))return;let d=v.tool_input?.workdir??v.tool_input?.cwd??v.cwd??process.cwd();if(typeof d!=='string'||!p.isAbsolute(d))return;d=p.resolve(d);let t=p.resolve(process.cwd());while(!f.existsSync(p.join(t,'.git'))){let q=p.dirname(t);if(q===t)return;t=q;}if(['GIT_DIR','GIT_COMMON_DIR','GIT_WORK_TREE','GIT_CEILING_DIRECTORIES','GIT_DISCOVERY_ACROSS_FILESYSTEM'].some(k=>process.env[k])){const g=require('node:child_process').spawnSync('git',['-C',d,'rev-parse','--show-toplevel'],{encoding:'utf8',windowsHide:true,timeout:5000});if(g.status!==0)return;d=p.resolve(g.stdout.trim())}else{if(f.existsSync(p.join(d,'HEAD'))&&f.existsSync(p.join(d,'objects'))&&f.existsSync(p.join(d,'refs')))return;while(!f.existsSync(p.join(d,'.git'))){const q=p.dirname(d);if(q===d)return;d=q;}}if(d!==t)return;const a=p.join(d,'.codex/hooks/dispatch.mjs');if(!f.existsSync(a))return;const m=await import(u.pathToFileURL(a).href),r=await m.run(process.argv[1],v,d);process.stdout.write(r.stdout);process.stderr.write(r.stderr);process.exitCode=r.code;})().catch(()=>{process.stderr.write('Codex project hook adapter unavailable; no advisory decision was produced.\\n')})\" PreToolUse",
"timeout": 180
}
]
}
],
"PostToolUse": [
{
"matcher": "^(?:Bash|PowerShell|exec_command|functions\\.exec_command|Edit|Write|MultiEdit|NotebookEdit|edit_file|write_file|functions\\.edit_file|functions\\.write_file|apply_patch|functions\\.apply_patch|.*[Cc]reate_?[Pp]ull_?[Rr]equest)$",
"hooks": [
{
"type": "command",
"command": "node -e '(async()=>{const f=require(\"node:fs\"),p=require(\"node:path\"),u=require(\"node:url\");let s=\"\";for await(const b of process.stdin){s+=b;if(s.length>1048576)return;}let v;try{v=JSON.parse(s)}catch{return}if(!v||typeof v!==\"object\"||Array.isArray(v))return;let d=v.tool_input?.workdir??v.tool_input?.cwd??v.cwd??process.cwd();if(typeof d!==\"string\"||!p.isAbsolute(d))return;d=p.resolve(d);let t=p.resolve(process.cwd());while(!f.existsSync(p.join(t,\".git\"))){let q=p.dirname(t);if(q===t)return;t=q;}if([\"GIT_DIR\",\"GIT_COMMON_DIR\",\"GIT_WORK_TREE\",\"GIT_CEILING_DIRECTORIES\",\"GIT_DISCOVERY_ACROSS_FILESYSTEM\"].some(k=>process.env[k])){const g=require(\"node:child_process\").spawnSync(\"git\",[\"-C\",d,\"rev-parse\",\"--show-toplevel\"],{encoding:\"utf8\",windowsHide:true,timeout:5000});if(g.status!==0)return;d=p.resolve(g.stdout.trim())}else{if(f.existsSync(p.join(d,\"HEAD\"))&&f.existsSync(p.join(d,\"objects\"))&&f.existsSync(p.join(d,\"refs\")))return;while(!f.existsSync(p.join(d,\".git\"))){const q=p.dirname(d);if(q===d)return;d=q;}}if(d!==t)return;const a=p.join(d,\".codex/hooks/dispatch.mjs\");if(!f.existsSync(a))return;const m=await import(u.pathToFileURL(a).href),r=await m.run(process.argv[1],v,d);process.stdout.write(r.stdout);process.stderr.write(r.stderr);process.exitCode=r.code;})().catch(()=>{process.stderr.write(\"Codex project hook adapter unavailable; no advisory decision was produced.\\n\")})' PostToolUse",
"commandWindows": "& $(if (Test-Path \"C:/Program Files/nodejs/node.exe\") { \"C:/Program Files/nodejs/node.exe\" } else { \"node\" }) -e \"(async()=>{const f=require('node:fs'),p=require('node:path'),u=require('node:url');let s='';for await(const b of process.stdin){s+=b;if(s.length>1048576)return;}let v;try{v=JSON.parse(s)}catch{return}if(!v||typeof v!=='object'||Array.isArray(v))return;let d=v.tool_input?.workdir??v.tool_input?.cwd??v.cwd??process.cwd();if(typeof d!=='string'||!p.isAbsolute(d))return;d=p.resolve(d);let t=p.resolve(process.cwd());while(!f.existsSync(p.join(t,'.git'))){let q=p.dirname(t);if(q===t)return;t=q;}if(['GIT_DIR','GIT_COMMON_DIR','GIT_WORK_TREE','GIT_CEILING_DIRECTORIES','GIT_DISCOVERY_ACROSS_FILESYSTEM'].some(k=>process.env[k])){const g=require('node:child_process').spawnSync('git',['-C',d,'rev-parse','--show-toplevel'],{encoding:'utf8',windowsHide:true,timeout:5000});if(g.status!==0)return;d=p.resolve(g.stdout.trim())}else{if(f.existsSync(p.join(d,'HEAD'))&&f.existsSync(p.join(d,'objects'))&&f.existsSync(p.join(d,'refs')))return;while(!f.existsSync(p.join(d,'.git'))){const q=p.dirname(d);if(q===d)return;d=q;}}if(d!==t)return;const a=p.join(d,'.codex/hooks/dispatch.mjs');if(!f.existsSync(a))return;const m=await import(u.pathToFileURL(a).href),r=await m.run(process.argv[1],v,d);process.stdout.write(r.stdout);process.stderr.write(r.stderr);process.exitCode=r.code;})().catch(()=>{process.stderr.write('Codex project hook adapter unavailable; no advisory decision was produced.\\n')})\" PostToolUse",
"timeout": 45
}
]
}
],
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "node -e '(async()=>{const f=require(\"node:fs\"),p=require(\"node:path\"),u=require(\"node:url\");let s=\"\";for await(const b of process.stdin){s+=b;if(s.length>1048576)return;}let v;try{v=JSON.parse(s)}catch{return}if(!v||typeof v!==\"object\"||Array.isArray(v))return;let d=v.tool_input?.workdir??v.tool_input?.cwd??v.cwd??process.cwd();if(typeof d!==\"string\"||!p.isAbsolute(d))return;d=p.resolve(d);let t=p.resolve(process.cwd());while(!f.existsSync(p.join(t,\".git\"))){let q=p.dirname(t);if(q===t)return;t=q;}if([\"GIT_DIR\",\"GIT_COMMON_DIR\",\"GIT_WORK_TREE\",\"GIT_CEILING_DIRECTORIES\",\"GIT_DISCOVERY_ACROSS_FILESYSTEM\"].some(k=>process.env[k])){const g=require(\"node:child_process\").spawnSync(\"git\",[\"-C\",d,\"rev-parse\",\"--show-toplevel\"],{encoding:\"utf8\",windowsHide:true,timeout:5000});if(g.status!==0)return;d=p.resolve(g.stdout.trim())}else{if(f.existsSync(p.join(d,\"HEAD\"))&&f.existsSync(p.join(d,\"objects\"))&&f.existsSync(p.join(d,\"refs\")))return;while(!f.existsSync(p.join(d,\".git\"))){const q=p.dirname(d);if(q===d)return;d=q;}}if(d!==t)return;const a=p.join(d,\".codex/hooks/dispatch.mjs\");if(!f.existsSync(a))return;const m=await import(u.pathToFileURL(a).href),r=await m.run(process.argv[1],v,d);process.stdout.write(r.stdout);process.stderr.write(r.stderr);process.exitCode=r.code;})().catch(()=>{process.stderr.write(\"Codex project hook adapter unavailable; no advisory decision was produced.\\n\")})' PreCompact",
"commandWindows": "& $(if (Test-Path \"C:/Program Files/nodejs/node.exe\") { \"C:/Program Files/nodejs/node.exe\" } else { \"node\" }) -e \"(async()=>{const f=require('node:fs'),p=require('node:path'),u=require('node:url');let s='';for await(const b of process.stdin){s+=b;if(s.length>1048576)return;}let v;try{v=JSON.parse(s)}catch{return}if(!v||typeof v!=='object'||Array.isArray(v))return;let d=v.tool_input?.workdir??v.tool_input?.cwd??v.cwd??process.cwd();if(typeof d!=='string'||!p.isAbsolute(d))return;d=p.resolve(d);let t=p.resolve(process.cwd());while(!f.existsSync(p.join(t,'.git'))){let q=p.dirname(t);if(q===t)return;t=q;}if(['GIT_DIR','GIT_COMMON_DIR','GIT_WORK_TREE','GIT_CEILING_DIRECTORIES','GIT_DISCOVERY_ACROSS_FILESYSTEM'].some(k=>process.env[k])){const g=require('node:child_process').spawnSync('git',['-C',d,'rev-parse','--show-toplevel'],{encoding:'utf8',windowsHide:true,timeout:5000});if(g.status!==0)return;d=p.resolve(g.stdout.trim())}else{if(f.existsSync(p.join(d,'HEAD'))&&f.existsSync(p.join(d,'objects'))&&f.existsSync(p.join(d,'refs')))return;while(!f.existsSync(p.join(d,'.git'))){const q=p.dirname(d);if(q===d)return;d=q;}}if(d!==t)return;const a=p.join(d,'.codex/hooks/dispatch.mjs');if(!f.existsSync(a))return;const m=await import(u.pathToFileURL(a).href),r=await m.run(process.argv[1],v,d);process.stdout.write(r.stdout);process.stderr.write(r.stderr);process.exitCode=r.code;})().catch(()=>{process.stderr.write('Codex project hook adapter unavailable; no advisory decision was produced.\\n')})\" PreCompact",
"timeout": 45
}
]
}
],
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "node -e '(async()=>{const f=require(\"node:fs\"),p=require(\"node:path\"),u=require(\"node:url\");let s=\"\";for await(const b of process.stdin){s+=b;if(s.length>1048576)return;}let v;try{v=JSON.parse(s)}catch{return}if(!v||typeof v!==\"object\"||Array.isArray(v))return;let d=v.tool_input?.workdir??v.tool_input?.cwd??v.cwd??process.cwd();if(typeof d!==\"string\"||!p.isAbsolute(d))return;d=p.resolve(d);let t=p.resolve(process.cwd());while(!f.existsSync(p.join(t,\".git\"))){let q=p.dirname(t);if(q===t)return;t=q;}if([\"GIT_DIR\",\"GIT_COMMON_DIR\",\"GIT_WORK_TREE\",\"GIT_CEILING_DIRECTORIES\",\"GIT_DISCOVERY_ACROSS_FILESYSTEM\"].some(k=>process.env[k])){const g=require(\"node:child_process\").spawnSync(\"git\",[\"-C\",d,\"rev-parse\",\"--show-toplevel\"],{encoding:\"utf8\",windowsHide:true,timeout:5000});if(g.status!==0)return;d=p.resolve(g.stdout.trim())}else{if(f.existsSync(p.join(d,\"HEAD\"))&&f.existsSync(p.join(d,\"objects\"))&&f.existsSync(p.join(d,\"refs\")))return;while(!f.existsSync(p.join(d,\".git\"))){const q=p.dirname(d);if(q===d)return;d=q;}}if(d!==t)return;const a=p.join(d,\".codex/hooks/dispatch.mjs\");if(!f.existsSync(a))return;const m=await import(u.pathToFileURL(a).href),r=await m.run(process.argv[1],v,d);process.stdout.write(r.stdout);process.stderr.write(r.stderr);process.exitCode=r.code;})().catch(()=>{process.stderr.write(\"Codex project hook adapter unavailable; no advisory decision was produced.\\n\")})' SessionStart",
"commandWindows": "& $(if (Test-Path \"C:/Program Files/nodejs/node.exe\") { \"C:/Program Files/nodejs/node.exe\" } else { \"node\" }) -e \"(async()=>{const f=require('node:fs'),p=require('node:path'),u=require('node:url');let s='';for await(const b of process.stdin){s+=b;if(s.length>1048576)return;}let v;try{v=JSON.parse(s)}catch{return}if(!v||typeof v!=='object'||Array.isArray(v))return;let d=v.tool_input?.workdir??v.tool_input?.cwd??v.cwd??process.cwd();if(typeof d!=='string'||!p.isAbsolute(d))return;d=p.resolve(d);let t=p.resolve(process.cwd());while(!f.existsSync(p.join(t,'.git'))){let q=p.dirname(t);if(q===t)return;t=q;}if(['GIT_DIR','GIT_COMMON_DIR','GIT_WORK_TREE','GIT_CEILING_DIRECTORIES','GIT_DISCOVERY_ACROSS_FILESYSTEM'].some(k=>process.env[k])){const g=require('node:child_process').spawnSync('git',['-C',d,'rev-parse','--show-toplevel'],{encoding:'utf8',windowsHide:true,timeout:5000});if(g.status!==0)return;d=p.resolve(g.stdout.trim())}else{if(f.existsSync(p.join(d,'HEAD'))&&f.existsSync(p.join(d,'objects'))&&f.existsSync(p.join(d,'refs')))return;while(!f.existsSync(p.join(d,'.git'))){const q=p.dirname(d);if(q===d)return;d=q;}}if(d!==t)return;const a=p.join(d,'.codex/hooks/dispatch.mjs');if(!f.existsSync(a))return;const m=await import(u.pathToFileURL(a).href),r=await m.run(process.argv[1],v,d);process.stdout.write(r.stdout);process.stderr.write(r.stderr);process.exitCode=r.code;})().catch(()=>{process.stderr.write('Codex project hook adapter unavailable; no advisory decision was produced.\\n')})\" SessionStart",
"timeout": 45
}
]
}
]
}
}
Loading
Loading