Cross-model doc review (Codex) caught that the headline cloud-persistence
claim was hollow and two docs over-claimed:
- attach now records repo_url from the adopted origin, so the no-daemon push
gate (repoPhaseComplete) recognizes the repo phase as done — WITHOUT this,
the per-turn/session-end pushes deferred forever after an attach, which is
the ONLY install path in a cloud sandbox (repo is refused there). Privacy is
still enforced at push time by the ladder.
- bootstrap verify's push_probe reads the shared per-root reader [D8], not the
legacy single file — a fresh v0.45.8 install no longer reports 'no push
recorded' when per-root status exists.
- Docs corrected to match code: runbook stamp → 0.45.8.0; README states the
per-turn cadence honestly (debounced local, next-turn failure notice);
GITHUB.md qualifies auto-push as Claude Code (Codex is pull); KEY_FILES says
repo/status use REST (not the full ladder) and drops the stale --push;
bootstrap.md hooks-location covers both carriers; README verbs surface says
seven. llms bundles regenerated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@@ -107,7 +107,7 @@ answers. Ask before anything destructive. You are not done until
`gbrain bootstrap verify` exits 0.
```
Everything from the Codex path applies — interview, identity from your own answers, local brain, private repo, keyless mode — plus Claude Code gets **per-turn context hooks**: your brain loads automatically into every prompt, and your work persists to your private repo after every turn (not just at session end — that covers the `/exit` case the harness never fires a session-end hook on), with a same-turn notice if a push ever fails. This works in a **Claude Code cloud session** too, not just on your laptop: verification falls back to pure git protocol when the sandbox blocks the GitHub API, and `gbrain bootstrap cloud-setup-script` prints the environment setup recipe. Restart the session after install and ask "what did I tell you my top jobs were?" — that's the moment it clicks. Full contract, security posture, cloud sandboxes, and uninstall: [docs/guides/bootstrap.md](docs/guides/bootstrap.md).
Everything from the Codex path applies — interview, identity from your own answers, local brain, private repo, keyless mode — plus Claude Code gets **per-turn context hooks**: your brain loads automatically into every prompt, and your work persists to your private repo on a per-turn cadence (debounced ~5 min locally, every turn in a cloud sandbox — this covers the `/exit` case the harness never fires a session-end hook on), with a notice on your next turn if a push ever fails. This works in a **Claude Code cloud session** too, not just on your laptop: verification falls back to pure git protocol when the sandbox blocks the GitHub API, and `gbrain bootstrap cloud-setup-script` prints the environment setup recipe. Restart the session after install and ask "what did I tell you my top jobs were?" — that's the moment it clicks. Full contract, security posture, cloud sandboxes, and uninstall: [docs/guides/bootstrap.md](docs/guides/bootstrap.md).
> **Prefer to make the repo yourself?** Create a new **empty** private repo **under your own GitHub account** (no README/.gitignore/license), clone it, open the clone in Claude Code (CLI or the desktop app's open-a-repo flow), and paste the same block — bootstrap adopts your empty repo instead of creating one. The repo must be empty and personal-account-owned; org-owned repos are refused.
@@ -131,7 +131,7 @@ The agent installs GBrain, creates the brain, asks for your API keys, loads the
### Lighter ways in
**Just want a memory for your coding agent — no identity, no repo.** Spin up a local brain and connect it in two commands — zero server, zero token, zero tunnel. `--surface verbs` gives your agent the five-verb memory protocol (`recall`, `remember`, `entity`, `synthesize`, `forget` — [MEMORY_VERBS v1](docs/protocol/MEMORY_VERBS_v1.md), frozen + additive-forever) instead of the full tool wall; drop the flag for every operation:
**Just want a memory for your coding agent — no identity, no repo.** Spin up a local brain and connect it in two commands — zero server, zero token, zero tunnel. `--surface verbs` gives your agent the seven-verb memory protocol (`recall`, `remember`, `entity`, `synthesize`, `forget`, plus `context_pack` + `delta` since v0.45.7 — [MEMORY_VERBS v1](docs/protocol/MEMORY_VERBS_v1.md), frozen + additive-forever) instead of the full tool wall; drop the flag for every operation:
```bash
gbrain init --pglite # 2-second local brain (no Docker)
User-facing contract: `docs/guides/bootstrap.md`. Runbook the paste block fetches:
`BOOTSTRAP_FOR_AGENTS.md` (root; carries a version stamp CI pins to VERSION).
- `src/commands/bootstrap.ts` — the `gbrain bootstrap {status,interview,render,repo,hooks,verify,uninstall,attach}` dispatcher. Engine-free everywhere except `verify` (which opens/closes its own engine — safe because verify runs with no live serve, before host registration). Mutating subcommands run under the workspace bootstrap lock; render is gated on interview `complete && confirmed` and hard-refuses when the workspace origin is a PUBLIC remote (identity files must never land in a public repo — the same template-door gate `status` enforces; unverifiable visibility warns and proceeds, treating the origin as public); the provider key routes to the 0600 config sink and never touches interview state; every subcommand appends a line to `<home>/bootstrap/install.jsonl`. Consent answers resolve FAIL-CLOSED: `consentAnswer` treats a hand-edited/unusable interview value (non-string, empty) as declined — loudly, with a re-record note — never falling through to a permissive bank default; `hooks` on Codex prints a corrective note when a persisted `project` MCP_SCOPE answer is found (raw state read, not the resolver — `codex mcp add` has no scope flag, registrations are always user-global) with safe clear instructions. `GBRAIN_BOOTSTRAP_ABORT_AFTER` is the deterministic kill-mid-phase test seam.
- `src/commands/bootstrap.ts` — the `gbrain bootstrap {status,interview,render,repo,hooks,verify,uninstall,attach,cloud-setup-script}` dispatcher. Engine-free everywhere except `verify` (which opens/closes its own engine — safe because verify runs with no live serve, before host registration).`cloud-setup-script` is a pure printer (prints `templates/bootstrap/cloud-setup-script.sh` for the cloud environment's setup step). Mutating subcommands run under the workspace bootstrap lock; render is gated on interview `complete && confirmed` and hard-refuses when the workspace origin is a PUBLIC remote (identity files must never land in a public repo — the same template-door gate `status` enforces; unverifiable visibility warns and proceeds, treating the origin as public); the provider key routes to the 0600 config sink and never touches interview state; every MUTATING subcommand appends a line to `<home>/bootstrap/install.jsonl` (the read-only `status` and `cloud-setup-script` do not log). `hooks` on Claude Code writes the committed carrier in a cloud sandbox (`writeCommittedClaudeHooks`) and the gitignored local file otherwise; `uninstall` tears down the durability wiring and removes this workspace's per-root push/debounce state. Consent answers resolve FAIL-CLOSED: `consentAnswer` treats a hand-edited/unusable interview value (non-string, empty) as declined — loudly, with a re-record note — never falling through to a permissive bank default; `hooks` on Codex prints a corrective note when a persisted `project` MCP_SCOPE answer is found (raw state read, not the resolver — `codex mcp add` has no scope flag, registrations are always user-global) with safe clear instructions. `GBRAIN_BOOTSTRAP_ABORT_AFTER` is the deterministic kill-mid-phase test seam.
- `src/core/bootstrap/format.ts` — `agent.json` manifest (format_version 1, provisional; `initialized` sentinel distinguishes a template clone from a bootstrapped workspace) + the machine-local install receipt (`<home>/bootstrap/receipt.json`) that proves THIS machine ran bootstrap; uninstall is keyed to the receipt, never the repo manifest. Atomic writes; `readManifest` never throws (typed states incl. conflict markers).
- `src/core/bootstrap/assets.ts` — every template + the question bank embedded via Bun `with { type: 'file' }` imports (the chunkers/code.ts pattern) so the compiled binary renders with no repo checkout; `DERIVED_TOKENS` (GITHUB_REPO_URL, CORPUS_RETENTION_DAYS) is the non-bank half of the template token set the CI bijection guard checks.
- `templates/bootstrap/` — the ten `{{TOKEN}}` identity templates (AGENTS/CLAUDE/SOUL/USER/MEMORY/HEARTBEAT/ACCESS_POLICY/GITHUB/memory-README/gitignore), `questions.json` (12 asked / 6 required; consent keys; `persist:false` sink keys), and `template-repo/` — the VENDORED deterministic render the release job diffs against before publishing the public template repo. Generic placeholder content only (privacy iron rule; CI-asserted).
- `src/core/bootstrap/interview.ts` — interview state at `<ws>/state/interview.json` (committed; multi-device re-render source). Read-back confirm hash: `--confirm` must present the hash of the exact answer set shown to the human, and ANY later answer change clears the confirmation — the single-batch self-confirm attack is structurally impossible. Set-time enforcement: length caps, reject-lists, allowed-lists, control-char strip, `{{` escaping. Conflict-markered files return agent-readable errors, not stack traces.
- `src/core/bootstrap/render.ts` — token substitution with interview values treated as data (line-leading `#`/`<!--`/fence escaping), hard-fail on unresolved tokens, never-clobber + timestamped backups on `--force`, blank-line collapse, byte floors scaled to answered count. `--minimal` is the deterministic placeholder mode the template-repo generator uses (byte-identical across runs; leaves required tokens as literal fill-me markers; writes `initialized:false`). `--only` never writes agent.json.
- `src/core/bootstrap/lock.ts` — the bootstrap-run mutex (atomic mkdir + pid liveness + age guard + ownership token; steal requires dead pid AND stale age) and the family's shared typed `BootstrapError` (GH_MISSING/GH_AUTH carry exit 2 = human action needed).
- `src/core/bootstrap/repo.ts` / `attach.ts` / `uninstall.ts` — private-repo lifecycle. `createPrivateRepo`: gh gates, slugified name probe, `gh repo create --private --source --push`, privacy verified via `gh api .private` (rate-limit/5xx is VERIFY_UNAVAILABLE, distinct from not-private) before any push, idempotency keyed off the remote URL. A pre-existing origin is adopted (disposition 'adopted') when the authed gh user owns it, there's no recorded `repo_url`, and it is SAFE — empty or already carrying our history (`assertAdoptableOrigin`; a foreign-content repo is refused `ORIGIN_NOT_EMPTY`, never a silent no-op); this is the create-repo-first path. Org-owned origins and anything else are refused and pointed at attach. Privacy is verified through the shared `repo-visibility.ts` ladder (REST → git-protocol fallback), so it keeps working when a cloud proxy blocks the GitHub API; a proxy-classed 403 yields a cloud-specific VERIFY_UNAVAILABLE message pointing at the attach flow. Inside a `cloud-sandbox` (execution-env), `createPrivateRepo` fails fast with `CLOUD_SANDBOX_REPO` before any `gh repo create` — a mid-session repo is never proxy-attached, so creation there is a dead end; the message routes to create-elsewhere + `bootstrap attach`. Repo-local git identity is set in both create and adopt paths before commit; `repo_url` is recorded only after a successful push. `attachWorkspace` (machine two): requires an `initialized` manifest, writes this machine's receipt, returns structured wiring steps. `uninstallWorkspace`: receipt-keyed, refuses under a live serve (read-only lock probe — never opens the engine), removes exactly receipt-recorded paths + marker-keyed host entries, keeps the brain unless `--delete-brain` AND bootstrap created it; never wholesale-deletes the gbrain home. All gh/git through an injectable ExecRunner seam.
- `src/core/bootstrap/repo.ts` / `attach.ts` / `uninstall.ts` — private-repo lifecycle. `createPrivateRepo`: gh gates, slugified name probe, `gh repo create --private --source` (the push happens separately via `ensureRemoteHasWorkspace`), privacy verified via REST `gh api repos/{o}/{r} --jq .private` (rate-limit/5xx is VERIFY_UNAVAILABLE, distinct from not-private; a proxy-classed 403 → a cloud-specific message) before any push, idempotency keyed off the remote URL. It shares repo-visibility.ts's URL parser + `isProxyBlocked403` classifier but runs REST directly (not the full ladder — creation needs the authed-owner REST answer). A pre-existing origin is adopted (disposition 'adopted') when the authed gh user owns it, there's no recorded `repo_url`, and it is SAFE — empty or already carrying our history (`assertAdoptableOrigin`; a foreign-content repo is refused `ORIGIN_NOT_EMPTY`, never a silent no-op); this is the create-repo-first path. Org-owned origins and anything else are refused and pointed at attach. Inside a `cloud-sandbox` (execution-env), `createPrivateRepo` fails fast with `CLOUD_SANDBOX_REPO` before any `gh repo create` — a mid-session repo is never proxy-attached, so creation there is a dead end; the message routes to create-elsewhere + `bootstrap attach`. Repo-local git identity is set in both create and adopt paths before commit; `repo_url` is recorded only after a successful push. `attachWorkspace` (machine two, and the ONLY install path in a cloud sandbox since `repo` is refused there): requires an `initialized` manifest, writes this machine's receipt — recording `repo_url` from the adopted github origin so the no-daemon push gate (`repoPhaseComplete`) recognizes the repo phase as done and the per-turn/session-end pushes actually fire (privacy is still enforced at push time by the ladder) — and returns structured wiring steps. `uninstallWorkspace`: receipt-keyed, refuses under a live serve (read-only lock probe — never opens the engine), removes exactly receipt-recorded paths + marker-keyed host entries, keeps the brain unless `--delete-brain` AND bootstrap created it; never wholesale-deletes the gbrain home. All gh/git through an injectable ExecRunner seam.
- `src/core/bootstrap/hooks.ts` + `host-specs.ts` — host wiring. `host-specs.ts` is the ONE module owning host-format assumptions (dated spec targets with verifiedAt + doc references: claude-code hooks/settings shapes incl. the 10,000-char hook-output cap; codex mcp-add argv; no-TOML-writer-in-v1 decision recorded). `writeClaudeHooks` does a structural JSON merge into `.claude/settings.local.json` keyed by a `_gbrain` marker — foreign hooks and permissions survive, re-runs dedupe, broken JSON is backed up loudly. `writeCommittedClaudeHooks` [D12] is the CLOUD carrier: a fresh cloud clone never sees the gitignored local file and hook config snapshots at session start, so cloud installs write the repo-COMMITTED `.claude/settings.json` with a PATH-resolved fail-open command (`command -v gbrain … || exit 0`; no absolute paths — the file travels between machines). The writers enforce a dedupe invariant — an event carried by the committed file is skipped by the local writer and vice-versa, matched on the EXACT portable-command shape (a bare marker + `gbrain hook` substring is spoofable), so one event never fires from both; `removeClaudeHooks` cleans both carriers; GBRAIN_HOME is refused in the committed carrier (machine-specific). `registerClaudeMcp`/`registerCodexMcp` build argv only (Claude Code takes `--scope`, project default; Codex has no scope flag — `codex mcp add` is always user-global; `-e`/`--env GBRAIN_SOURCE` so MCP writes land in the workspace source, and `serve --surface full` pinned so a pre-existing `mcp_surface: verbs` config row can't silently narrow the bootstrap op surface).
- `src/commands/hook.ts` — engine-free `gbrain hook {session-start,user-prompt,stop,session-end}` (zero engine modules in the import graph; a hook must NEVER contend for the PGLite writer lock). user-prompt: stdin hook JSON → transcript-path confinement → last-4-turns window + cross-turn dedupe (the transcript's `hook_additional_context` attachments — the blocks WE previously injected — ride `priorContextText`, deduplicated and capped at `PRIOR_CONTEXT_MAX_BYTES` (32KB, so the advisory payload can never blow the IPC message cap; one oversized block is skipped without evicting smaller ones), so a page is volunteered once per session, not once per mention; structured extraction only, never raw-turn substring matching) → IPC turn_context (with a feedback-loop `channel`, `--harness <claude-code|codex>`, default claude-code) → `hookSpecificOutput.additionalContext` under an 800ms self-deadline; every path fails open (exit 0, empty stdout) with a typed reason in the heartbeat. Listed in cli.ts's `STARTUP_HOOK_SKIP_COMMANDS` (per-prompt invocations must never spawn a detached check-update child; membership is pinned by a source grep — the runtime path no-ops under NODE_ENV=test). session-start: file-plane digest (allowlisted MEMORY.md sections, push staleness, prior failures) + crashed-session recovery push gated on an initialized manifest. session-end: confined full-transcript parse → redacted corpus write (session-id filename dedup, retention prune) → parser-drift detection (`bytes>0 && turns==0` is loud) → best-effort workspace push. session-start recovery + session-end pushes run in a DETACHED child so the hook returns immediately (a synchronous inline push previously blocked harness startup on a dirty tree); the corpus write is atomic and clears the stale ingested/in-progress sidecars so a resumed session re-ingests its appended transcript. stop: also runs a per-turn debounced detached push [D3] (`stopPushIfDue` — per-workspace-root debounce state, 5-min default local + ephemeral-container / every turn in a cloud-sandbox, a failing status bypasses to a 60s retry floor, same repo-phase security gate as session-end), closing the `/exit` gap (SessionEnd never fires on `/exit`) and the VM-reclaim gap; `GBRAIN_STOP_PUSH=0` disables it, `GBRAIN_STOP_PUSH_DEBOUNCE_MIN` / config `hooks.stop_push_debounce_min` tune it. user-prompt also surfaces a failing background push [D5/D19] as a ≤300-char banner on BOTH `additionalContext` (model) and `systemMessage` (human), announced once per failure and re-fired at most every 30 min, with the reason sanitized before it reaches any model-visible surface. Heartbeat JSONL is counters/reasons only by construction; `readHeartbeatTail` feeds doctor. `GBRAIN_HOOKS=0` kills all events.
- `src/commands/hook.ts` — engine-free `gbrain hook {session-start,user-prompt,stop,session-end,compact}` (zero engine modules in the import graph; a hook must NEVER contend for the PGLite writer lock). user-prompt: stdin hook JSON → transcript-path confinement → last-4-turns window + cross-turn dedupe (the transcript's `hook_additional_context` attachments — the blocks WE previously injected — ride `priorContextText`, deduplicated and capped at `PRIOR_CONTEXT_MAX_BYTES` (32KB, so the advisory payload can never blow the IPC message cap; one oversized block is skipped without evicting smaller ones), so a page is volunteered once per session, not once per mention; structured extraction only, never raw-turn substring matching) → IPC turn_context (with a feedback-loop `channel`, `--harness <claude-code|codex>`, default claude-code) → `hookSpecificOutput.additionalContext` under an 800ms self-deadline; every path fails open (exit 0, empty stdout) with a typed reason in the heartbeat. Listed in cli.ts's `STARTUP_HOOK_SKIP_COMMANDS` (per-prompt invocations must never spawn a detached check-update child; membership is pinned by a source grep — the runtime path no-ops under NODE_ENV=test). session-start: file-plane digest (allowlisted MEMORY.md sections, push staleness, prior failures) + crashed-session recovery push gated on an initialized manifest. session-end: confined full-transcript parse → redacted corpus write (session-id filename dedup, retention prune) → parser-drift detection (`bytes>0 && turns==0` is loud) → best-effort workspace push. session-start recovery + session-end pushes run in a DETACHED child so the hook returns immediately (a synchronous inline push previously blocked harness startup on a dirty tree); the corpus write is atomic and clears the stale ingested/in-progress sidecars so a resumed session re-ingests its appended transcript. stop: also runs a per-turn debounced detached push [D3] (`stopPushIfDue` — per-workspace-root debounce state, 5-min default local + ephemeral-container / every turn in a cloud-sandbox, a failing status bypasses to a 60s retry floor, same repo-phase security gate as session-end), closing the `/exit` gap (SessionEnd never fires on `/exit`) and the VM-reclaim gap; `GBRAIN_STOP_PUSH=0` disables it, `GBRAIN_STOP_PUSH_DEBOUNCE_MIN` / config `hooks.stop_push_debounce_min` tune it. user-prompt also surfaces a failing background push [D5/D19] as a ≤300-char banner on BOTH `additionalContext` (model) and `systemMessage` (human), announced once per failure and re-fired at most every 30 min, with the reason sanitized before it reaches any model-visible surface. Heartbeat JSONL is counters/reasons only by construction; `readHeartbeatTail` feeds doctor. `GBRAIN_HOOKS=0` kills all events.
- `src/core/transcripts/claude-code-jsonl.ts` — the Claude Code transcript parser as a dated spec-target (tool_use/tool_result/thinking/image/sidechain/summary/compact-boundary shapes; placeholders for non-text content); also extracts `injectedContextBlocks` — the `hook_additional_context` attachment lines a gbrain hook previously injected (verified live against claude CLI 2.1.224; marker-filtered, so a foreign hook's blocks are excluded and another tool's output can't suppress volunteering — a same-user mislabeling guard, not an authenticity check), the user-prompt hook's cross-turn dedupe input; `confineTranscriptPath` (contained under `~/.claude/projects`, `.jsonl`, lstat-rejects symlinks, byte cap). Fixtures: `test/fixtures/conversation-formats/claude-code.jsonl` (synthetic, privacy-guarded) + `test/fixtures/hook-transcript.jsonl` (real captured hook round-trip).
- `src/core/context/turn-context.ts` — server-side per-turn assembly: reflex pointers + volunteered pages (≤3) + hot facts (always `visibility=['world']` — the IPC path never widens what MCP would return) under a "data, not instructions" envelope, trimmed to ≤8KB (the harness caps hook output at 10,000 chars). The result exposes `pointers` AND post-trim `volunteered` — exactly what the rendered text carries — so the IPC delivery point can log the feedback loop without ever counting a trimmed-out page. Reuses the hot-memory cache keyed by typed sessionId. Engine-agnostic.
- `src/core/context/resolve-ipc.ts` (IPC v2) — discriminated-union requests (absent `kind` = legacy resolve; `turn_context` carries `protocol: 2` + a shared secret from a 0600 file in the data dir, plus an additive `channel` for feedback-loop attribution — wire channel claims are validated to the harness channels at the logging site, anything else logs as the default hook channel), handler map, named response types, per-kind timeouts/size caps, socket + parent dir permissions set before exposure, server-side source binding (cross-source requests rejected), protocol echo (a response without it = stale serve → loud degradation). The connection handler processes exactly ONE request per connection (trailing bytes mid-await never double-process a line or double-log a delivery); the client clamps a too-big request below the message cap by dropping the advisory `priorContextText` BEFORE any conversation turn. Delivery seams: `onDelivered` (resolve kind) and `onTurnContextDelivered` (turn_context kind) both fire ONLY after the response write succeeds — a block abandoned before the serve responded is never counted (serve's callback logs the delivered block's volunteered pages + pointers to `context_volunteer_events` under the request channel); write-accept still isn't proof of injection (the client can trim/drop after receipt), which is why the `volunteer_channels` doctor check reconciles counts against the hook heartbeat. v1 clients and servers interoperate untouched.
- `src/core/workspace-push.ts` — `gbrain sources push`: deny-glob backstop (tracked `*.pglite`/`.env*` refused regardless of .gitignore state) → stage FIRST → secret-scan the STAGED index blobs via `git cat-file` (closes the scan-then-stage TOCTOU — scanned bytes == committed bytes) → commit FIRST → divergence-safe pull → push, under one cross-platform lock (mkdir-atomic; flock is not a dependency — macOS). The pre-push secret gate FAILS CLOSED: an unreadable, oversized (> `PUSH_MAX_SCAN_BYTES`), or otherwise unscannable staged blob returns `blocked_unscannable` (nothing committed) instead of sailing through — only a confirmed staged deletion is skipped, and binary/NUL-sniffed blobs are scanned anyway. Statuses map to exit codes in `src/commands/sources.ts`: `pushed`/`skipped_in_flight` → 0; `blocked_secrets`/`blocked_tracked_deny`/`blocked_unscannable`/`refused_visibility` → 5; `pull_conflict`/`push_failed`/other → 1. The privacy gate runs the shared `repo-visibility.ts` ladder (REST → git protocol) and refuses proven-public AND unverifiable remotes (never fail-open). Three escape hatches for self-hosted git you trust, each warns and downgrades ONLY `unverifiable` (never a proven-public push): CLI `--allow-unverified-remote` > env `GBRAIN_ALLOW_UNVERIFIED_REMOTE=1` > file-plane config `push.allow_unverified_remote` (the only channel a detached hook child can read). Pushes even on clean trees; writes per-workspace status `<home>/bootstrap/push-status-<roothash>.json` (a shared `readPushStatuses`/`summarizePushStatuses` reader; one workspace's success can't mask another's failure, and records for a since-deleted root are filtered). Parent-repo-aware (a source may be a subdirectory of the workspace repo).
- `src/core/gbrain-home.ts` — the single GBRAIN_HOME resolution choke point (delegates to config's parent-dir semantics; 0700 on create) — durability, push, hooks, and bootstrap all route through it so home semantics cannot drift.
- `src/core/execution-env.ts` — the third bootstrap axis (harness × engine × ENVIRONMENT). `detectExecutionEnvironment(signals?)` returns `local | cloud-sandbox | ephemeral-container` from injected signals: `CLAUDE_CODE_REMOTE=true` (official, never local) or a `cse_`-prefixed remote-session id or a credential-injecting proxy (`GH_TOKEN`/`GITHUB_TOKEN === 'proxy-injected'`, or an anthropic-egress `https_proxy`) → cloud-sandbox; RENDER/RAILWAY/FLY / `/.dockerenv` → ephemeral-container. `isCredentialInjectingProxy(env?)` is the exported proxy check (load-bearing for the repo-visibility ambiguity rule). `binaryOnPath(name)` moved here (live-PATH `Bun.which`) and is re-exported by `bootstrap/status.ts`; autopilot's `detectInstallTarget()` routes its ephemeral branch through the shared detector. Pure + signal-injected. Tests: `test/execution-env.test.ts`.
- `src/core/repo-visibility.ts` — ONE repo-privacy verdict for every consumer (workspace-push gate, bootstrap repo/verify/status), replacing three drifted probes. `verifyRepoVisibility({originUrl, repoDir, runner, fetchImpl, env})` runs a fail-closed ladder: REST (`gh api repos/{o}/{r} --jq .private` — NEVER `gh repo view`/GraphQL, which cloud proxies pin) → authed `git ls-remote` (SSRF flags via `durableSsrfFlags` + `--end-of-options`) → an anonymous smart-HTTP probe (`redirect:'manual'`, userinfo stripped). A 200 counts PUBLIC only with git-advertisement proof (content-type or pkt-line prefix); a 401/404 counts PRIVATE only with origin attribution (github.com needs `x-github-request-id` — a bare `WWW-Authenticate` is RFC-mandated on every 401 and a middlebox 401s identically, so it is NOT proof; non-github origins have no trustable signal → `unverifiable`). Everything else → `unverifiable`. `classifyGh403`/`isProxyBlocked403` distinguish a real GitHub 403 (JSON with `message`+`documentation_url`) from a sandbox-proxy 403. `parseGithubOwnerRepo` is the canonical URL parser (bootstrap/repo + hook re-adapt it). Private verdicts cache 1h (private-only, per-origin, userinfo-stripped key) at `<home>/bootstrap/visibility-cache.json`. Engine-free; every IO seam injected. Tests: `test/repo-visibility.test.ts`.
- `src/core/repo-visibility.ts` — ONE repo-privacy verdict shared by every consumer, replacing three drifted probes. The FULL ladder runs in `workspace-push.ts` (the push gate) and `bootstrap/verify.ts` (`checkRepoPrivacy`); `bootstrap/repo.ts` and `bootstrap/status.ts` reuse its URL parser + 403 classifier but call REST directly (both are sync-by-contract: repo creation needs the authed-owner answer, status runs inside the phase-detect chain). `verifyRepoVisibility({originUrl, repoDir, runner, fetchImpl, env})` runs a fail-closed ladder: REST (`gh api repos/{o}/{r} --jq .private` — NEVER `gh repo view`/GraphQL, which cloud proxies pin) → authed `git ls-remote` (SSRF flags via `durableSsrfFlags` + `--end-of-options`) → an anonymous smart-HTTP probe (`redirect:'manual'`, userinfo stripped). A 200 counts PUBLIC only with git-advertisement proof (content-type or pkt-line prefix); a 401/404 counts PRIVATE only with origin attribution (github.com needs `x-github-request-id` — a bare `WWW-Authenticate` is RFC-mandated on every 401 and a middlebox 401s identically, so it is NOT proof; non-github origins have no trustable signal → `unverifiable`). Everything else → `unverifiable`. `classifyGh403`/`isProxyBlocked403` distinguish a real GitHub 403 (JSON with `message`+`documentation_url`) from a sandbox-proxy 403. `parseGithubOwnerRepo` is the canonical URL parser (bootstrap/repo + hook re-adapt it). Private verdicts cache 1h (private-only, per-origin, userinfo-stripped key) at `<home>/bootstrap/visibility-cache.json`. Engine-free; every IO seam injected. Tests: `test/repo-visibility.test.ts`.
- `src/core/bootstrap/verify.ts` + `status.ts` — verify is the definition of done: fail-soft check suite over the REAL write path (put_page op → write-through file under `brain/` → in-process sweep → graph floor via link tables → recall), the keyless magic-moment check (`## Facts` fence → zero-LLM reconciliation → world-visibility read-back), source_id collision resolution (as the one bootstrap subcommand holding an engine: a manifest `source_id` already registered to a DIFFERENT checkout → derives a stable `workspace-<8char-path-hash>`, persists it to agent.json, names the re-register steps — every consumer reads `manifest.source_id`), token sweep, byte floors, secret scan, deny globs, repo privacy, hooks smoke (in-process IPC), capability report, first-run tour; snapshots kept last-5 under `<home>/bootstrap/`. status owns the ordered PHASES list (the runbook defers to it), artifact-first detection, install.jsonl, the runbook version-stamp skew check, and the support blob doctor/agents relay verbatim.
- `src/core/bootstrap/template-repo.ts` + `scripts/generate-template-repo.ts` — deterministic public-template generation (render `--minimal` + placeholder manifest + stamped README); published only by the release workflow after diffing against the vendored tree.
- `scripts/check-bootstrap-tag.sh` / `scripts/check-bootstrap-templates.sh` — CI guards: sanctioned distribution ref only (`latest-stable`; the release job advances it after assets publish) + runbook stamp == VERSION; template↔question-bank token bijection + placeholder-only assertion + offline generator↔vendored byte-diff + runbook-phase↔status.ts consistency + harness-scoping counter-signal pins (the MCP-scope consent is Claude Code only: the runbook must carry the Codex "Do NOT offer an MCP scope choice" bullet and the "Claude Code only" consent scoping, and the bank's MCP_SCOPE question must start with `(Claude Code only` with `phase: interview` — tripwires against deleting the load-bearing prose, not placement proofs). Both skip gracefully when their subjects are absent.
@@ -1686,7 +1686,7 @@ answers. Ask before anything destructive. You are not done until
`gbrain bootstrap verify` exits 0.
```
Everything from the Codex path applies — interview, identity from your own answers, local brain, private repo, keyless mode — plus Claude Code gets **per-turn context hooks**: your brain loads automatically into every prompt, and your work persists to your private repo after every turn (not just at session end — that covers the `/exit` case the harness never fires a session-end hook on), with a same-turn notice if a push ever fails. This works in a **Claude Code cloud session** too, not just on your laptop: verification falls back to pure git protocol when the sandbox blocks the GitHub API, and `gbrain bootstrap cloud-setup-script` prints the environment setup recipe. Restart the session after install and ask "what did I tell you my top jobs were?" — that's the moment it clicks. Full contract, security posture, cloud sandboxes, and uninstall: [docs/guides/bootstrap.md](docs/guides/bootstrap.md).
Everything from the Codex path applies — interview, identity from your own answers, local brain, private repo, keyless mode — plus Claude Code gets **per-turn context hooks**: your brain loads automatically into every prompt, and your work persists to your private repo on a per-turn cadence (debounced ~5 min locally, every turn in a cloud sandbox — this covers the `/exit` case the harness never fires a session-end hook on), with a notice on your next turn if a push ever fails. This works in a **Claude Code cloud session** too, not just on your laptop: verification falls back to pure git protocol when the sandbox blocks the GitHub API, and `gbrain bootstrap cloud-setup-script` prints the environment setup recipe. Restart the session after install and ask "what did I tell you my top jobs were?" — that's the moment it clicks. Full contract, security posture, cloud sandboxes, and uninstall: [docs/guides/bootstrap.md](docs/guides/bootstrap.md).
> **Prefer to make the repo yourself?** Create a new **empty** private repo **under your own GitHub account** (no README/.gitignore/license), clone it, open the clone in Claude Code (CLI or the desktop app's open-a-repo flow), and paste the same block — bootstrap adopts your empty repo instead of creating one. The repo must be empty and personal-account-owned; org-owned repos are refused.
@@ -1710,7 +1710,7 @@ The agent installs GBrain, creates the brain, asks for your API keys, loads the
### Lighter ways in
**Just want a memory for your coding agent — no identity, no repo.** Spin up a local brain and connect it in two commands — zero server, zero token, zero tunnel. `--surface verbs` gives your agent the five-verb memory protocol (`recall`, `remember`, `entity`, `synthesize`, `forget` — [MEMORY_VERBS v1](docs/protocol/MEMORY_VERBS_v1.md), frozen + additive-forever) instead of the full tool wall; drop the flag for every operation:
**Just want a memory for your coding agent — no identity, no repo.** Spin up a local brain and connect it in two commands — zero server, zero token, zero tunnel. `--surface verbs` gives your agent the seven-verb memory protocol (`recall`, `remember`, `entity`, `synthesize`, `forget`, plus `context_pack` + `delta` since v0.45.7 — [MEMORY_VERBS v1](docs/protocol/MEMORY_VERBS_v1.md), frozen + additive-forever) instead of the full tool wall; drop the flag for every operation:
```bash
gbrain init --pglite # 2-second local brain (no Docker)
if(origin)return{id,ok: true,warn: true,detail:'origin exists but no push recorded yet — run `gbrain sources push` once to prove the persistence path'};
test('attach steps are the ordered machine-2 todo list; re-attach preserves the same-workspace receipt',()=>{
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.