Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
47 changes: 47 additions & 0 deletions .claude/hooks/guard-main-checkout-bash.selftest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,34 @@ expect() { # expect <block|allow> <command> [env…]
fi
}

stderr_of() { # stderr_of <command> [env…] -> the refusal text an agent actually reads
local cmd="$1"; shift
local payload
payload="$(jq -nc --arg c "$cmd" --arg w "$CWD" \
'{cwd:$w,tool_name:"Bash",tool_input:{command:$c}}')"
printf '%s' "$payload" | env "$@" "$hook" 2>&1 >/dev/null
}

says() { # says <needle> <label> <command> [env…]
local needle="$1" label="$2" subject="$3"; shift 3
local out; out="$(stderr_of "$subject" "$@")"
case "$out" in
*"$needle"*) pass=$((pass + 1)); printf ' ok says %s\n' "$label" ;;
*) fail=$((fail + 1)); printf ' FAIL missing "%s" %s\n' "$needle" "$label" ;;
esac
}

lacks() { # lacks <needle> <label> <command> [env…]
# The direction only an ABSENCE assertion can hold: a remedy that stopped being true stays
# in the text a reader acts on long after the thing it described stopped working.
local needle="$1" label="$2" subject="$3"; shift 3
local out; out="$(stderr_of "$subject" "$@")"
case "$out" in
*"$needle"*) fail=$((fail + 1)); printf ' FAIL still says "%s" %s\n' "$needle" "$label" ;;
*) pass=$((pass + 1)); printf ' ok lacks %s\n' "$label" ;;
esac
}

echo "== writes into the shared PRIMARY checkout are blocked =="
CWD="$MAIN"
expect block "sed -i s/a/b/ $MAIN/pkg/x.ts"
Expand Down Expand Up @@ -289,6 +317,25 @@ CWD="$MAIN"
expect allow 'sed -i s/a/b/ pkg/x.ts' OS_ALLOW_MAIN_EDITS=1
expect allow 'echo x > README.md' OS_ALLOW_MAIN_EDITS=1

echo "== the hatch names WHERE it works: this hook's own environment, never a prefix =="
# The refusal used to end by telling the reader to re-run the same thing with the variable
# as a VAR=1 command prefix — an instruction that cannot work where it is printed. A VAR=1
# prefix sets the variable in the environment of THAT COMMAND; this hook is not that
# command, and it reads the variable from its own environment, so the prefix changes
# nothing and the refusal repeats (#15971). An instruction that does not work is an
# invitation to route around the guard, so both directions are pinned: the dead remedy is
# gone, and the sentence names the environment the hook actually reads. The `allow` rows
# next door — the variable really in the hook's environment — are this pair's other half.
# The first row is the card's own reproduction, kept as a case: the prefix spelled exactly
# as the old message told the reader to spell it must still BLOCK, because it never reaches
# this hook. It is the twin of the `allow` rows above, where the same variable is really in
# the hook's environment.
CWD="$MAIN"
expect block "OS_ALLOW_MAIN_EDITS=1 rm -f $MAIN/x"
expect block 'OS_ALLOW_MAIN_EDITS=1 sed -i s/a/b/ pkg/x.ts'
lacks 're-run with' 'the refusal no longer prints the prefix remedy' 'sed -i s/a/b/ pkg/x.ts'
says 'hook itself runs in' 'the refusal names the environment this hook reads' 'sed -i s/a/b/ pkg/x.ts'

echo "== unparseable / absent payload fails open =="
for probe in '{"tool_name":"Bash","tool_input":{}}' 'not json at all' '{}'; do
if printf '%s' "$probe" | "$hook" >/dev/null 2>&1; then
Expand Down
5 changes: 4 additions & 1 deletion .claude/hooks/guard-main-checkout-bash.sh
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,10 @@ Always fine, no flag needed:
writes into a linked worktree sed -i … ../${name}-<task>/packages/…
writes outside any repo /tmp/…, the scratchpad, \$HOME dotfiles

Deliberate non-task exception: re-run with OS_ALLOW_MAIN_EDITS=1.
Deliberate non-task exception: set OS_ALLOW_MAIN_EDITS=1 in the
environment this hook itself runs in — a local settings "env" entry, or whatever this
agent process was started with. A VAR=1 prefix on a command sets it for that command
only, and this hook is not that command, so a prefix never reaches it.
EOF
exit 2
fi
Expand Down
39 changes: 39 additions & 0 deletions .claude/hooks/guard-shared-stash.selftest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,33 @@ expect() { # expect <block|allow> <command> [env…]
fi
}

