Skip to content
Merged
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
111 changes: 102 additions & 9 deletions .claude/inject-org-context.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
#!/usr/bin/env bash
# SessionStart hook — inject the bounded-systems canonical Claude context.
# Canonical source since .github#175: bounded-systems/.github -> claude/context.md
# (PUBLIC — the #491 audit found nothing private; the .github-private copy stays
# byte-identical until #494 retires it and remains the fallback below).
# (PUBLIC — the #491 audit found nothing private; the .github-private copy is a
# duplicate that remains a fallback below). Since infra#415 that public copy is
# reached through boot.bounded.tools/public-context.md rather than raw
# .githubusercontent.com — same bytes, one fewer host a session must reach. See
# step 0.
#
# THE TWO COPIES ARE NOT GUARANTEED IDENTICAL, and this header used to say they
# were "byte-identical until #494 retires it". Both halves were wrong (#581):
# #494 is a different subject (it retired PATHBASE_LEASE_KEY, closed 2026-08-16),
# and the copies diverged on 2026-08-15 when 50bc53d taught the three-door claim
# ladder in .github-private and never propagated here. For two days every session
# in the fleet was injected the OLD single-door convention — the one keycard#7
# proved unsound and #530 built claim-relay.yml to displace — because step 0
# below fetches THIS repo's copy and it was the stale one.
#
# A comment asserting an invariant is not a check (agentic-code-hygiene rule 3).
# So this script no longer assumes it: step L prefers a local checkout when the
# session has one, and SAYS SO OUT LOUD when two local copies disagree, which is
# the only moment a session can notice the drift that affects it.
# Fail OPEN but never SILENT: anything that goes wrong yields no context and one
# status line saying so — a degraded session must be able to tell (#491).
set -uo pipefail
Expand All @@ -11,13 +28,67 @@ command -v jq >/dev/null 2>&1 || exit 0
path='repos/bounded-systems/.github-private/contents/claude/context.md'
ctx=""

# 0) The public canonical copy — anonymous raw fetch, no token, no clone. This
# is the source that works in a cloud session with NOTHING attached; every
# fallback below was measured failing there on 2026-07-31, when the file
# lived only in the private repo.
if command -v curl >/dev/null 2>&1; then
# L) THE LOCAL CHECKOUT, when the session has one. Tried before any network
# source because a fetched copy can be OLDER than the tree the session was
# created from, and on 2026-08-15 it was: this session's own correct
# context.md sat two directories away on disk while step 0 pulled the stale
# one over the wire (#581). A session should not be taught a convention its
# own checkout contradicts.
#
# Root resolution mirrors org-repair.sh's, deliberately — never assume
# /home/user. .github-private is preferred when both are present because it
# is the copy boot-deploy.yml publishes into CONTEXT_KV, and empirically the
# one that gets maintained first.
#
# When both exist and DIFFER, that is the #581 defect recurring, and it is
# reported rather than silently resolved: the elected copy still loads (a
# session with context beats one without), but the divergence is named in
# the injected text so whoever reads it can see the org is inconsistent.
R="${CLAUDE_SESSION_ROOT:-}"
[ -f "$R/.github/.claude/boot.sh" ] 2>/dev/null || R="$PWD"
[ -f "$R/.github/.claude/boot.sh" ] || R="${PWD%/*}"
[ -f "$R/.github/.claude/boot.sh" ] || R=/home/user
priv="$R/.github-private/claude/context.md"
pub="$R/.github/claude/context.md"
drift=""
if [ -f "$priv" ] && [ -f "$pub" ] && ! cmp -s "$priv" "$pub"; then
drift="⚠ org context DRIFT: ${priv} and ${pub} differ. Using the .github-private copy. The .github copy is what a session WITHOUT a checkout is served, so bare sessions are getting the other text — see .github-private#581."
fi
if [ -f "$priv" ]; then
ctx="$(cat "$priv" 2>/dev/null || true)"
elif [ -f "$pub" ]; then
ctx="$(cat "$pub" 2>/dev/null || true)"
fi

# 0) The public canonical copy — anonymous, no token, no clone. This is the
# source that works in a cloud session with NOTHING attached; every fallback
# below was measured failing there on 2026-07-31, when the file lived only in
# the private repo.
#
# SERVED THROUGH THE BOOT WORKER since infra#415, not fetched from
# raw.githubusercontent.com directly. THE BYTES ARE THE SAME FILE: the Worker
# fetches bounded-systems/.github's claude/context.md from CLOUDFLARE'S
# network and streams it through verbatim — verified byte-identical against
# both the checkout and the raw upstream at deploy time (all three hashed to
# 99f98f39… on 2026-08-17). What changes is only which host THIS session
# contacts, and that is the entire point: this line was the last session-side
# use of raw.githubusercontent.com, and the only reason that host was still in
# the front-desk egress allowlist (.github-private#534 item 2).
#
# NOT the Worker's /context.md — that route is lease-gated and serves the
# .github-PRIVATE copy out of CONTEXT_KV, a different file that #581 recorded
# diverging from this one. /public-context.md is the public copy, which is
# what this hook has always fetched.
#
# NO raw.githubusercontent FALLBACK is kept, deliberately. A fallback to the
# host we are retiring would stop working the moment the allowlist entry is
# dropped, leaving dead code that reads like resilience; and the failure it
# would cover — the Worker being unreachable — is already covered by steps 1-3
# over github.com, a host that is not going anywhere. Fail open, loudly, per
# the message at the bottom.
if [ -z "$ctx" ] && command -v curl >/dev/null 2>&1; then
ctx="$(curl -fsSL --connect-timeout 5 --max-time 15 \
https://raw.githubusercontent.com/bounded-systems/.github/main/claude/context.md \
https://boot.bounded.tools/public-context.md \
2>/dev/null || true)"
fi

