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
3 changes: 0 additions & 3 deletions .agents/skills/harness-adapters/references/harness/claude.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ Busy hooks verified 2026-07-28 on Claude Code 2.1.220.
| Effort | `--effort <low\|medium\|high\|xhigh\|max>`, 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.
Expand Down
3 changes: 1 addition & 2 deletions .agents/skills/stuck-crewmate-recovery/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ Escalate in order:
4. If the crewmate is genuinely wedged after redirection, relaunch it with `FM_HOME=<this-firstmate-home> bin/fm-control.sh <task-id> relaunch --note '<progress so far>'`, 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.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,5 @@ __pycache__/
*.pyc
.env
config/
.sessions/

.tools/
7 changes: 1 addition & 6 deletions .pi/extensions/lib/fm-calm-working-ship.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,7 @@ const ANSI_FOREGROUND: Record<Exclude<CalmWorkingShipColor, "plain">, 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<CalmWorkingShipSprite, "frame"> & {
/** Render one frame that exactly fits `width`, clamping the track to it first. */
Expand Down
2 changes: 1 addition & 1 deletion bin/fm-spawn.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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" <<EOF
{"autoCompactEnabled":true,"autoCompactWindow":500000,"hooks":{"UserPromptSubmit":[{"hooks":[{"type":"command","command":"$j_submit"}]}],"Stop":[{"hooks":[{"type":"command","command":"$j_stop"}]}],"StopFailure":[{"hooks":[{"type":"command","command":"$j_stopfail"}]}],"SessionEnd":[{"hooks":[{"type":"command","command":"$j_sessionend"}]}]}}
{"hooks":{"UserPromptSubmit":[{"hooks":[{"type":"command","command":"$j_submit"}]}],"Stop":[{"hooks":[{"type":"command","command":"$j_stop"}]}],"StopFailure":[{"hooks":[{"type":"command","command":"$j_stopfail"}]}],"SessionEnd":[{"hooks":[{"type":"command","command":"$j_sessionend"}]}]}}
EOF
exclude_path '.claude/settings.local.json'
;;
Expand Down
9 changes: 0 additions & 9 deletions tests/fm-calm-pi-extension.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3008,15 +3008,6 @@ const strip = (text) => 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 ------------------
Expand Down
8 changes: 2 additions & 6 deletions tests/fm-spawn-claude-attribution.test.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# Regression test for the claude) branch of bin/fm-spawn.sh: the generated
# <worktree>/.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
Expand Down Expand Up @@ -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,
Expand Down
Loading