stderr_of() { # stderr_of <command> [env…] -> the refusal text an agent actually reads
local cmd="$1"; shift
local payload
payload="$(jq -nc --arg c "$cmd" '{tool_name:"Bash",tool_input:{command:$c}}')"
printf '%s' "$payload" | env "$@" "$hook" 2>&1 >/dev/null
}

says() { # says <needle> <label> <command> [env…]
local needle="$1" label="$2" subject="$3"; shift 3
local out; out="$(stderr_of "$subject" "$@")"
case "$out" in
*"$needle"*) pass=$((pass + 1)); printf ' ok says %s\n' "$label" ;;
*) fail=$((fail + 1)); printf ' FAIL missing "%s" %s\n' "$needle" "$label" ;;
esac
}

lacks() { # lacks <needle> <label> <command> [env…]
# The direction only an ABSENCE assertion can hold: a remedy that stopped being true stays
# in the text a reader acts on long after the thing it described stopped working.
local needle="$1" label="$2" subject="$3"; shift 3
local out; out="$(stderr_of "$subject" "$@")"
case "$out" in
*"$needle"*) fail=$((fail + 1)); printf ' FAIL still says "%s" %s\n' "$needle" "$label" ;;
*) pass=$((pass + 1)); printf ' ok lacks %s\n' "$label" ;;
esac
}

echo "== mutating forms must be blocked =="
expect block 'git stash'
expect block 'git stash push -- packages/fields/src/RecordPickerDialog.tsx'
Expand Down Expand Up @@ -123,6 +150,18 @@ expect block "echo 'a \\' ; git stash pop"
echo "== escape hatch =="
expect allow 'git stash pop' OS_ALLOW_STASH=1

echo "== the hatch names WHERE it works: this hook's own environment, never a prefix =="
# The refusal used to end by telling the reader to re-run the same thing with the variable
# as a VAR=1 command prefix — an instruction that cannot work where it is printed. A VAR=1
# prefix sets the variable in the environment of THAT COMMAND; this hook is not that
# command, and it reads the variable from its own environment, so the prefix changes
# nothing and the refusal repeats (#15971). An instruction that does not work is an
# invitation to route around the guard, so both directions are pinned: the dead remedy is
# gone, and the sentence names the environment the hook actually reads. The `allow` rows
# next door — the variable really in the hook's environment — are this pair's other half.
lacks 're-run with' 'the refusal no longer prints the prefix remedy' 'git stash pop'
says 'hook itself runs in' 'the refusal names the environment this hook reads' 'git stash pop'

echo "== payload with no command fails open =="
if printf '%s' '{"tool_name":"Bash","tool_input":{}}' | "$hook" >/dev/null 2>&1; then
pass=$((pass + 1)); printf ' ok allow (empty tool_input)\n'
Expand Down
12 changes: 8 additions & 4 deletions .claude/hooks/guard-shared-stash.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@
# for anyone who means it. Widening it to string-match anywhere in the command would block
# every `grep "git stash"` run against this very file.
#
# Self-test (48 cases, no network, no build): .claude/hooks/guard-shared-stash.selftest.sh
# 48 = 46 `expect ` lines + 2 inline specials (empty-tool_input fail-open, no-jq fallback).
# Re-derive when the matrix changes: `grep -c '^expect ' <selftest>` + 2, and the run's own
# Self-test (50 cases, no network, no build): .claude/hooks/guard-shared-stash.selftest.sh
# 50 = 46 `expect ` lines + 2 inline specials (empty-tool_input fail-open, no-jq fallback)
# + 2 hatch-message assertions (`lacks`/`says`, which are not `expect ` lines).
# Re-derive when the matrix changes: `grep -c '^expect ' <selftest>` + 4, and the run's own
# tail prints the total ("N passed, N failed") — keep this number equal to it.

set -uo pipefail
Expand Down Expand Up @@ -203,7 +204,10 @@ Already allowed, no flag needed:
git stash list | git stash show | git stash create
git stash apply <sha> | git stash store <sha> # literal hex id, never stash@{N}

Deliberate exception (the stack really is yours alone): re-run with OS_ALLOW_STASH=1.
Deliberate exception (the stack really is yours alone): set OS_ALLOW_STASH=1 in the
environment this hook itself runs in — a local settings "env" entry, or whatever this
agent process was started with. A VAR=1 prefix on a command sets it for that command
only, and this hook is not that command, so a prefix never reaches it.
EOF
exit 2
done
Expand Down
41 changes: 41 additions & 0 deletions .claude/hooks/guard-tree-enum.selftest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,33 @@ expect() { # expect <block|allow> <command> [env…]
fi
}

stderr_of() { # stderr_of <command> [env…] -> the refusal text an agent actually reads
local cmd="$1"; shift
local payload
payload="$(jq -nc --arg c "$cmd" '{tool_name:"Bash",tool_input:{command:$c}}')"
printf '%s' "$payload" | env "$@" "$hook" 2>&1 >/dev/null
}