Expand Down Expand Up @@ -53,8 +124,30 @@ fi
# the silent variant made a context-less session indistinguishable from a
# healthy one, and the verbspec worker shipped work without knowing.
if [ -z "$ctx" ]; then
echo "org context NOT loaded — all sources failed (public raw, gh api, git clone, curl+token). Degraded mode: see the org stanza in CLAUDE.md."
echo "org context NOT loaded — all sources failed (boot.bounded.tools/public-context.md, gh api, git clone, curl+token). Degraded mode: see the org stanza in CLAUDE.md."
exit 0
fi

# One session start injects this block ONCE (#508, measured 2026-08-16): the
# attached repo's own SessionStart hook and the boot-installed dispatcher both
# run this script, in separate envelopes no in-process merge can reach — so the
# second run detects the first through a short-lived marker and stays silent.
# The predicate is "already INJECTED", never "other hook installed" — installed
# is not fired (#427, #506). Keyed by session id and aged 90s: sibling hooks in
# one start fire seconds apart, while distinct starts (create → resume, or the
# snapshot's --init-only pass → the first real session) are minutes apart or
# carry a different id. Marker only on SUCCESS: a degraded run writes nothing,
# so a later sibling that can reach the context still injects it.
m="/tmp/.bounded-org-context.${CLAUDE_CODE_SESSION_ID:-any}"
now="$(date +%s)"
mt="$(stat -c %Y "$m" 2>/dev/null || stat -f %m "$m" 2>/dev/null || echo 0)"
if [ -f "$m" ] && [ $((now - mt)) -lt 90 ]; then
exit 0
fi
touch "$m" 2>/dev/null || true
# The drift notice rides INSIDE the injected context, not on a bare stdout line:
# only additionalContext reaches the session, and a warning the session cannot
# read is the silent failure this script's #491 lesson is about.
[ -n "$drift" ] && ctx="${drift}"$'\n\n'"${ctx}"
jq -n --arg c "$ctx" \
'{hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:$c}}'
47 changes: 35 additions & 12 deletions .claude/org-repair.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,28 @@
# as a committed file, so .claude/settings.json can pre-approve exactly this
# invocation: `bash .claude/org-repair.sh` (#491). Canonical logic lives in
# bounded-systems/.github-private CLAUDE.md; adopt-claude-harness keeps this
# copy in sync. Verifies every byte against the dialog-recorded
# $ORG_BOOT_SHA256 and REFUSES rather than running anything unverified.
# copy in sync. Verifies every byte against a RESOLVED digest and REFUSES rather
# than running anything unverified.
#
# THE DIGEST SOURCE CHANGED, AND THIS SCRIPT WAS INERT FLEET-WIDE UNTIL IT WAS
# FIXED (2026-08-17, .github-private#534/#581). It used to REQUIRE
# $ORG_BOOT_SHA256 and exit 0 with "not a bounded-systems cloud session" when it
# was unset. That variable was RETIRED OUTRIGHT on 2026-08-16 evening
# (.github#192): the boot digest now rides the channel manifest, written only by
# the OIDC-pinned boot-manifest lane on main, and the front-desk dialog is down
# to a single variable (BS_ROUTES_CONFIG). Measured 2026-08-17 from a live
# front-desk session whose dialog matches the recorded union exactly: the dialog
# carries BS_ROUTES_CONFIG and nothing else, ORG_BOOT_SHA256 is unset, and this
# script printed "not a bounded-systems cloud session; nothing to do" and
# installed nothing — in a session that was exactly that. It exited 0 while
# doing so, so the failure was SILENT and every adopter had a dead step 1.
#
# The resolution order below matches CLAUDE.md's step-1 block, which was already
# corrected: legacy $ORG_BOOT_SHA256 honored first WHILE IT EXISTS, otherwise the
# channel manifest. With neither resolvable it refuses rather than falling
# through to an unverified file — `bash` only ever runs bytes that hashed to the
# resolved digest.
set -uo pipefail
[ -n "${ORG_BOOT_SHA256:-}" ] || {
echo "org-repair: ORG_BOOT_SHA256 unset — not a bounded-systems cloud session; nothing to do"
exit 0
}
R="${CLAUDE_SESSION_ROOT:-}" # resolve the checkout root — never assume /home/user
[ -f "$R/.github/.claude/boot.sh" ] || R="$PWD"
[ -f "$R/.github/.claude/boot.sh" ] || R="${PWD%/*}"
Expand All @@ -19,18 +34,26 @@ if cmp -s "$C/stop-hook-git-check.sh" "$R/.github/.claude/stop-hook-git-check.sh
echo "bootstrap in effect"
exit 0
fi
B="$R/.github/.claude/boot.sh" # attached checkout — same bytes, no egress
echo "$ORG_BOOT_SHA256 $B" | sha256sum -c --status - 2>/dev/null || {
F="curl -fsSL --retry 3 --retry-connrefused --retry-max-time 60 --connect-timeout 5 --max-time 30"
# Legacy variable first while it still exists anywhere, then the channel
# manifest (.github#192). The manifest is written only by the OIDC-pinned
# boot-manifest lane on main, so it is a trust source, not a convenience.
S="${ORG_BOOT_SHA256:-$($F https://boot.bounded.tools/channel/front-desk.json 2>/dev/null \
| sed -n 's/.*"boot":"\([0-9a-f]\{64\}\)".*/\1/p')}"
# The attached checkout is tried BEFORE the network on purpose: the repair must
# work when egress is down, which is exactly when a resumed session is most
# likely to be repairing by hand.
B="$R/.github/.claude/boot.sh"
{ [ -n "$S" ] && echo "$S $B" | sha256sum -c --status - 2>/dev/null; } || {
B=""
curl -fsSL --retry 3 --retry-connrefused --retry-max-time 60 --connect-timeout 5 \
--max-time 30 "https://boot.bounded.tools/$ORG_BOOT_SHA256.sh" -o /tmp/boot.sh \
&& echo "$ORG_BOOT_SHA256 /tmp/boot.sh" | sha256sum -c --status - && B=/tmp/boot.sh; }
[ -n "$S" ] && $F "https://boot.bounded.tools/$S.sh" -o /tmp/boot.sh \
&& echo "$S /tmp/boot.sh" | sha256sum -c --status - && B=/tmp/boot.sh; }
if [ -n "$B" ]; then
CLAUDE_SESSION_ROOT="$R" bash "$B"
D="$R/.github/.claude/session-start-dispatch.mjs"
[ -f "$D" ] || D=/opt/bounded-boot/session-start-dispatch.mjs
CLAUDE_SESSION_ROOT="$R" node "$D"
else
echo "org-repair: REFUSED — no copy matched \$ORG_BOOT_SHA256; installing nothing"
echo "org-repair: REFUSED — no verified copy (no digest source, or no bytes matched); installing nothing"
exit 1
fi
30 changes: 22 additions & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,16 +262,30 @@ deno task docs

