diff --git a/.agents/skills/harness-adapters/references/harness/claude.md b/.agents/skills/harness-adapters/references/harness/claude.md index 23abc909184..47a63a4265f 100644 --- a/.agents/skills/harness-adapters/references/harness/claude.md +++ b/.agents/skills/harness-adapters/references/harness/claude.md @@ -14,9 +14,6 @@ Busy hooks verified 2026-07-28 on Claude Code 2.1.220. | Effort | `--effort `, verified on 2.1.196. | | Permissions | `--dangerously-skip-permissions` by default, or `--permission-mode auto` when `config/claude-permission-mode` is `auto`; the `auto` shape verified on 2.1.269. See [`Claude permission mode`](../../../../../docs/configuration.md#claude-permission-mode-configclaude-permission-mode) for the launch grant and configuration. | -A crew or scout's per-worktree `.claude/settings.local.json` carries `"autoCompactEnabled":true,"autoCompactWindow":500000`, so Claude auto-compacts on its own even though the captain's own user-scope `~/.claude/settings.json` sets `autoCompactEnabled` to `false`. -`bin/fm-spawn.sh`'s Claude `KIND != secondmate` branch owns that per-worktree write, and a low context reading on a live Claude crewmate is therefore the harness's own job to compact, not a manual `/compact` trigger. - ## Workspace trust Claude gates a folder it has never seen behind an interactive workspace-trust dialog (titled "Quick safety check: Is this a project you created or one you trust?"), so every fresh task worktree would hit it, and so would every secondmate home no operator has opened by hand. diff --git a/.agents/skills/stuck-crewmate-recovery/SKILL.md b/.agents/skills/stuck-crewmate-recovery/SKILL.md index 88a7ae60d47..ffef22777f0 100644 --- a/.agents/skills/stuck-crewmate-recovery/SKILL.md +++ b/.agents/skills/stuck-crewmate-recovery/SKILL.md @@ -77,7 +77,6 @@ Escalate in order: 4. If the crewmate is genuinely wedged after redirection, relaunch it with `FM_HOME= bin/fm-control.sh relaunch --note ''`, which stops the agent, carries the brief plus that note into a replacement in the same local copy, and restores the prior record if the replacement cannot start. Pass `--harness`, `--model`, or `--effort` on that same command when the worker should come back on a different runtime. Genuine wedging means looping, unresponsive, repeating the same obstacle, or truly dead. - A low context reading is not wedging. - A low context reading is not wedging; harness-specific compaction behavior belongs to the selected adapter reference, including the [Claude crew/scout contract](../harness-adapters/references/harness/claude.md). + A low context reading is not wedging; modern harnesses auto-compact and keep going. The worktree and commits persist, so relaunch is cheap. 5. If a second relaunch fails too, write `failed` to the backlog and tell the captain the plain failure, preserved work, and consequence using `AGENTS.md` section 9; do not mention metadata, harness, window, or worktree unless the path itself is needed for action. diff --git a/.gitignore b/.gitignore index 5c3f8569597..3eece43c35f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,5 @@ __pycache__/ *.pyc .env config/ -.sessions/ .tools/ diff --git a/.pi/extensions/lib/fm-calm-working-ship.ts b/.pi/extensions/lib/fm-calm-working-ship.ts index 2b28633bfa0..d8f8ede4695 100644 --- a/.pi/extensions/lib/fm-calm-working-ship.ts +++ b/.pi/extensions/lib/fm-calm-working-ship.ts @@ -44,12 +44,7 @@ const ANSI_FOREGROUND: Record, string> = // Restores the default foreground so color never bleeds into padding or later frames. const RESET = "\u001b[39m"; -// The working-row widget slot is deliberately shared with the standalone Pi Calm -// extension, which installs its boat under the same "calm-working-ship" key. Pi -// replaces widgets under one key, so a session that loads both Calms renders a -// single boat and a session loading either alone is unchanged. Rename the slot -// in both implementations together, or dual-install sessions duplicate the boat. -export const CALM_WORKING_SHIP_WIDGET_KEY = "calm-working-ship"; +export const CALM_WORKING_SHIP_WIDGET_KEY = "firstmate-calm-working-ship"; export type CalmWorkingShipAnimation = Omit & { /** Render one frame that exactly fits `width`, clamping the track to it first. */ diff --git a/bin/fm-spawn.sh b/bin/fm-spawn.sh index 6b4c9bbf913..e2b4d5a4114 100755 --- a/bin/fm-spawn.sh +++ b/bin/fm-spawn.sh @@ -4552,7 +4552,7 @@ if [ "$KIND" != secondmate ]; then j_stopfail=$(json_escape "$busy_cmd_prefix idle $busy_suffix --event stop-failure 2>/dev/null || true") j_sessionend=$(json_escape "$busy_cmd_prefix idle $busy_suffix --event session-end 2>/dev/null || true") cat >"$WT/.claude/settings.local.json" < text.replace(new RegExp(`${ESC}\\[[0-9;]*m`, "g"), ""); const check = (condition, message) => { if (!condition) throw new Error(message); }; -// The working-row slot is deliberately shared with the standalone Pi Calm -// extension, which installs its own boat under the same key. Pi replaces widgets -// per key, so a session loading both Calms renders exactly one boat. Renaming -// this slot in only one implementation silently restores the duplicate-boat -// defect this suite exists to prevent. -check( - CALM_WORKING_SHIP_WIDGET_KEY === "calm-working-ship", - `Firstmate Calm claims a private working-row widget key (${CALM_WORKING_SHIP_WIDGET_KEY}); it must share the standalone Calm slot "calm-working-ship" so dual-install sessions render one boat`, -); const sailOf = (frame) => strip(frame[0]).includes(SAIL) ? SAIL : "none"; // --- Calm cadence: the boat is materially slower than the water ------------------ diff --git a/tests/fm-spawn-claude-attribution.test.sh b/tests/fm-spawn-claude-attribution.test.sh index ce78426c2d5..28e5f935837 100755 --- a/tests/fm-spawn-claude-attribution.test.sh +++ b/tests/fm-spawn-claude-attribution.test.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Regression test for the claude) branch of bin/fm-spawn.sh: the generated # /.claude/settings.local.json must carry the lifecycle hooks it -# exists for, the crew auto-compaction override, and NO attribution object. +# exists for and NO attribution object. # # Co-author suppression is owned upstream now: upstream 72bfdd0 (#3945) passes an # explicit `"attribution":{"commit":"","pr":"","sessionUrl":false}` object in @@ -139,15 +139,11 @@ test_claude_spawn_settings_carry_hooks_without_attribution() { command -v jq >/dev/null 2>&1 || fail "jq is required to parse the generated settings" jq -e 'has("attribution") | not' "$settings" >/dev/null \ || fail "the per-worker attribution object was reintroduced into the spawned settings" - jq -e '.autoCompactEnabled == true' "$settings" >/dev/null \ - || fail "generated settings did not enable auto-compaction for the crew" - jq -e '.autoCompactWindow == 500000' "$settings" >/dev/null \ - || fail "generated settings did not set the crew's auto-compaction window" for hook in UserPromptSubmit Stop StopFailure SessionEnd; do jq -e ".hooks.\"$hook\" | length > 0" "$settings" >/dev/null \ || fail "generated settings lost the $hook lifecycle hook" done - pass "claude spawn settings carry the hooks, auto-compaction, and no per-worker attribution object" + pass "claude spawn settings carry the hooks and no per-worker attribution object" } # Scope pin: another harness's spawn must not receive a Claude settings file,