Finalize the workflow-paradigm rewrite (red-team fixes applied) - #2
Merged
Merged
Conversation
* Fix turn-end Stop hook to use CLAUDE_PROJECT_DIR path Claude Code runs hook commands via /bin/sh from the session cwd, so the bare relative bin/fm-turnend-guard.sh path fails when cwd is not the repo root. Anchor the command with "$CLAUDE_PROJECT_DIR"/bin/fm-turnend-guard.sh instead; verified CLAUDE_PROJECT_DIR is set on Stop hooks in Claude Code 2.1.201. Document the cwd caveat and add a settings.json regression test. * no-mistakes(document): Document Stop hook path anchoring
* docs: trim AGENTS.md redundancy (diet PR 3/3) Consolidates five duplicated passages to a single owner each, per data/agentsmd-diet-s2/report.md redundancy items c3-c7: - Inheritable-config propagation mechanism: owned by section 3 (where the sweep runs); sections 4 and 7 keep compact references. Section 4 retains its one genuinely unique fact (crew-harness inherit-vs-fallback semantics), just no longer restates the propagation mechanism itself. - Landed-work definition: owned by section 7's ship-teardown detail (PR-containment mechanics, pr= discovery fallback); section 1's hard rule #3 keeps the rule plus a three-case summary and a pointer. - Backend meta-field enumeration: owned by docs/configuration.md ("Runtime backend", already comprehensive including cmux) and each backend's own doc; AGENTS.md keeps only the fields common to every task plus a pointer. - Dropped one redundant restatement of "silence is correct while waiting" in section 8. - Worktree-tangle guard explanation: owned by section 8 (already the fuller, cross-referenced version); section 3's TANGLE bullet keeps the remediation action and points at section 8 for the why. Also adds two captain-requested single-sentence rules: invoke bin/ scripts by absolute $FM_ROOT path after any cd away from the home, and a backend spawn refusal must be surfaced to the captain rather than silently worked around by switching backends. AGENTS.md: 901 -> 889 lines, 112355 -> 108560 bytes. * no-mistakes(review): Clarify post-cd bin invocation guidance * no-mistakes(document): Sync AGENTS trim docs * no-mistakes(lint): Fix Markdown line style
…henguid#259) * feat(backends): cmux detection fallbacks and socket-mode matrix Workstream A: cmux's bundled claude wrapper strips every CMUX_* env var on its passthrough path (reproduced live 2026-07-04, cmux 0.64.17), so a claude-harness firstmate inside a cmux tab has no CMUX_WORKSPACE_ID. fm_backend_detect now falls back - macOS-only, only when the primary marker is absent - to __CFBundleIdentifier=com.cmuxterm.app and then a process ancestry walk resolved by bundle id (lsappinfo) plus a bundle-shaped ps comm match. Innermost-first ordering is unchanged and absorbs the tmux-inside-cmux bundle-id false positive; the auto-detect NOTICE names the winning fallback signal. Workstream B: the five socketControlMode values were traced through cmux source (commit 9c91710e3f58): off/cmuxOnly can never admit an external CLI, automation admits same-user clients with no secret (0600 socket only), password needs the auth handshake, allowAll opens the socket to every local user (0666). Automation mode is now the documented recommendation; the adapter's refusals name every viable mode, classify Invalid password as unauth, and the launch-timeout message names the off-mode possibility. Docs carry the wrapper-strip empirical record, the fallback contract and authority split, and the full mode matrix with rationale; tests cover the new detection paths, the nested false positive, and the refusal wording. * no-mistakes(review): Document cmux fallback detection * no-mistakes(review): Update cmux architecture docs * no-mistakes(document): Align cmux backend docs
* fix(backends): home-scope zellij tab titles to close cross-home collision gap Zellij's one shared "firstmate" session has no per-home split and enforces no tab-name uniqueness, so two firstmate homes with colliding task ids could send/peek/close each other's tabs - the same gap a no-mistakes review gate caught for cmux (docs/cmux-backend.md). Ports that fix: every new tab is created with a home-scoped title (fm-<home-label>-<id>), and every list/find/recover/kill path scopes matches to this home's own tag. A tab spawned before this change still matches via its old untagged bare title, but only when unambiguous - two live tabs sharing a bare title refuse rather than guessing which one is ours. Factors the home-label/hash derivation shared with cmux into bin/fm-backend-hometag-lib.sh so the two adapters can't drift. * no-mistakes(review): Fix zellij child teardown home tag * no-mistakes(review): Fix zellij teardown and selector scoping * no-mistakes(document): Sync zellij home-scope docs
Replace the crewmate/tmux-window dispatch model with in-session dynamic workflows. Remove harness adapters, crew spawn/teardown/supervision, pane peeking, stale-crew detection, and multi-backend machinery. Promote the board-v2 interface and the design/red-team/merge gates to first-class sections. Preserve firstmate persistence in tmux, the board pollers and watcher for board input, delivery modes and the no-mistakes gate, secondmates (flagged for David's confirmation), captain.md-at-startup, bootstrap/recovery, backlog, self-update, and X mode. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reconcile the d3d3c7a rewrite down to the final workflow-era manual: the finalA draft as the base, folding in the load-bearing board contract from d3d3c7a (section semantics as whose-turn, one item one row, MVP-tracker naming, scan-all-threads-before-seen) and applying the synthesis amendments (Kronos tickets plus cite-repo-files-by-path in section 3, the built-in verify then project-verify then no-mistakes done stack in section 5, the transfer-file maintenance discipline in section 10, the reconciled escape-hatch and skill dispositions in section 12). Secondmates are dropped outright; the tmux-crewmate and watcher stack moves to the documented escape hatch. CLAUDE.md is a symlink to this file.
fm-poll.sh is the launchd-supervised (com.firstmate.poller, KeepAlive) board/merge/stall poller that replaces the agent-armed watcher for daily duty: it loops state/*.check.sh on the check contract, delivers wakes through the durable queue in fm-wake-lib.sh, keeps its own state/.last-poller-beat beacon and a home-scoped singleton guard, and injects a synthetic startup check event so a wake confirms the delivery path end to end. fm-write-fence.sh is the broadened PreToolUse hook: it blocks writes under ~/dev/work except the agent-worktree allowlist (~/dev/work/*/.claude/worktrees and ~/.treehouse), failing open on unreadable payloads. fm-board-checkin.sh writes a real per-item stamp into state/board-checkins.json atomically at each phase boundary. launchd/com.firstmate.poller.plist ships UNLOADED with a header comment: loading it is the human-verified cutover step, with __FM_ROOT__/__FM_HOME__ placeholders substituted then.
Mark the retire-marked scripts from the disposition table with a DEPRECATED header: the watcher/guard stack (fm-guard, fm-tangle-lib), the crewmate scaffolding (fm-brief, fm-promote), the afk daemon (fm-supervise-daemon), and the secondmate stack (fm-home-seed, fm-backlog-handoff, fm-config-push, fm-config-inherit-lib, fm-marker-lib). Each stays functional on disk and is removed only in the cleanup PR after the Opus 4.8 transition settles, so rollback stays real.
Restructure the toolbelt reference into New, Kept, Escape hatch, Retired, and Inert sections so each script's disposition is explicit, add the three new scripts plus the launchd plist and the generated stall check, and note that nothing is deleted until the cleanup PR.
…links The projects/<name> entries are firstmate's own real clones, not symlinks into ~/dev/work, so the ~/dev/work rule never covered them. Add an explicit block on <FM_ROOT>/projects/** (allowing only projects/<name>/.claude/worktrees/**), and make resolve() walk up to the nearest existing ancestor so a new file created under a symlinked directory still resolves into the real fenced tree instead of slipping past as an unresolved literal. Add colocated behavior tests.
…up wake A bare kill -0 on the pidfile trusted a recycled pid: after a kill -9 left a stale pidfile whose pid got reused, the launchd instance read it as "already running" and exited, and KeepAlive + the 10s throttle wedged the poller in a silent respawn loop that never polled the board. Record the process identity (start time + command) alongside the pid and honor the pidfile only when the identity still matches, so a reused pid is treated as dead. Also rate-limit the synthetic startup wake so a crash-respawn loop cannot flood the durable wake queue; a genuine restart still emits it.
…ktrees AGENTS.md sections 4 and 6 name bin/fm-teardown.sh --worktree <path> as the sanctioned disposal for a changed worktree, but only the task-id path existed; a workflow worktree has no state/<id>.meta, so the documented command errored. Add a --worktree mode that runs the same landed check keyed on the worktree path (refusing dirty or unlanded work, refusing a main checkout, removing only on a pass or with --force), independent of any meta record. Add colocated tests.
- Rule 2 and section 3: projects/<name> are firstmate's own clones (real dirs), not symlinks into ~/dev/work; describe the write fence as wired at the poller cutover and best-effort behind isolation (fails open), not present-tense sole enforcement. Document the fence-wiring as a hard step of that cutover in the plist header, kept out of tracked settings for this shared-template repo. - Rule 1: the tracker-sync merge authorization is gated on David's per-run okay of the change list; keep the standing merge authority the skill grants. - Budgets and stall detection: mark data/budgets.md and state/workflow-runs.check.sh as built at the cutover, with an interim path. - docs/scripts.md: fence covers projects/ clones; poller pidfile identity check and rate-limited startup wake.
DQ4443
force-pushed
the
fm/workflow-paradigm-rewrite
branch
from
July 6, 2026 07:41
7b113ba to
d891159
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This swaps firstmate's operating model from the tmux-crewmate supervision stack to native Workflow and Agent dispatch. Instead of spawning crewmates in tmux windows and babysitting them with fm-watch.sh, firstmate sizes a Workflow to the task, watches structured returns, and brings you outcomes. The old scripts stay on disk as a documented escape hatch (AGENTS.md section 12); nothing is deleted. Board polling, merge polling, and stall detection move off the hand-armed watcher onto a launchd job (fm-poll.sh, com.firstmate.poller), so "the pollers must never lapse" becomes a property of launchd rather than of a session remembering to arm something. A broadened PreToolUse write fence replaces the old html-only block, and board check-in stamps are written deterministically by the orchestration script.
The four commits on top of the paradigm work are the red-team pass. They fix two findings that undercut the stated guarantees and were not in the builder's own open-issues list. The write fence structurally missed projects/: those are firstmate's own real clones, not the symlinks into ~/dev/work the docs claimed, so the ~/dev/work rule never covered them. And fm-poll.sh trusted a bare kill -0 on its pidfile, so a recycled pid could read as "already running" and wedge the poller in a silent KeepAlive respawn loop that never polls the board. Both are fixed with tests. The rest of the pass reconciles AGENTS.md and docs/scripts.md with what the code actually does, and implements the fm-teardown.sh --worktree mode the docs already named but nobody had built.
Main thing to check before the cutover: fm-write-fence.sh now blocks writes under <FM_ROOT>/projects/** as well as ~/dev/work/**, allowing only the isolated .claude/worktrees/ trees inside each. If any real workflow legitimately writes into a projects/ clone outside a worktree, the fence would block it once wired. I believe nothing does (projects/ has always been read-only to firstmate), but that is the assumption worth a sanity-check.
Seven decisions are baked into this branch, each independently vetoable:
Go-live is gated, not automatic. The launchd plist ships unloaded with placeholder paths and a header describing the human cutover: substitute the real paths, load the plist, confirm the synthetic startup wake drains, round-trip one real board thread and one action through the durable queue, wire the write fence into local settings, and only then disarm fm-watch.sh. Three smoke tests still to run at that cutover: the synthetic startup wake arrives through the wake queue, a real board event round-trips, and a kill -9 of the poller is restarted by launchd inside the throttle window. None of that has run yet. This branch is the code, not the cutover.
Rollback is clean. Revert this PR, unload the plist with launchctl bootout if it was ever loaded, and re-arm fm-watch.sh. The escape-hatch scripts were never removed, so the old paradigm is one arm command away the entire time.
One caveat on validation: the no-mistakes pipeline did not run. It refuses on a dirty working tree, and this checkout arrived with ten uncommitted files, a prettier reformat of skill and doc markdown (.agents/skills//SKILL.md, README.md, docs/-backend.md, docs/turnend-guard.md, skills/stow/SKILL.md) that I did not create and was not allowed to touch or commit. Rather than stash someone else's in-flight work through a pipeline that rebases, I pushed the branch and opened this PR by hand. What I did verify locally: shellcheck on the exact CI command (bin/.sh bin/backends/.sh tests/.sh) exits clean, bash -n passes on every changed script, the two new test files pass (fm-write-fence, 10 cases; fm-teardown-worktree, 6 cases), and the existing fm-teardown and fm-tangle-guard suites still pass after the teardown change. The full tests/.test.sh e2e suite, which needs tmux, was not run end to end; that is the part the pipeline would have covered. Commit or discard those ten files and
git push no-mistakes fm/workflow-paradigm-rewritewill run the whole gate.