says() { # says <needle> <label> <command> [env…]
local needle="$1" label="$2" subject="$3"; shift 3
local out; out="$(stderr_of "$subject" "$@")"
case "$out" in
*"$needle"*) pass=$((pass + 1)); printf ' ok says %s\n' "$label" ;;
*) fail=$((fail + 1)); printf ' FAIL missing "%s" %s\n' "$needle" "$label" ;;
esac
}

lacks() { # lacks <needle> <label> <command> [env…]
# The direction only an ABSENCE assertion can hold: a remedy that stopped being true stays
# in the text a reader acts on long after the thing it described stopped working.
local needle="$1" label="$2" subject="$3"; shift 3
local out; out="$(stderr_of "$subject" "$@")"
case "$out" in
*"$needle"*) fail=$((fail + 1)); printf ' FAIL still says "%s" %s\n' "$needle" "$label" ;;
*) pass=$((pass + 1)); printf ' ok lacks %s\n' "$label" ;;
esac
}

echo "== THE MEASURED SIGNATURE: working-tree list + origin/main read in one command =="
# objectui, 2026-08-29 — the loop that reported "no workflow subscribes ready_for_review"
expect block 'for f in .github/workflows/*.yml; do git show "origin/main:$f" | grep -q ready_for_review && echo "$f"; done'
Expand Down Expand Up @@ -101,6 +128,20 @@ expect allow 'git worktree add ../objectui-issue-13305 -b claude/issue-13305 ori
echo "== the deliberate exception releases it =="
expect allow 'for f in .github/workflows/*.yml; do git show "origin/main:$f"; done' OS_ALLOW_TREE_ENUM=1

echo "== the hatch names WHERE it works: this hook's own environment, never a prefix =="
# The refusal used to end by telling the reader to re-run the same thing with the variable
# as a VAR=1 command prefix — an instruction that cannot work where it is printed. A VAR=1
# prefix sets the variable in the environment of THAT COMMAND; this hook is not that
# command, and it reads the variable from its own environment, so the prefix changes
# nothing and the refusal repeats (#15971). An instruction that does not work is an
# invitation to route around the guard, so both directions are pinned: the dead remedy is
# gone, and the sentence names the environment the hook actually reads. The `allow` rows
# next door — the variable really in the hook's environment — are this pair's other half.
lacks 're-run with' 'the refusal no longer prints the prefix remedy' \
'for f in .github/workflows/*.yml; do git show "origin/main:$f"; done'
says 'hook itself runs in' 'the refusal names the environment this hook reads' \
'for f in .github/workflows/*.yml; do git show "origin/main:$f"; done'

echo "== fails OPEN on payloads it cannot parse =="
printf '%s' '{"tool_name":"Bash","tool_input":{}}' | "$hook" >/dev/null 2>&1
if [ $? -eq 0 ]; then pass=$((pass + 1)); printf ' ok allow <no command in payload>\n'
Expand Down
5 changes: 4 additions & 1 deletion .claude/hooks/guard-tree-enum.sh
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,10 @@ A command that enumerates with git ls-tree is never blocked here, however it the
Either half alone is fine too — this fires only on the two together.

