feat(spawn): add general post-worktree-create hook seam#371
Open
zachlandes wants to merge 4 commits into
Open
Conversation
fm-spawn now runs a local, gitignored config/hooks/post-worktree-create hook right after it creates a task's worktree - or a secondmate's home - and asserts it is a genuine isolated worktree, for every task kind. This is a general extension seam for fleet-local worktree provisioning that should not live in tracked code. firstmate ships no hook, so an absent hook is a complete no-op with zero behavior change. The hook is purely additive: a hook that errors is warned and swallowed so the spawn always continues, and it never runs against the primary checkout (the call site only reaches it past the isolation assertion, and fm-hooks-lib.sh refuses to run when the worktree resolves to the firstmate root as a backstop). The hook receives the worktree path, project name, task id, and kind as both positional args and environment. Adds bin/fm-hooks-lib.sh, a docs/examples/ sample hook, the contract in docs/configuration.md, an AGENTS.md config-index pointer, and colocated seam tests (no-op absent, runs present, skips primary, non-fatal on error).
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.
Summary
Adds a general post-worktree-create hook to
fm-spawn.sh: after a task's worktree — or a secondmate's home — is created and asserted to be a genuine isolated worktree,fm-spawnruns a local, gitignoredconfig/hooks/post-worktree-createscript if the operator installed one. This is a general extension seam for fleet-local worktree provisioning that should not live in tracked code.firstmate ships no hook, so an absent hook is a complete no-op with zero behavior change for anyone not using it.
Why
Worktree provisioning that a fleet needs (e.g. dropping per-project config into each worktree) previously had no seam and would have to edit tracked code. That's a poor fit here: firstmate self-updates fast-forward-only, so a local commit on tracked files blocks future updates, and fleet-specific features don't belong upstream. A general hook keeps the tracked code additive and lets each fleet keep its own wiring local and gitignored.
Behavior
ship,scout, andsecondmate.timeout/gtimeoutwith a budget ofFM_HOOK_TIMEOUTseconds (default 120) and a 5s kill-after grace, so even a hook that traps/ignoresSIGTERMis stopped and reported like a failure.FM_HOOK_TIMEOUT=0is a documented no-limit opt-out. When neither timeout binary is onPATH, the hook runs unbounded after a stderr warning — a missing binary never fails the spawn.fm-spawn's isolation assertion, andfm-hooks-lib.shadditionally refuses to run when the worktree resolves to the firstmate root as a backstop.$1..$4) andFM_HOOK_*environment.config/hooks/is gitignored, so an installed hook stays fleet-local. It is also inheritable:config/hooks/post-worktree-createpropagates from the primary into secondmate homes like the other crew-affecting config, with the executable bit preserved (and repaired on re-convergence if a destination copy loses it).Contents
bin/fm-hooks-lib.sh— the seam runner (fm_run_post_worktree_create_hook).bin/fm-spawn.sh— sources the lib and calls the seam after worktree isolation is asserted.bin/fm-config-inherit-lib.sh— makes the hook inheritable and preserves/repairs file mode on copy.docs/configuration.md— the hook contract;docs/examples/post-worktree-create— a runnable sample.AGENTS.md— one-line config-index pointer..gitignore—config/hooks/.tests/fm-hooks.test.sh— no-op-absent, runs-with-args-and-env, skips-primary, non-fatal-on-error, non-fatal-on-hang (incl. aSIGTERM-ignoring hook), and a fake-timeout check of the-k 5 <budget>invocation.tests/fm-secondmate-harness.test.sh— hook inheritance with mode preservation.Validated through the no-mistakes pipeline (review, test, document, lint all green).
shellcheck bin/*.sh bin/backends/*.sh tests/*.shis clean.