1. **Bootstrap**: run `bash .claude/org-repair.sh` — pre-approved where this
repo's settings carry the org floor; generator-owned repos may still prompt.
Its only trust anchor is the dialog-recorded `$ORG_BOOT_SHA256` (typed by a
human in the environment dialog, never fetched): it executes a local or
fetched copy ONLY if the bytes hash to that value, and refuses otherwise.
It executes a local or fetched copy ONLY if the bytes hash to a resolved
digest, and refuses otherwise. The digest comes from the channel manifest
(written only by the OIDC-pinned `boot-manifest` lane on main), with the
legacy `$ORG_BOOT_SHA256` honored first while it still exists — that
variable was retired outright on 2026-08-16 (`.github`#192) and is absent
from the dialog, so the manifest is the live source.
`bootstrap in effect` → continue. `REFUSED` or a permission denial → stop
and report; do not work around it.
2. **Claim before working**: dispatch `claim-ticket.yml` in
`bounded-systems/.github` (workflow_dispatch: `repo`, `issue`, `claimant`),
then confirm the claim comment ON THE ISSUE names your claimant. Any
assignee or `claimed` label → someone else's. Window unreachable → claim by
hand (assign + comment) and say the window was down. No issue → open one.
2. **Claim before working** — doors, best reachable first (`#529`):
1. `claim-ticket.yml` in `bounded-systems/.github` (workflow_dispatch:
`repo`, `issue`, `claimant`) — the real door, lease-backed. Reachable
only from a session created with `.github` attached; mid-session
`add_repo` refuses it.
2. `claim-relay.yml` in `bounded-systems/.github-private`
(workflow_dispatch: `issue`, `claimant`) — for `.github-private` issues
when door 1 is unreachable. Bot-authored, run-backed record; it does
**not** authenticate the claimant (`#530`).
3. Hand-claim (assign + comment) — **last resort only**, when no door is
reachable. It provides **no exclusion** (keycard#7, `signerSelfAsserted`)
— a marker, not a claim; say plainly the window was down.

Whichever door: confirm the claim comment ON THE ISSUE names your claimant.
Any assignee or `claimed` label → someone else's; do not start. No issue →
open one and claim it.
3. **Degraded mode**: no "bounded-systems — Claude context" block in your
session context means the org context did not load. You may claim and work
THIS repo only — no org-level `[settings]`/`[org]` changes, no cross-repo
Expand Down
Loading