diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac0fde5d..e1474c34 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,16 @@ new version heading in the same commit.
## [Unreleased]
+## [0.448.6] - 2026-09-22
+### Fixed
+- **An agent's git commands can no longer reach the Agentric checkout.** An agent's folder usually isn't a
+ repo, so git walks up, and where the data home sits inside the product checkout it lands in agent-os
+ itself. On globex an engineer run did `cd ` followed, unchained, by
+ `git remote set-url origin …client-app` + `git reset --hard`. The cd failed, and the product's own source
+ was replaced by another repo (`terminal/` gone, so every later session crashed on launch, for 80 minutes).
+ Every launch now exports `GIT_CEILING_DIRECTORIES` = the agents folder. Repos inside an agent's folder
+ are unaffected. Pinned by `scripts/git-ceiling-test.cjs`.
+
## [0.448.5] - 2026-09-22
### Fixed
- **Two synchronous paths could freeze a whole tenant's server.** Both hit globex on 2026-09-22: `/health`
diff --git a/package-lock.json b/package-lock.json
index 7901ff19..0e40f818 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "agent-os",
- "version": "0.448.5",
+ "version": "0.448.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "agent-os",
- "version": "0.448.5",
+ "version": "0.448.6",
"license": "MIT",
"bin": {
"agent-os": "bin/agent-os"
diff --git a/package.json b/package.json
index ac3e1226..baaab297 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "agent-os",
- "version": "0.448.5",
+ "version": "0.448.6",
"description": "A generic, governed operating system for running autonomous agents safely across brands. Ships with a local web console.",
"license": "MIT",
"type": "commonjs",
@@ -27,7 +27,7 @@
"check-deps": "bash scripts/install-deps.sh --check",
"dev": "ts-node src/cli.ts serve",
"demo:dev": "ts-node src/demo.ts",
- "test:governance": "node scripts/version-sync-test.cjs && node scripts/event-loop-freeze-test.cjs && node scripts/governance-conformance.cjs && node scripts/tier-a-policy-test.cjs && node scripts/policy-baseline-test.cjs && node scripts/heredoc-intent-test.cjs && node scripts/capability-registry-test.cjs && node scripts/composio-envelope-test.cjs && node scripts/composio-identity-test.cjs && node scripts/idle-reaper-test.cjs && node scripts/dm-continuity-test.cjs && node scripts/telegram-dm-lane-test.cjs && node scripts/cli-link-origin-test.cjs && node scripts/alert-staleness-test.cjs && node scripts/run-as-identity-test.cjs && node scripts/email-identity-guard-test.cjs && node scripts/deps-freshness-test.cjs && node scripts/runtime-account-test.cjs && node scripts/runtime-account-misattribution-test.cjs && node scripts/runtime-usage-refresh-test.cjs && node scripts/keychain-credential-test.cjs && node scripts/credential-preflight-test.cjs && node scripts/capacity-queue-test.cjs && node scripts/runtime-login-test.cjs && node scripts/rotate-on-reload-test.cjs && node scripts/headless-resumable-test.cjs && node scripts/session-revive-gates-test.cjs && node scripts/session-pause-test.cjs && node scripts/make-live-lock-test.cjs && node scripts/claude-config-seed-test.cjs && node scripts/claude-config-isolation-test.cjs && node scripts/output-style-test.cjs && node scripts/session-cost-test.cjs && node scripts/chain-model-test.cjs && node scripts/task-workers-test.cjs && node scripts/tuning-patch-test.cjs && node scripts/task-runs-test.cjs && node scripts/task-pr-links-test.cjs && node scripts/task-draft-delete-test.cjs && node scripts/task-discussion-delivery-test.cjs && node scripts/task-resume-test.cjs && node scripts/task-unblock-test.cjs && node scripts/audience-session-access-test.cjs && node scripts/warm-chat-test.cjs && node scripts/poke-warm-caller-test.cjs && node scripts/wakeup-queue-test.cjs && node scripts/stranded-human-stop-test.cjs && node scripts/inject-submit-test.cjs && node scripts/blocked-routing-test.cjs && node scripts/self-dispatch-guard-test.cjs && node scripts/task-proposals-test.cjs && node scripts/npm-boundary-test.cjs && node scripts/agent-edit-guard-test.cjs && node scripts/per-agent-context-test.cjs && node scripts/goal-update-guard-test.cjs && node scripts/insights-signal-test.cjs && node scripts/outcome-derivation-test.cjs && node scripts/episode-quality-test.cjs && node scripts/memory-upkeep-test.cjs && node scripts/automem-health-test.cjs && node scripts/memory-store-switch-test.cjs && node scripts/memory-preload-test.cjs && node scripts/turn-lifecycle-test.cjs && node scripts/resume-seed-test.cjs && node scripts/outcome-vocabulary-test.cjs && node scripts/skill-presets-test.cjs && node scripts/skill-edit-proposal-test.cjs && node scripts/notify-hook-route-test.cjs && node scripts/review-notify-test.cjs && node scripts/turn-idle-background-guard-test.cjs && node scripts/waiting-brief-test.cjs && node scripts/runtime-death-alert-test.cjs && node scripts/github-per-member-test.cjs && node scripts/github-multi-org-test.cjs && node scripts/card-measurement-test.cjs && node scripts/scheduler-admission-test.cjs && node scripts/tick-liveness-test.cjs && node scripts/audit-mirror-test.cjs && node scripts/request-metrics-test.cjs && node scripts/tool-usage-test.cjs && node scripts/sessions-list-perf-test.cjs && node scripts/summarizer-degradation-test.cjs && node scripts/agent-history-scope-test.cjs && node scripts/webhook-ingress-test.cjs && node scripts/slack-content-filter-test.cjs && node scripts/slack-ingress-test.cjs && node scripts/discord-ingress-test.cjs && node scripts/chat-attachments-test.cjs && node scripts/clickup-task-bridge-test.cjs && node scripts/agentric-commands-test.cjs && node scripts/whats-new-test.cjs && node scripts/opencode-gate-test.cjs && node scripts/protected-path-guard-test.cjs && node scripts/attach-grace-test.cjs && node scripts/attach-file-liveness-test.cjs && node scripts/feed-smoke.cjs && node scripts/activity-classify-test.cjs && node scripts/goal-room-test.cjs && node scripts/secret-rotation-test.cjs && node scripts/update-watch-test.cjs && node scripts/runtime-update-watch-test.cjs && node scripts/setup-wizard-test.cjs && node scripts/md-pdf-test.cjs && node scripts/proposal-surfacing-test.cjs && node scripts/process-janitor-test.cjs && node scripts/detached-work-steer-test.cjs && node scripts/statusline-install-test.cjs && node scripts/docs-create-agent-test.cjs && node scripts/agent-stats-rollup-test.cjs && node scripts/task-discussion-rollup-test.cjs && node scripts/session-insights-stamp-test.cjs && node scripts/loop-stall-attribution-test.cjs && node scripts/session-progress-test.cjs && node scripts/drift-nudge-test.cjs && node scripts/goal-metric-review-test.cjs && node scripts/capability-gap-test.cjs && node scripts/workflow-proposal-test.cjs && node scripts/automation-edit-proposal-test.cjs && node scripts/app-card-close-test.cjs",
+ "test:governance": "node scripts/version-sync-test.cjs && node scripts/event-loop-freeze-test.cjs && node scripts/git-ceiling-test.cjs && node scripts/governance-conformance.cjs && node scripts/tier-a-policy-test.cjs && node scripts/policy-baseline-test.cjs && node scripts/heredoc-intent-test.cjs && node scripts/capability-registry-test.cjs && node scripts/composio-envelope-test.cjs && node scripts/composio-identity-test.cjs && node scripts/idle-reaper-test.cjs && node scripts/dm-continuity-test.cjs && node scripts/telegram-dm-lane-test.cjs && node scripts/cli-link-origin-test.cjs && node scripts/alert-staleness-test.cjs && node scripts/run-as-identity-test.cjs && node scripts/email-identity-guard-test.cjs && node scripts/deps-freshness-test.cjs && node scripts/runtime-account-test.cjs && node scripts/runtime-account-misattribution-test.cjs && node scripts/runtime-usage-refresh-test.cjs && node scripts/keychain-credential-test.cjs && node scripts/credential-preflight-test.cjs && node scripts/capacity-queue-test.cjs && node scripts/runtime-login-test.cjs && node scripts/rotate-on-reload-test.cjs && node scripts/headless-resumable-test.cjs && node scripts/session-revive-gates-test.cjs && node scripts/session-pause-test.cjs && node scripts/make-live-lock-test.cjs && node scripts/claude-config-seed-test.cjs && node scripts/claude-config-isolation-test.cjs && node scripts/output-style-test.cjs && node scripts/session-cost-test.cjs && node scripts/chain-model-test.cjs && node scripts/task-workers-test.cjs && node scripts/tuning-patch-test.cjs && node scripts/task-runs-test.cjs && node scripts/task-pr-links-test.cjs && node scripts/task-draft-delete-test.cjs && node scripts/task-discussion-delivery-test.cjs && node scripts/task-resume-test.cjs && node scripts/task-unblock-test.cjs && node scripts/audience-session-access-test.cjs && node scripts/warm-chat-test.cjs && node scripts/poke-warm-caller-test.cjs && node scripts/wakeup-queue-test.cjs && node scripts/stranded-human-stop-test.cjs && node scripts/inject-submit-test.cjs && node scripts/blocked-routing-test.cjs && node scripts/self-dispatch-guard-test.cjs && node scripts/task-proposals-test.cjs && node scripts/npm-boundary-test.cjs && node scripts/agent-edit-guard-test.cjs && node scripts/per-agent-context-test.cjs && node scripts/goal-update-guard-test.cjs && node scripts/insights-signal-test.cjs && node scripts/outcome-derivation-test.cjs && node scripts/episode-quality-test.cjs && node scripts/memory-upkeep-test.cjs && node scripts/automem-health-test.cjs && node scripts/memory-store-switch-test.cjs && node scripts/memory-preload-test.cjs && node scripts/turn-lifecycle-test.cjs && node scripts/resume-seed-test.cjs && node scripts/outcome-vocabulary-test.cjs && node scripts/skill-presets-test.cjs && node scripts/skill-edit-proposal-test.cjs && node scripts/notify-hook-route-test.cjs && node scripts/review-notify-test.cjs && node scripts/turn-idle-background-guard-test.cjs && node scripts/waiting-brief-test.cjs && node scripts/runtime-death-alert-test.cjs && node scripts/github-per-member-test.cjs && node scripts/github-multi-org-test.cjs && node scripts/card-measurement-test.cjs && node scripts/scheduler-admission-test.cjs && node scripts/tick-liveness-test.cjs && node scripts/audit-mirror-test.cjs && node scripts/request-metrics-test.cjs && node scripts/tool-usage-test.cjs && node scripts/sessions-list-perf-test.cjs && node scripts/summarizer-degradation-test.cjs && node scripts/agent-history-scope-test.cjs && node scripts/webhook-ingress-test.cjs && node scripts/slack-content-filter-test.cjs && node scripts/slack-ingress-test.cjs && node scripts/discord-ingress-test.cjs && node scripts/chat-attachments-test.cjs && node scripts/clickup-task-bridge-test.cjs && node scripts/agentric-commands-test.cjs && node scripts/whats-new-test.cjs && node scripts/opencode-gate-test.cjs && node scripts/protected-path-guard-test.cjs && node scripts/attach-grace-test.cjs && node scripts/attach-file-liveness-test.cjs && node scripts/feed-smoke.cjs && node scripts/activity-classify-test.cjs && node scripts/goal-room-test.cjs && node scripts/secret-rotation-test.cjs && node scripts/update-watch-test.cjs && node scripts/runtime-update-watch-test.cjs && node scripts/setup-wizard-test.cjs && node scripts/md-pdf-test.cjs && node scripts/proposal-surfacing-test.cjs && node scripts/process-janitor-test.cjs && node scripts/detached-work-steer-test.cjs && node scripts/statusline-install-test.cjs && node scripts/docs-create-agent-test.cjs && node scripts/agent-stats-rollup-test.cjs && node scripts/task-discussion-rollup-test.cjs && node scripts/session-insights-stamp-test.cjs && node scripts/loop-stall-attribution-test.cjs && node scripts/session-progress-test.cjs && node scripts/drift-nudge-test.cjs && node scripts/goal-metric-review-test.cjs && node scripts/capability-gap-test.cjs && node scripts/workflow-proposal-test.cjs && node scripts/automation-edit-proposal-test.cjs && node scripts/app-card-close-test.cjs",
"test:alert-staleness": "node scripts/alert-staleness-test.cjs",
"test:deps": "node scripts/deps-freshness-test.cjs && node scripts/runtime-account-test.cjs && node scripts/runtime-account-misattribution-test.cjs && node scripts/runtime-login-test.cjs && node scripts/claude-config-seed-test.cjs && node scripts/claude-config-isolation-test.cjs",
"test:dm-continuity": "node scripts/dm-continuity-test.cjs",
diff --git a/scripts/git-ceiling-test.cjs b/scripts/git-ceiling-test.cjs
new file mode 100644
index 00000000..42c9ae71
--- /dev/null
+++ b/scripts/git-ceiling-test.cjs
@@ -0,0 +1,77 @@
+#!/usr/bin/env node
+/* Git discovery is fenced at the agents folder.
+ *
+ * globex 2026-09-22: an engineer run did `cd ` and then, unchained,
+ * `git remote set-url origin …client-app` + `git reset --hard origin/`. The cd failed, git walked
+ * up from the agent's folder to the agent-os checkout the data home lived in, and replaced the product's
+ * own source with another repo — `terminal/` gone, every later session crashed on launch.
+ *
+ * Pins: every launch exports GIT_CEILING_DIRECTORIES = the agents folder; with it, git run in the agent's
+ * folder finds NO repo even when an ancestor is one (and without it, it would — the test proves the walk
+ * is real); a repo INSIDE the agent's folder still works. Isolated home; no tmux or claude needed. */
+const fs = require('fs');
+const os = require('os');
+const path = require('path');
+const { spawnSync } = require('child_process');
+
+const ROOT = path.resolve(__dirname, '..');
+const HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'aos-git-ceiling-test-'));
+process.env.AGENT_OS_HOME = HOME;
+process.env.AGENT_OS_TENANT = 'testco';
+process.env.AOS_NO_TTYD = '1';
+process.env.CLAUDE_CONFIG_DIR = path.join(HOME, 'claude-config');
+fs.mkdirSync(process.env.CLAUDE_CONFIG_DIR, { recursive: true });
+delete process.env.AGENT_OS_SECRET_KEY;
+
+let pass = 0, fail = 0;
+const assert = (c, name, d) => c ? (pass++, console.log(` \x1b[32m✓\x1b[0m ${name}`)) : (fail++, console.log(` \x1b[31m✗ ${name}\x1b[0m${d ? ' — ' + d : ''}`));
+
+const { loadAgentOS } = require(path.join(ROOT, 'dist/kernel.js'));
+const { TerminalManager } = require(path.join(ROOT, 'dist/terminal.js'));
+const aos = loadAgentOS();
+const tm = new TerminalManager(aos, 'http://127.0.0.1:0', path.join(HOME, 'tmux.sock'));
+
+let spawned;
+const live = new Set();
+tm.backend.aliveNames = () => new Set(live);
+tm.backend.spawn = (_s, o) => { spawned = o; live.add(o.tmuxName); };
+tm.backend.kill = (_s, t) => { live.delete(t); };
+tm.backend.capturePane = () => '';
+tm.backend.hasClient = () => false;
+
+// Only git's own env should decide discovery in these probes.
+const cleanEnv = Object.fromEntries(Object.entries(process.env).filter(([k]) => !k.startsWith('GIT_')));
+const git = (cwd, extra) => spawnSync('git', ['rev-parse', '--show-toplevel'], { cwd, env: { ...cleanEnv, ...extra }, encoding: 'utf8' });
+
+(async () => {
+ const agent = aos.listAgents ? (aos.listAgents()[0]?.id) : undefined;
+ const AGENT = agent || 'agent-author';
+ tm.createSession(AGENT, 'probe', 'noop', 'automation:a1', true);
+ await new Promise((r) => setTimeout(r, 50));
+
+ const env = spawned?.env || {};
+ const agentDir = env.AGENT_DIR;
+ assert(!!agentDir, 'the launch carries AGENT_DIR', JSON.stringify(Object.keys(env)).slice(0, 200));
+ assert(env.GIT_CEILING_DIRECTORIES === fs.realpathSync(path.dirname(agentDir)),
+ 'GIT_CEILING_DIRECTORIES is the (real) agents folder', `${env.GIT_CEILING_DIRECTORIES} vs ${agentDir}`);
+
+ // Model the live layout: the data home inside a product checkout. Make the home's PARENT chain a repo.
+ const product = fs.realpathSync(HOME);
+ spawnSync('git', ['init', '-q', product]);
+ const without = git(agentDir, {});
+ assert(without.status === 0 && without.stdout.trim() === product,
+ 'control: WITHOUT the fence, git in the agent folder resolves to the enclosing checkout', without.stderr);
+ const withFence = git(agentDir, { GIT_CEILING_DIRECTORIES: env.GIT_CEILING_DIRECTORIES });
+ assert(withFence.status !== 0 && /not a git repository/.test(withFence.stderr),
+ 'WITH the fence, git in the agent folder finds no repo — a stray reset cannot reach the product', withFence.stdout);
+
+ const inner = path.join(agentDir, 'repos', 'client-app');
+ fs.mkdirSync(inner, { recursive: true });
+ spawnSync('git', ['init', '-q', inner]);
+ const own = git(inner, { GIT_CEILING_DIRECTORIES: env.GIT_CEILING_DIRECTORIES });
+ assert(own.status === 0 && own.stdout.trim() === fs.realpathSync(inner), 'a repo inside the agent folder still works', own.stderr);
+
+ console.log(`\ngit-ceiling-test: ${pass} passed, ${fail} failed`);
+ try { fs.rmSync(HOME, { recursive: true, force: true }); } catch { /* best-effort */ }
+ process.exit(fail ? 1 : 0);
+})();
diff --git a/src/terminal.ts b/src/terminal.ts
index 28b61386..aad6f83d 100644
--- a/src/terminal.ts
+++ b/src/terminal.ts
@@ -3066,6 +3066,14 @@ export class TerminalManager {
// thread follow-ups are delivered by send-keys (see deliverToResident / reviveResident).
if (o.resident) env.RESIDENT = '1';
env.AGENT_DIR = manifest.dir;
+ // Fence git discovery at the agents folder. An agent's folder usually isn't a repo, so git walks UP —
+ // and where the data home sits inside the product checkout it lands in agent-os itself. globex
+ // 2026-09-22: an engineer run did `cd ` then, unchained,
+ // `git remote set-url origin …client-app` + `git reset --hard origin/`. The cd failed, git
+ // resolved to the agent-os checkout, and the product's own source was replaced by another repo:
+ // `terminal/` gone, so every session launched afterwards crashed. Repos INSIDE the agent's folder
+ // (clones, worktrees, its own `git init`) are below the ceiling and unaffected.
+ try { env.GIT_CEILING_DIRECTORIES = fs.realpathSync(path.dirname(manifest.dir)); } catch { /* no dir → no walk to fence */ }
env.HOOK = this.gateHookFor(runtime);
// Tells the shared gate hook which tool→capability routing table to use.
env.AOS_RUNTIME = runtime;