Deliberate exception (the working tree really is the population you mean — e.g. asking
what YOUR branch changed): re-run with OS_ALLOW_TREE_ENUM=1.
what YOUR branch changed): set OS_ALLOW_TREE_ENUM=1 in the
environment this hook itself runs in — a local settings "env" entry, or whatever this
agent process was started with. A VAR=1 prefix on a command sets it for that command
only, and this hook is not that command, so a prefix never reaches it.
EOF
exit 2
fi
Expand Down
26 changes: 13 additions & 13 deletions scripts/upstream-port-pin.json
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,8 @@
{
"ported": ".claude/hooks/guard-main-checkout-bash.sh",
"upstreamPath": ".claude/hooks/guard-main-checkout-bash.sh",
"ref": "924f0fed152781868739b66a3376f6b21d9dffe0",
"upstreamSha256": "138594f3955ed1145437709ae917eeb7e9b561c91cfd46c2665a80bbc8260afd",
"ref": "7c2c5aedd94d7b0d94c91432bc607862e9c83c6c",
"upstreamSha256": "bedf697936506b9943a9471224c9cc60e513cc4d801729c1df2eb83a8123625c",
"divergences": [
{
"id": "example-edit-path",
Expand Down Expand Up @@ -456,8 +456,8 @@
{
"ported": ".claude/hooks/guard-main-checkout-bash.selftest.sh",
"upstreamPath": ".claude/hooks/guard-main-checkout-bash.selftest.sh",
"ref": "924f0fed152781868739b66a3376f6b21d9dffe0",
"upstreamSha256": "8c6d3d3984419f746424417269f3398ddca1950c71ccc683e945d994539c97e7",
"ref": "7c2c5aedd94d7b0d94c91432bc607862e9c83c6c",
"upstreamSha256": "fab5ec59b0855373bbc1c498af7f791af3c26e9b1894244a2ded2e1b9ac75b38",
"divergences": [
{
"id": "matrix-provenance",
Expand Down Expand Up @@ -524,8 +524,8 @@
{
"ported": ".claude/hooks/guard-shared-stash.sh",
"upstreamPath": ".claude/hooks/guard-shared-stash.sh",
"ref": "2b9f5810b777ee9d0211d9d6273d854bd8e2d5bd",
"upstreamSha256": "44e187d2df28e65bae514ff044a9bb9573bb7d29597681d8e2243dc61cf0c998",
"ref": "7c2c5aedd94d7b0d94c91432bc607862e9c83c6c",
"upstreamSha256": "374e7008e7281f44d4dd62efcf2bbb0bd6c3155c5ee5fd68f1202b46a06639b9",
"divergences": [
{
"id": "no-prime-directive-citation",
Expand All @@ -549,7 +549,7 @@
"id": "selftest-case-count-and-derivation",
"why": "This copy pins its own self-test's case count and records how to re-derive it, rather than leaving a number nobody re-checks.",
"upstream": "# Self-test (51 cases, no network, no build): .claude/hooks/guard-shared-stash.selftest.sh\n",
"ported": "# Self-test (48 cases, no network, no build): .claude/hooks/guard-shared-stash.selftest.sh\n# 48 = 46 `expect ` lines + 2 inline specials (empty-tool_input fail-open, no-jq fallback).\n# Re-derive when the matrix changes: `grep -c '^expect ' <selftest>` + 2, and the run's own\n# tail prints the total (\"N passed, N failed\") — keep this number equal to it.\n"
"ported": "# Self-test (50 cases, no network, no build): .claude/hooks/guard-shared-stash.selftest.sh\n# 50 = 46 `expect ` lines + 2 inline specials (empty-tool_input fail-open, no-jq fallback)\n# + 2 hatch-message assertions (`lacks`/`says`, which are not `expect ` lines).\n# Re-derive when the matrix changes: `grep -c '^expect ' <selftest>` + 4, and the run's own\n# tail prints the total (\"N passed, N failed\") — keep this number equal to it.\n"
},
{
"id": "unquoted-backslash-rationale",
Expand Down Expand Up @@ -592,8 +592,8 @@
{
"ported": ".claude/hooks/guard-shared-stash.selftest.sh",
"upstreamPath": ".claude/hooks/guard-shared-stash.selftest.sh",
"ref": "2b9f5810b777ee9d0211d9d6273d854bd8e2d5bd",
"upstreamSha256": "2274f808dc89a1cb8df5431bfa2fd115b3171256136b7d1410ac7dccb08f5022",
"ref": "7c2c5aedd94d7b0d94c91432bc607862e9c83c6c",
"upstreamSha256": "fe747fb161f7f0031dc5ca56af76a62a32d3d9d046d9445e0369e923d0d91c69",
"divergences": [
{
"id": "matrix-provenance",
Expand Down Expand Up @@ -702,8 +702,8 @@
{
"ported": ".claude/hooks/guard-tree-enum.sh",
"upstreamPath": ".claude/hooks/guard-tree-enum.sh",
"ref": "c074c57e452734821caf6f43329a20e873926f16",
"upstreamSha256": "add7e5615359e336df722873924055829ea40b34ab106a73d2ae2a1abc8ce2f3",
"ref": "7c2c5aedd94d7b0d94c91432bc607862e9c83c6c",
"upstreamSha256": "8dd823052c8b276a7d8ee9bd183ef7aaee2f187b15f6ddd4f832684107ff29fe",
"divergences": [
{
"id": "incident-first-person",
Expand All @@ -728,8 +728,8 @@
{
"ported": ".claude/hooks/guard-tree-enum.selftest.sh",
"upstreamPath": ".claude/hooks/guard-tree-enum.selftest.sh",
"ref": "c074c57e452734821caf6f43329a20e873926f16",
"upstreamSha256": "6690dd40321eb00c8680f694fb3f48725c666291c524b668e6b161ca39d12aa9",
"ref": "7c2c5aedd94d7b0d94c91432bc607862e9c83c6c",
"upstreamSha256": "e19872688753c34743a0ee35cd0e9672d8d46a2773daa72928c19510d38c6a59",
"divergences": [
{
"id": "incident-first-person",
Expand Down
Loading