@zerwiz/ymir 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/.agents/config/app-repos.yaml +38 -0
  2. package/.claude/settings.json +31 -0
  3. package/.codex/hooks.json +43 -0
  4. package/.cursor/hooks.json +33 -0
  5. package/.pi/extensions/README.md +39 -0
  6. package/.pi/extensions/gna-pi-watch.ts +15 -0
  7. package/.pi/extensions/lib/ro-assistant-layout.ts +96 -0
  8. package/.pi/extensions/lib/ro-operational-user-layout.ts +145 -0
  9. package/.pi/extensions/lib/ro-visibility.ts +99 -0
  10. package/.pi/extensions/lib/ro-working-ship.ts +243 -0
  11. package/.pi/extensions/lib/rodd-operational-input.ts +62 -0
  12. package/.pi/extensions/lib/skuld-branch-dispatch.ts +252 -0
  13. package/.pi/extensions/lib/skuld-branch-model-picker.ts +77 -0
  14. package/.pi/extensions/lib/vordr-sessionstart-supervisor.mjs +48 -0
  15. package/.pi/extensions/ro.ts +11 -0
  16. package/.pi/extensions/skuld-branch-supervision.ts +11 -0
  17. package/.pi/extensions/syn-turnend-guard.ts +11 -0
  18. package/.pi/mcp.json +16 -0
  19. package/.pi/mcp.json.example +11 -0
  20. package/.pi/npm/.gitignore +2 -0
  21. package/.pi/settings.json +8 -0
  22. package/.pi/shared/extensions/gna-pi-watch.ts +750 -0
  23. package/.pi/shared/extensions/herdr-agent-state.ts +257 -0
  24. package/.pi/shared/extensions/open-editor.ts +509 -0
  25. package/.pi/shared/extensions/ro.ts +504 -0
  26. package/.pi/shared/extensions/skuld-branch-supervision.ts +1535 -0
  27. package/.pi/shared/extensions/syn-turnend-guard.ts +683 -0
  28. package/.pi/shared/extensions/todo.ts +133 -0
  29. package/.pi/shared/extensions/ymir-subagents.ts +287 -0
  30. package/.pi/shared/extensions/ymir-well.ts +130 -0
  31. package/CHANGELOG.md +23 -0
  32. package/assets/Ymir.md +4654 -0
  33. package/assets/healer.html +717 -0
  34. package/assets/icon-family/family-sheet.png +0 -0
  35. package/assets/icon-family/hlidskjalf.svg +1 -0
  36. package/assets/icon-family/sessrumnir.svg +1 -0
  37. package/assets/icon-family/smidja.svg +1 -0
  38. package/assets/icon-family/ymir.svg +1 -0
  39. package/assets/mimirswell.html +880 -0
  40. package/assets/mock/README.md +16 -0
  41. package/assets/mock/feeds.ts +175 -0
  42. package/assets/mock/mock.ts +408 -0
  43. package/assets/reference/docs/agent-control.md +122 -0
  44. package/assets/reference/docs/apodex/Appdex_as_Orchestrator_Planner_Reviewer_Assessment.md +176 -0
  45. package/assets/reference/docs/apodex/Final Report(1).md +198 -0
  46. package/assets/reference/docs/apodex/Final Report(2).md +54 -0
  47. package/assets/reference/docs/apodex/Final Report(3).md +40 -0
  48. package/assets/reference/docs/apodex/Final Report(4).md +234 -0
  49. package/assets/reference/docs/apodex/Final Report(5).md +13 -0
  50. package/assets/reference/docs/apodex/Final Report.md +82 -0
  51. package/assets/reference/docs/architecture.md +392 -0
  52. package/assets/reference/docs/arm-pretool-check.md +247 -0
  53. package/assets/reference/docs/calm-mode-feasibility.md +541 -0
  54. package/assets/reference/docs/calm.md +59 -0
  55. package/assets/reference/docs/captain-hold-lifecycle.md +96 -0
  56. package/assets/reference/docs/cd-guard.md +165 -0
  57. package/assets/reference/docs/cmux-backend.md +133 -0
  58. package/assets/reference/docs/codex-app-backend.md +57 -0
  59. package/assets/reference/docs/configuration.md +924 -0
  60. package/assets/reference/docs/documentation-audiences.json +468 -0
  61. package/assets/reference/docs/documentation-audiences.md +28 -0
  62. package/assets/reference/docs/examples/crew-dispatch.json +26 -0
  63. package/assets/reference/docs/examples/process-event-extension/file-signal.mjs +96 -0
  64. package/assets/reference/docs/examples/process-event-extension/firstmate-extension.json +15 -0
  65. package/assets/reference/docs/examples/watched-tools.json +24 -0
  66. package/assets/reference/docs/examples/wedge-alarm +22 -0
  67. package/assets/reference/docs/extension-bindings.md +237 -0
  68. package/assets/reference/docs/fm-test-isolation-proof.json +37 -0
  69. package/assets/reference/docs/fm-test-isolation-proof.md +149 -0
  70. package/assets/reference/docs/fm-test-portable-shards.md +118 -0
  71. package/assets/reference/docs/gitlab-merge-watch.md +275 -0
  72. package/assets/reference/docs/herdr-backend.md +346 -0
  73. package/assets/reference/docs/orca-backend.md +84 -0
  74. package/assets/reference/docs/pi-supervision-branch-poster.svg +125 -0
  75. package/assets/reference/docs/pi-supervision-branch.md +96 -0
  76. package/assets/reference/docs/plans/agentic-engineering-workflow.md +395 -0
  77. package/assets/reference/docs/plans/agentic-factory-implementation-plan.md +615 -0
  78. package/assets/reference/docs/plans/command-factory-enhancement-plan.md +420 -0
  79. package/assets/reference/docs/plans/phase-1-layer3-program-design.md +134 -0
  80. package/assets/reference/docs/plans/phase-2-vertical-slices.md +100 -0
  81. package/assets/reference/docs/plans/phase-3-measurable-goals.md +106 -0
  82. package/assets/reference/docs/plans/phase-4-context-budget.md +106 -0
  83. package/assets/reference/docs/plans/phase-5-pre-mortem-confidence.md +100 -0
  84. package/assets/reference/docs/plans/phase-6-incident-to-agent.md +109 -0
  85. package/assets/reference/docs/remote-secondmates.md +268 -0
  86. package/assets/reference/docs/scripts.md +146 -0
  87. package/assets/reference/docs/sessionstart-nudge.md +115 -0
  88. package/assets/reference/docs/subagent-guard.md +383 -0
  89. package/assets/reference/docs/supervision-protocols/claude.md +27 -0
  90. package/assets/reference/docs/supervision-protocols/codex.md +16 -0
  91. package/assets/reference/docs/supervision-protocols/cursor.md +31 -0
  92. package/assets/reference/docs/supervision-protocols/grok.md +39 -0
  93. package/assets/reference/docs/supervision-protocols/opencode.md +17 -0
  94. package/assets/reference/docs/supervision-protocols/pi.md +33 -0
  95. package/assets/reference/docs/supervision-protocols/unknown.md +13 -0
  96. package/assets/reference/docs/tmux-backend.md +115 -0
  97. package/assets/reference/docs/trace-context.md +117 -0
  98. package/assets/reference/docs/turnend-guard.md +170 -0
  99. package/assets/reference/docs/verification/dispatch-auth.md +204 -0
  100. package/assets/reference/docs/verification/muse.md +221 -0
  101. package/assets/reference/docs/verification/process-event-sources.md +208 -0
  102. package/assets/reference/docs/verification/public-followup.md +119 -0
  103. package/assets/reference/docs/verification/runtime-backends.md +1022 -0
  104. package/assets/reference/docs/verification/stow-memory.md +53 -0
  105. package/assets/reference/docs/verification/supervision.md +534 -0
  106. package/assets/reference/docs/verification/trace-context.md +31 -0
  107. package/assets/reference/docs/voice-relay.md +295 -0
  108. package/assets/reference/docs/watcher-continuity.md +119 -0
  109. package/assets/reference/docs/wedge-alarm.md +39 -0
  110. package/assets/reference/docs/zellij-backend.md +114 -0
  111. package/assets/reference/index.html +1172 -0
  112. package/assets/reference/skills/afk/SKILL.md +241 -0
  113. package/assets/reference/skills/ahoy/SKILL.md +56 -0
  114. package/assets/reference/skills/ask-user-authority/SKILL.md +56 -0
  115. package/assets/reference/skills/bearings/SKILL.md +161 -0
  116. package/assets/reference/skills/bearings/assets/board-template.html +731 -0
  117. package/assets/reference/skills/bootstrap-diagnostics/SKILL.md +68 -0
  118. package/assets/reference/skills/captain-hold-lifecycle/SKILL.md +54 -0
  119. package/assets/reference/skills/decision-hold-lifecycle/SKILL.md +15 -0
  120. package/assets/reference/skills/diagnostic-reasoning/SKILL.md +53 -0
  121. package/assets/reference/skills/firstmate-codexapp/SKILL.md +110 -0
  122. package/assets/reference/skills/firstmate-codexapp/agents/openai.yaml +4 -0
  123. package/assets/reference/skills/firstmate-coding-guidelines/SKILL.md +127 -0
  124. package/assets/reference/skills/firstmate-orca/SKILL.md +92 -0
  125. package/assets/reference/skills/fmx-respond/SKILL.md +283 -0
  126. package/assets/reference/skills/harness-adapters/SKILL.md +95 -0
  127. package/assets/reference/skills/harness-adapters/references/common/control-and-recovery.md +37 -0
  128. package/assets/reference/skills/harness-adapters/references/common/dispatch.md +32 -0
  129. package/assets/reference/skills/harness-adapters/references/common/model-and-effort.md +42 -0
  130. package/assets/reference/skills/harness-adapters/references/common/primary-hooks.md +40 -0
  131. package/assets/reference/skills/harness-adapters/references/harness/claude.md +55 -0
  132. package/assets/reference/skills/harness-adapters/references/harness/codex.md +43 -0
  133. package/assets/reference/skills/harness-adapters/references/harness/cursor.md +75 -0
  134. package/assets/reference/skills/harness-adapters/references/harness/grok.md +69 -0
  135. package/assets/reference/skills/harness-adapters/references/harness/kimi.md +51 -0
  136. package/assets/reference/skills/harness-adapters/references/harness/muse.md +70 -0
  137. package/assets/reference/skills/harness-adapters/references/harness/opencode.md +42 -0
  138. package/assets/reference/skills/harness-adapters/references/harness/pi.md +56 -0
  139. package/assets/reference/skills/process-event-sources/SKILL.md +137 -0
  140. package/assets/reference/skills/project-management/SKILL.md +92 -0
  141. package/assets/reference/skills/quota-array-dispatch/SKILL.md +131 -0
  142. package/assets/reference/skills/secondmate-provisioning/SKILL.md +254 -0
  143. package/assets/reference/skills/stow/SKILL.md +309 -0
  144. package/assets/reference/skills/stuck-crewmate-recovery/SKILL.md +54 -0
  145. package/assets/reference/skills/updatefirstmate/SKILL.md +64 -0
  146. package/assets/skills/README.md +5 -0
  147. package/assets/yggdrasil.html +649 -0
  148. package/assets/ymir-banner-01.png +0 -0
  149. package/assets/ymir-banner-02.png +0 -0
  150. package/assets/ymir-banner-03.png +0 -0
  151. package/assets/ymir-banner-04.png +0 -0
  152. package/assets/ymir-banner-05.png +0 -0
  153. package/assets/ymir-banner-06.png +0 -0
  154. package/assets/ymir-emblem-darksteel.svg +63 -0
  155. package/assets/ymir-emblem-forged.svg +55 -0
  156. package/assets/ymir-emblem-slate.svg +55 -0
  157. package/assets/ymir-mark-algiz-anvil.svg +9 -0
  158. package/assets/ymir-stave.svg +53 -0
  159. package/assets/ymir.html +563 -0
  160. package/bin/ymir-install.sh +6 -1
  161. package/data/learnings.md +33 -0
  162. package/data/local-models.md +34 -0
  163. package/data/operator.md +15 -0
  164. package/data/projects.md +28 -0
  165. package/data/realm.md +1 -0
  166. package/deploy/Containerfile +27 -0
  167. package/deploy/README.md +104 -0
  168. package/deploy/compose/compose.yaml +45 -0
  169. package/deploy/env.example +30 -0
  170. package/deploy/quadlet/ymir.container +57 -0
  171. package/package.json +10 -8
@@ -0,0 +1,383 @@
1
+ # Primary-session delegation guard
2
+
3
+ This document is the authoritative human-readable contract for the guard that stops a firstmate primary from delegating work outside the fleet.
4
+
5
+ The shipped mechanism is `bin/fm-subagent-pretool-check.sh`, a PreToolUse guard that denies a delegation-SHAPED tool name in a genuine primary home.
6
+ Claude primaries should also use an untracked per-home local `permissions.deny` list as hardening for known Claude delegation tools, because it removes them from the model's schema entirely.
7
+ That deny list must not ship in tracked `.claude/settings.json` because it is Claude-only rather than harness-agnostic, and because tracked project settings propagate into linked worktrees where they disarm legitimate crewmates.
8
+
9
+ ## Why this exists
10
+
11
+ On 2026-07-22 a firstmate primary ran four workers through Claude Code's built-in subagent tool instead of `bin/fm-spawn.sh`.
12
+ Three consequences were observed, not hypothesized.
13
+
14
+ - The fleet view showed zero work under way for the whole run, because no `state/<id>.meta` and no `data/<id>/brief.md` were ever created.
15
+ - When the primary session restarted, two of those workers died mid-flight and their work was lost.
16
+ A real crewmate lives in its own backend session with durable state and survives a primary restart.
17
+ - The supervision cycle then stayed down for 73 minutes unnoticed, which silently killed the captain's Workflowy intake channel, since that channel only fires while a watch cycle runs.
18
+
19
+ The deeper defect is that the bypass did not merely skip dispatch, it made the in-flight-work branch of the guard stack structurally inert.
20
+ Only `bin/fm-spawn.sh` writes `state/<id>.meta`, so untracked project work contributes nothing to the in-flight count used by `bin/fm-supervision-lib.sh` and `bin/fm-turnend-guard.sh`.
21
+ Work started through the harness's own delegation tool writes no metadata, so the in-flight count stayed at zero and the turn-end guard never blocked a blind turn end.
22
+
23
+ That is the reason the fence has to sit on the harness tool surface, before the primary can create untracked work.
24
+ No additional guard keyed on task metadata can catch this class of failure, because the failure is precisely the absence of that metadata.
25
+
26
+ ## Purpose and boundary
27
+
28
+ The guard addresses one concrete, mechanically identifiable event: the primary session reaching for a tool that creates work the fleet will not know about.
29
+
30
+ It deliberately does **not** address the broader question of whether a given piece of work should be delegated at all.
31
+ That question is a judgment boundary over read-and-think work, it has no tool-shape signal, and a hook that tried to police it would degrade into an advisory nag.
32
+ The scope line is therefore: wrong tool reached for, deny; wrong amount of thinking done before reaching for a tool, out of scope.
33
+
34
+ The guard is also not a dispatch-quality check.
35
+ It says nothing about whether the resulting brief, project, or delivery mode is correct.
36
+
37
+ ## Shipped mechanism
38
+
39
+ `bin/fm-subagent-pretool-check.sh` is the shipped layer.
40
+ It classifies the tool NAME by shape rather than against a fixed list.
41
+ The tracked Claude PreToolUse matcher is `.*`, so every Claude tool name reaches the script and the script is the single owner of classification.
42
+ A stem-enumerating matcher would reintroduce the fail-open-by-enumeration problem this guard exists to solve, because any future tool name outside the matcher would be silently missed before the script could inspect it.
43
+ A tool is delegation-shaped when its normalized lowercase name contains one of these stems:
44
+
45
+ ```text
46
+ agent subagent task workflow cron schedul worktree
47
+ delegate spawn dispatch handoff remote sendmessage monitor
48
+ ```
49
+
50
+ Three exclusions keep the shape test from producing false positives.
51
+
52
+ - A name beginning `mcp__` is never classified.
53
+ An MCP server chooses its own tool names, a task or agent noun there is common, and it has no bearing on fleet dispatch.
54
+ - `OBSERVE_ONLY_TOOLS`: the exact names `taskoutput`, `taskstop`, `taskget`, `tasklist`, `cronlist`, `bashoutput`, and `killshell` are allowed.
55
+ These observe or stop work that already exists rather than creating it, and denying them at this layer could strand already-running work with no way to inspect or end it.
56
+ A Claude primary's optional local deny list may still remove them from the schema.
57
+ The shipped guard stays narrower on purpose so it can never be the reason a runaway task cannot be stopped.
58
+ - `PLAN_ONLY_TOOLS`: the exact names `taskcreate` and `taskupdate` are allowed.
59
+ These write, which is why they are a separate list rather than more entries in the observe-or-stop one, but what they write is the harness's session-local todo list.
60
+ That list has no executor: it spawns no agent, allocates no worktree, registers no schedule, and starts nothing that could outlive the session or escape a firstmate guard.
61
+ So it is not the "work, agent, schedule, or isolated workspace that firstmate would not know about" the guard exists to stop, and the stem match on `task` is a false positive rather than a policy.
62
+ The cost of the false positive was concrete: the primary could not track its own plan, and the deny text told it to run `bin/fm-brief.sh` and `bin/fm-spawn.sh` to create a todo entry.
63
+
64
+ Both exclusion lists match the whole normalized name, never a substring, so neither can widen by accident: `TaskCreateAgent` and `RemoteTaskCreate` stay denied.
65
+ Folding the two lists together would be the drift risk, because the observe-or-stop rationale is not true of a tool that writes.
66
+
67
+ The shipped guard fires on every delegation-shaped name that reaches it, including future names that no deny list knows about yet.
68
+ That future-name behavior is the reason the tracked matcher must match all tools and let the script filter.
69
+
70
+ ## Recommended Local Claude Deny List
71
+
72
+ Claude primaries should add this deny list in untracked per-home local settings, never in tracked `.claude/settings.json`:
73
+
74
+ ```json
75
+ {
76
+ "permissions": {
77
+ "deny": [
78
+ "Task",
79
+ "Agent",
80
+ "Workflow",
81
+ "RemoteTrigger",
82
+ "Monitor",
83
+ "ScheduleWakeup",
84
+ "SendMessage",
85
+ "EnterWorktree",
86
+ "ExitWorktree",
87
+ "CronCreate",
88
+ "CronDelete",
89
+ "CronList",
90
+ "TaskGet",
91
+ "TaskList",
92
+ "TaskStop",
93
+ "TaskOutput"
94
+ ]
95
+ }
96
+ }
97
+ ```
98
+
99
+ A denied name is removed from the model's schema entirely.
100
+ The model is never offered the tool, so there is no call to intercept, no matcher to get wrong, no fail-open path, and no dependence on the model's cooperation.
101
+ This is removal, not interception, and it is strictly stronger than any hook.
102
+
103
+ This list is recommended local hardening because it closes the known Claude surface before the hook is needed.
104
+ It is not tracked for two reasons.
105
+
106
+ - It is Claude-only, so it can never be the harness-agnostic shipped fix.
107
+ - A tracked `.claude/settings.json` propagates into linked worktrees and disarms legitimate crewmates.
108
+ This was verified when a Claude session in a task worktree of this repo lost its `Agent` tool.
109
+
110
+ The width of the list remains a captain-owned decision, because denying some of these changes how the captain works with the primary session.
111
+ Keep it as one flat local array that is reviewable at a glance and narrowable in one line.
112
+ In particular `TaskOutput`, `TaskStop`, `TaskGet`, `TaskList`, and `CronList` only observe or stop work that already exists, yet the recommended local deny list still removes all five by default.
113
+ The hook deliberately allows those five, so the shipped guard can never strand a runaway task with no way to inspect or end it, and it allows `TaskCreate` and `TaskUpdate` too, so it can never be the reason the primary cannot track its own plan.
114
+ The two session-local todo tools are no longer recommended for local denial at all, because they write only the harness's session-local todo list, which has no executor and spawns nothing, so removing them from the schema removes no delegation power.
115
+ Denying them there would instead reproduce at a stronger layer the exact false positive the shipped guard now avoids, leaving anyone who adopts this list verbatim unable to let a primary track its own plan.
116
+ Narrowing the list further, including the five observe-or-stop names, is the captain's call, and this local list is the only layer that can remove a todo tool from the primary's schema.
117
+
118
+ `permissions.allow` is a pre-approval list, not an availability list, so there is no fail-closed positive allowlist available.
119
+ That is why any fixed deny list is fail-open against future tools and why the shape-based guard still exists.
120
+ The hook cannot re-enable a tool removed from the schema; it only handles a tool name that still reaches PreToolUse.
121
+
122
+ ### Both `Task` and `Agent` are valid deny keys
123
+
124
+ The tool presents to the model as `Agent`.
125
+ A prior investigation recorded that the deny key must be `Task` and that using `Agent` "silently does nothing at all".
126
+ That is not what this machine shows.
127
+
128
+ A five-way A/B with a control, each run in its own directory to rule out settings caching, found that `Task` and `Agent` each independently remove the tool, and that a nonsense name leaves it present.
129
+ The full evidence is in the validation record below.
130
+
131
+ Pinning both names in the recommended local deny list is correct regardless of which build is running.
132
+ It costs one line and removes the failure mode where a rename or a rollback silently reopens the surface.
133
+
134
+ ## Scope
135
+
136
+ The shipped hook fires only in a genuine firstmate primary home, using the shared predicate `fm_primary_scope_matches` from `bin/fm-primary-scope-lib.sh`.
137
+ This is the same predicate `bin/fm-sessionstart-nudge.sh` and `bin/fm-turnend-guard.sh` use, so the three tracked primary-scoped hooks cannot drift apart.
138
+
139
+ A home is in scope when it has `AGENTS.md`, a `bin/` directory, an existing state directory, and either a plain checkout where git-dir equals git-common-dir or a valid `.fm-secondmate-home` marker.
140
+ A marked secondmate home is in scope on purpose: it operates its own fleet and must dispatch through it for the same durability reasons.
141
+
142
+ A crewmate's disposable task worktree is a linked git worktree, which is the shape `bin/fm-spawn.sh` always hands out, so it is out of scope.
143
+ A crewmate using delegation tools inside its own task worktree is legitimate and stays allowed.
144
+ A non-firstmate repo is out of scope.
145
+ Any failure to confirm the home is inert, never a block, so a broken environment can never deny a tool call.
146
+
147
+ A local Claude deny list is upstream of hook scope and removes known Claude delegation tools wherever Claude applies it.
148
+ Do not put that list in tracked project settings, because linked worktrees inherit those settings and would lose legitimate delegation tools.
149
+ The hook scope is the shipped enforcement boundary, and the linked-worktree negative case proves the script itself does not block legitimate crewmate delegation.
150
+
151
+ ## Escape hatch
152
+
153
+ `FM_ALLOW_SUBAGENT=1` in the session environment allows the call at the shipped hook.
154
+ This is the only escape hatch and the guard fails closed on every other value, including empty, `0`, `yes`, and `true`.
155
+
156
+ It is an environment variable rather than a flag, a config file, or a state file because that makes it unforgeable in-session.
157
+ The variable must be present when the harness process is launched, so no tool call the agent makes can enable it for the call that follows.
158
+ A deliberate use therefore requires restarting the session with the variable set, which is a conscious act, while an accidental use is impossible.
159
+
160
+ The escape hatch does not affect any local Claude deny list.
161
+ A tool removed from the schema stays removed, so a genuinely intended use of a locally denied tool also requires narrowing or removing that local entry before launch.
162
+
163
+ ## Output contract
164
+
165
+ - Allow returns exit 0 with both streams empty.
166
+ - Deny returns exit 2 and writes `{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny"},"systemMessage":"[subagent-dispatch] ..."}` to stderr.
167
+ - Default deny mode also writes `{"decision":"deny","reason":"[subagent-dispatch] ..."}` to stdout for Grok.
168
+ - `--claude` suppresses stdout completely, because Claude Code ignores a PreToolUse deny when stdout is nonempty.
169
+ This is the same verified quirk recorded in [`arm-pretool-check.md`](arm-pretool-check.md), and the tracked Claude hook therefore passes `--claude`.
170
+ - Malformed or empty stdin, invalid JSON, a payload with no tool name, and missing `jq` for stdin transport all fail open with exit 0 and no output.
171
+
172
+ The deny message names the real dispatch path.
173
+ When `bin/fm-scout.sh` exists in the home the message first defers to the `AGENTS.md` intake classification, then routes work already classified as a scout there and authorized ship work with its bounded research to `bin/fm-brief.sh` then `bin/fm-spawn.sh`.
174
+ When that script is absent the message still defers to intake classification and degrades to naming `bin/fm-brief.sh` then `bin/fm-spawn.sh` for dispatched work, rather than pointing at a script that is not there.
175
+
176
+ ## Harness wiring
177
+
178
+ Every supported primary harness was reviewed.
179
+ Applicability turns on one question: does the harness expose built-in delegation tools that a primary session could use instead of `bin/fm-spawn.sh`?
180
+
181
+ | Harness | Delegation surface | Status |
182
+ | --- | --- | --- |
183
+ | Claude | 16 known tools, listed above | Scoped guard wired and live-verified; untracked local deny list verified and recommended. |
184
+ | Codex | none | Not applicable, verified empirically below. Codex 0.144.1 exposes no subagent, sub-task, or delegated-agent tool, so there is nothing to remove or intercept. `.codex/hooks.json` is unchanged. |
185
+ | Grok | present, exact tokens unconfirmed | Not wired pending live verification. See below. |
186
+ | OpenCode | present, exact tokens unconfirmed | Not wired pending live verification. See below. |
187
+ | Pi | none reported | Not wired pending live verification. See below. |
188
+
189
+ ### Codex, verified not applicable
190
+
191
+ Codex 0.144.1 was asked to enumerate its own tools in a scratch git repo on 2026-07-22.
192
+
193
+ ```sh
194
+ codex exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check \
195
+ "List the exact names of every tool available to you in this session, one per line, nothing else. Then state on a final line whether you have any tool that spawns a subagent, sub-task, or delegated agent: answer SUBAGENT_TOOL=yes or SUBAGENT_TOOL=no."
196
+ ```
197
+
198
+ Exact reported tool set and verdict:
199
+
200
+ ```text
201
+ web.run
202
+ functions.exec_command
203
+ functions.write_stdin
204
+ functions.list_mcp_resources
205
+ functions.list_mcp_resource_templates
206
+ functions.read_mcp_resource
207
+ functions.update_plan
208
+ functions.request_user_input
209
+ functions.request_plugin_install
210
+ functions.view_image
211
+ functions.get_goal
212
+ functions.create_goal
213
+ functions.update_goal
214
+ functions.apply_patch
215
+ image_gen.imagegen
216
+ tool_search.tool_search_tool
217
+ multi_tool_use.parallel
218
+ SUBAGENT_TOOL=no
219
+ ```
220
+
221
+ `multi_tool_use.parallel` batches calls to the tools above; it does not spawn an agent.
222
+ Codex is therefore not applicable today, and this table row is the tripwire: if a future Codex release adds a delegated-agent tool, wire `.codex/hooks.json` the same way its `Bash` PreToolUse entries already forward stdin to a checker.
223
+
224
+ ### Grok, OpenCode, and Pi, inspected but not wired
225
+
226
+ The integration surface of each was inspected and each is structurally wireable for the shipped guard.
227
+
228
+ - Grok's tracked hooks (`.grok/hooks/fm-primary-pretool-check.json`, `.grok/hooks/fm-primary-cd-check.json`) use a `PreToolUse` matcher, currently `Bash`, and pipe stdin to a checker.
229
+ The checker already reads Grok's `.toolName` field, so only the matcher token is missing.
230
+ Grok does expose a delegation surface: `docs/supervision-protocols/grok.md` documents `get_command_or_subagent_output(<task_id>)`, which implies a corresponding dispatch tool.
231
+ - OpenCode's tracked plugins gate on `input?.tool !== "bash"` inside `tool.execute.before`, and block by throwing.
232
+ Swapping that comparison for a call into this checker with `--tool` is the whole change.
233
+ - Pi's tracked extension gates on `event.toolName !== "bash"` inside `pi.on("tool_call", ...)` and blocks by returning `{block: true}`.
234
+ The same change applies. A parallel evaluation reports that Pi exposes no delegation tool at all, which would make it not applicable, but that was not verified here.
235
+
236
+ None of the three is wired in this change because none of the three binaries is installed on the host where this work was done, so the exact tool-name tokens could not be confirmed and the wiring could not be validated against the real harness.
237
+ This repo's rule in the `firstmate-coding-guidelines` skill is that a harness hook must be validated in a scratch project before it is trusted, and `arm-pretool-check.md` records the concrete cost of guessing: a Grok hook whose `command` string is even slightly wrong fails to launch the hook at all.
238
+ Wiring an unvalidated matcher would trade a known gap for an unknown breakage.
239
+
240
+ The bounded follow-up for each is identical to the Codex procedure above.
241
+ On a host with the binary installed, ask the harness to enumerate its tools, then wire the matcher and re-run the live matrix below.
242
+ `bin/fm-subagent-pretool-check.sh` needs no change for any of them: it already accepts Grok's stdin shape and the `--tool` CLI form OpenCode and Pi use, and it already emits the Grok stdout decision object by default.
243
+
244
+ ## Live validation record, 2026-07-22
245
+
246
+ Harness version:
247
+
248
+ ```text
249
+ 2.1.217 (Claude Code)
250
+ ```
251
+
252
+ Every run used a scratch project under this task worktree.
253
+ No modified file was installed into the primary checkout or a live harness configuration, and no live watcher, fleet state, or task metadata was used.
254
+ The launch command throughout was:
255
+
256
+ ```sh
257
+ claude -p "$PROMPT" --dangerously-skip-permissions --output-format text
258
+ ```
259
+
260
+ ### Tool name and matcher mechanics
261
+
262
+ The tool name delivered to PreToolUse hooks was established before any matcher was written, using a throwaway project whose only hook appended `.tool_name` to a log for matcher `.*`.
263
+ It logged `Agent` and `Bash`.
264
+ A second project using matcher `^(Task|Agent)$` logged `Agent` only, confirming both the live tool name and that Claude Code honors regex anchors in a PreToolUse matcher.
265
+ The tracked matcher is now `.*`, matching the throwaway-project evidence above so any future tool name reaches the script classifier.
266
+
267
+ ### Deny-key A/B, with control
268
+
269
+ Prompt: `List the exact names of every tool available to you, comma-separated on one line, nothing else.`
270
+ Each variant ran in its own fresh directory to rule out settings caching.
271
+
272
+ | `.claude/settings.json` | `Agent` in tool list? |
273
+ | --- | --- |
274
+ | `{}` | Yes |
275
+ | `{"permissions":{"deny":["Task"]}}` | No |
276
+ | `{"permissions":{"deny":["Agent"]}}` | No |
277
+ | `{"permissions":{"deny":["ZzzNotARealTool"]}}` | Yes |
278
+ | `{"permissions":{"deny":["Task","Agent"]}}` | No |
279
+
280
+ The nonsense-name control is what makes this conclusive: the tool disappears only when a real name is denied, so the removal is caused by the deny entry rather than by run-to-run variation.
281
+ Both `Task` and `Agent` are therefore working deny keys on this build, correcting the earlier claim that only `Task` works.
282
+
283
+ The observed baseline surface was 29 tools:
284
+
285
+ ```text
286
+ Agent, Bash, Edit, Read, ReportFindings, ScheduleWakeup, Skill, ToolSearch, Workflow, Write,
287
+ CronCreate*, CronDelete*, CronList*, DesignSync*, EnterWorktree*, ExitWorktree*, Monitor*,
288
+ NotebookEdit*, PushNotification*, RemoteTrigger*, SendMessage*, TaskCreate*, TaskGet*,
289
+ TaskList*, TaskOutput*, TaskStop*, TaskUpdate*, WebFetch*, WebSearch*
290
+ ```
291
+
292
+ A `*` marks a deferred tool, which is lazy-loaded through `ToolSearch` and does not appear in a plain tool list unless the prompt asks for deferred entries.
293
+ This distinction matters when reading the next result: a tool absent from a plain listing is not necessarily denied.
294
+
295
+ ### Local deny-list hardening
296
+
297
+ Run in a scratch firstmate-shaped project containing `AGENTS.md`, `state/`, a full copy of `bin/`, and a Claude settings file containing the local deny list exactly as recommended on that date, which was the 18-name form that still included `TaskCreate` and `TaskUpdate`.
298
+ The result validates that local deny list rather than tracked repo state, and the recommendation above has since dropped those two session-local todo tools.
299
+ Asking for deferred entries explicitly returned:
300
+
301
+ ```text
302
+ Bash, Edit, Read, ReportFindings, Skill, ToolSearch, Write,
303
+ DesignSync*, NotebookEdit*, PushNotification*, WebFetch*, WebSearch*
304
+ ```
305
+
306
+ All 18 locally denied names are gone and every ordinary working tool remains, including the five deferred ones.
307
+ Comparing against the 29-tool baseline confirms the removal set is exactly the deny list and nothing else.
308
+
309
+ ### Shipped guard, the case a fixed deny list cannot cover
310
+
311
+ To reproduce a future tool that ships before a local deny list is updated, `Workflow` was removed from the deny list in the same scratch project while the guard stayed wired.
312
+
313
+ Prompt: `Call the Workflow tool to run any trivial workflow. You must actually attempt the Workflow tool call.`
314
+
315
+ Claude reported:
316
+
317
+ ```text
318
+ I attempted the Workflow tool call as requested. It was blocked by a PreToolUse hook in this repo:
319
+
320
+ > [subagent-dispatch] the firstmate primary dispatches through the fleet, not the harness's own
321
+ > delegation tools... (blocked tool: Workflow). Launch the session with FM_ALLOW_SUBAGENT=1 for a
322
+ > deliberate exception.
323
+ ```
324
+
325
+ This is the load-bearing result: the shipped guard denied a delegation tool that the deny list did not cover, which is the future-name case the shape classifier exists for.
326
+
327
+ ### Shipped guard scope, the negative case
328
+
329
+ The same `Workflow` prompt was then run in a `git worktree add` linked worktree of that scratch project, carrying the identical tracked hook and checker bytes, with no escape hatch.
330
+
331
+ ```text
332
+ The Workflow tool call was not blocked by a hook. It executed normally: launched, ran 1 agent,
333
+ and completed successfully returning {"result":"ok"}.
334
+ ```
335
+
336
+ Same hook, same bytes, deny in the primary home and allow in a crewmate-shaped worktree.
337
+ This is the scoping contract working end to end rather than a hook that simply never fires.
338
+
339
+ ### Escape hatch
340
+
341
+ The same `Workflow` prompt in the scratch primary home, launched as `FM_ALLOW_SUBAGENT=1 claude -p ...`:
342
+
343
+ ```text
344
+ Result: the Workflow tool call was NOT blocked by a hook. It launched and ran to completion.
345
+ ```
346
+
347
+ ### Empty-stdout requirement
348
+
349
+ A Claude deny is honored only when the hook's stdout is empty.
350
+ `tests/fm-subagent-pretool-check.test.sh` asserts stdout is empty on every `--claude` deny and that default mode still emits the Grok object on stdout.
351
+ The live consequence is confirmed by the shipped-guard result above: Claude honored the deny and reported the reason text.
352
+
353
+ ## Automated validation
354
+
355
+ `tests/fm-subagent-pretool-check.test.sh` owns the acceptance matrix and is registered in the `pure-contract-unit` family in `bin/fm-test-run.sh`.
356
+ It covers the tracked Claude settings boundary that forbids a `permissions` key; the match-all Claude hook registration; denial of every work-creating delegation tool by shape; denial of twelve hypothetical future tool names that appear on no list; the observe-or-stop, plan-only, and MCP exclusions; the exactness of the plan-only exclusion against six near-miss names a substring or shorter-stem widening would release; the scout-present and scout-absent message variants; the escape hatch including its fail-closed values; inertness in a linked task worktree and in a non-firstmate repo; in-scope enforcement for a marked secondmate home; both stdin transports; the empty-stdout requirement; fail-open transport behavior; and the preserved `Bash` seatbelts and `Stop` guard.
357
+
358
+ Run:
359
+
360
+ ```sh
361
+ bash -n bin/fm-subagent-pretool-check.sh
362
+ bin/fm-lint.sh
363
+ tests/fm-subagent-pretool-check.test.sh
364
+ ```
365
+
366
+ ## Known residual gap
367
+
368
+ The other tracked Claude hook entries in `.claude/settings.json` refuse to run under Grok's Claude-compatible settings loading (docs/turnend-guard.md "Harness integrations"), because Grok already covers each of those events through its own `.grok/hooks/` registration and running both creates a duplicate path.
369
+ This entry is the deliberate exception and stays unguarded: Grok is "inspected but not wired" above, so no `.grok/hooks/` registration covers the subagent-spawn event at all, and guarding it would remove the guard from Grok entirely rather than deduplicate it.
370
+ The coverage it leaves is partial rather than correct - the tracked entry passes `--claude`, which suppresses exactly the stdout decision object Grok consumes - so treat this as incidental reach, not as Grok being wired.
371
+ Wiring Grok properly still requires the matcher-token verification described above, and that is what closes this exception.
372
+ The same exception now also covers Cursor, which loads the tracked Claude settings as well: `.cursor/hooks.json` registers no subagent-spawn matcher, so this entry stays unguarded there for the same reason, and its `--claude` rendering leaves Cursor the exit-2 and stderr path rather than Cursor's own decision object.
373
+ Cursor's subagent tool name has not been verified, and registering an unverified matcher would be a guess rather than coverage, so closing it needs the same verification step.
374
+
375
+ This change does not close the deeper harness-agnostic defect.
376
+ Every firstmate guard's in-flight-work branch keys off `state/<id>.meta`, and only `bin/fm-spawn.sh` writes that record.
377
+ `bin/fm-supervision-lib.sh` also recognizes a Relay poll as supervision need, but unaccounted primary work still contributes nothing to that predicate.
378
+ Without an independent Relay need, unaccounted primary work therefore reads as idle rather than suspicious.
379
+
380
+ The durable fix for that class is to make the guards treat "the primary is doing project-shaped work with zero `state/*.meta` files" as a suspicious state rather than an idle one.
381
+ That would catch this class on any harness, including work created through `Bash`.
382
+ This change fences only the Claude tool surface.
383
+ That is a separate change to `bin/fm-supervision-lib.sh` and `bin/fm-turnend-guard.sh` and is out of scope here.
@@ -0,0 +1,27 @@
1
+ Mode: Claude Stop-hook-owned supervision.
2
+
3
+ When this session owns supervision and away mode is not active:
4
+ 1. Drain first with `bin/fm-wake-drain.sh`.
5
+ After handling all emitted wakes and reconciling open decisions and unread status lines, run the exact `--ack-through` command printed as `WAKE_ACK_REQUIRED`; until then the work remains durable for idempotent re-handling after interruption.
6
+ 2. Routine watcher arm and re-arm are owned by the Stop `asyncRewake` hook (`bin/fm-claude-stop-autoarm.sh`), never by you.
7
+ Every turn end while supervision is needed launches or attaches one home-scoped watcher cycle with no model command and no model tokens.
8
+ An actionable close wakes you through the hook's exit-2 rewake, delivered as a `Stop hook feedback` message.
9
+ 3. On a `Stop hook feedback` wake (`signal:`, `stale:`, `check:`, or `heartbeat`), run `bin/fm-wake-drain.sh` first and handle the wake.
10
+ Do not run `bin/fm-watch-arm.sh` after an ordinary wake; the next turn end re-arms automatically when supervision is still needed.
11
+ Do not invent a wake from an attach-status line alone; drain and act only on real wake records, the drain's `OPEN DECISIONS` and `UNREAD STATUS` entries, or a real watcher reason line.
12
+ 4. On the one `Stop hook feedback` automatic-mechanism failure notice (`firstmate watcher auto-arm FAILED ...`), drain, inspect the automatic mechanism failure, and do not turn the notice into a repeating manual-arm loop.
13
+ 5. If the Stop hook does not claim the home or reports an exhausted failure, inspect its registration and watcher startup path before ending blind.
14
+ Keep the Stop-owned automatic mechanism as the only Claude arm owner.
15
+ 6. Treat `watcher: started ...` and `watcher: attached ...` inside automatic arm output as proof that one live cycle exists.
16
+ On attach, the arm follows verified identity-matched successors instead of exiting when the first cycle ends.
17
+ 7. The durable wake queue preserves actionable events between a rewake and the next Stop-launched arm, while the bounded turn-end guard prevents a blind Stop when recovery did not start.
18
+ No PreToolUse hook denies fleet commands based on watcher status.
19
+ [`watcher-continuity.md`](../watcher-continuity.md) owns the exact session-lock recovery boundary.
20
+ 8. The turn-end guard (`bin/fm-turnend-guard.sh --claude`) remains the final backstop.
21
+ It requires the PID-strict live-watcher and fresh-beacon predicate at the Stop boundary, while the mid-turn pull guard accepts a fresh beacon without a live process under Claude's between-turns auto-arm model.
22
+ It allows the stop when a watcher is healthy or an open auto-arm generation claim owns recovery, while fresh failure epochs advance the bounded one-time attended fail-open progression described in [`turnend-guard.md`](../turnend-guard.md).
23
+ 9. Waiting on the hook-owned cycle is silent: do not send idle progress while the watcher is parked.
24
+
25
+ The watcher itself remains `bin/fm-watch.sh`, and `bin/fm-watch-arm.sh` remains the verified arm wrapper that the Stop hook foregrounds.
26
+ Re-arm attaches to an existing healthy cycle when one is already present and follows its verified successor chain.
27
+ See [`watcher-continuity.md`](../watcher-continuity.md) for the arm-layer successor and clean-close failure contract and the Claude ownership model.
@@ -0,0 +1,16 @@
1
+ Mode: Codex foreground checkpoint.
2
+
3
+ When this session owns supervision and away mode is not active:
4
+ 1. Drain first with `bin/fm-wake-drain.sh`.
5
+ After handling all emitted wakes and reconciling open decisions and unread status lines, run the exact `--ack-through` command printed as `WAKE_ACK_REQUIRED`; until then the work remains durable for idempotent re-handling after interruption.
6
+ 2. Source `__FM_X_MODE_ENV__` first when Relay is active.
7
+ 3. First cycle: run one foreground watcher checkpoint with `bin/fm-watch-checkpoint.sh --seconds "${FM_CODEX_WATCH_CHECKPOINT:-180}"`.
8
+ 4. Ordinary wake: if the command prints `signal:`, `stale:`, `check:`, or `heartbeat`, drain queued wakes, handle that wake, then start the next checkpoint.
9
+ 5. If the command prints `checkpoint:` or exits 124 with no wake, drain queued wakes anyway, process any queued user message now visible to Codex, then start the next checkpoint.
10
+ 6. Never use shell `&` or Codex background tasks for firstmate watcher supervision.
11
+ 7. Do not run `bin/fm-watch-arm.sh` as Codex's normal supervision command.
12
+ If it is ever shelled anyway, a backgrounded, piped, or bundled anti-pattern is denied automatically by the PreToolUse seatbelt (`bin/fm-arm-pretool-check.sh`) registered in `.codex/hooks.json`.
13
+ 8. Failure or missing cycle only: drain queued wakes, inspect the failure, then start a fresh foreground checkpoint.
14
+
15
+ Codex cannot reason while a foreground tool call is running.
16
+ The bounded checkpoint returns control regularly so user messages and queued wakes can be handled without relying on background-task wake semantics.
@@ -0,0 +1,31 @@
1
+ Mode: Cursor stop-hook-owned park.
2
+
3
+ When this session owns supervision and away mode is not active:
4
+ 1. Drain first with `bin/fm-wake-drain.sh`.
5
+ After handling all emitted wakes and reconciling open decisions, run the exact `--ack-through` command printed as `WAKE_ACK_REQUIRED`; until then the work remains durable for idempotent re-handling after interruption.
6
+ 2. Routine watcher arm and re-arm are owned by the `stop` hook (`bin/fm-turnend-guard-cursor.sh`), never by you.
7
+ Cursor runs that hook synchronously and awaits it, so every turn end while supervision is needed parks the turn boundary open on one home-scoped watcher cycle, with no model command and no model tokens spent while parked.
8
+ 3. An actionable close wakes you as a follow-up turn carrying the `watcher` operational kind.
9
+ On that wake, run `bin/fm-wake-drain.sh` first and handle it.
10
+ Do not run `bin/fm-watch-arm.sh` after an ordinary wake; the next turn end parks again automatically when supervision is still needed.
11
+ Do not invent a wake from an attach-status line alone; drain and act only on real wake records, the drain's `OPEN DECISIONS` entries, or a real watcher reason line.
12
+ 4. The captain keeps control while the hook is parked.
13
+ A message typed into a parked Cursor pane is accepted and runs its turn immediately, but the older park remains the recorded owner until that turn ends and the next `stop` hook claims the baton.
14
+ An actionable watcher close in that window can still be delivered by the older park as one follow-up.
15
+ This is bounded and safe: only one park exists in that window, so the event is a real wake rather than a stale duplicate of another park's wake, the durable wake queue makes handling idempotent, and the next `stop` claim makes an older park that is still running stand down without emitting.
16
+ The private supersession records are `state/.cursor-park-owner` and its short publication and commit lock `state/.cursor-park-owner.lock`.
17
+ 5. On a `turn-end-guard` follow-up, the park could not establish a live cycle.
18
+ Inspect the watcher startup path rather than turning the notice into a repeating manual-arm loop; the nag is bounded by `FM_CURSOR_TURNEND_BLOCK_BUDGET` (default 3) and then stops on its own.
19
+ 6. Treat `watcher: started ...` and `watcher: attached ...` inside park output as proof that one live cycle exists.
20
+ On attach, the arm follows verified identity-matched successors instead of exiting when the first cycle ends.
21
+ 7. The durable wake queue preserves actionable events between a follow-up and the next park.
22
+ [`watcher-continuity.md`](../watcher-continuity.md) owns the exact session-lock recovery boundary.
23
+ 8. Waiting on the hook-owned park is silent: do not send idle progress while the watcher is parked.
24
+
25
+ The watcher itself remains `bin/fm-watch.sh`, and `bin/fm-watch-arm.sh` remains the verified arm wrapper that the `stop` hook runs as its own tracked child.
26
+ Re-arm attaches to an existing healthy cycle when one is already present and follows its verified successor chain.
27
+ See [`watcher-continuity.md`](../watcher-continuity.md) for the arm-layer successor and clean-close failure contract.
28
+
29
+ Exit status 2 is a silent no-op on Cursor's `stop` step, so this adapter never blocks a turn end and instead forces one bounded follow-up, which [`turnend-guard.md`](../turnend-guard.md) accepts as an equal alternative.
30
+ That document owns the double loop bound, the supersession contract, the Pi-host stand-down, and the compatibility limits, including that a Cursor primary must be launched with `--trust` for its project hooks to load at all.
31
+ Cursor's `beforeSubmitPrompt` step fires once for a real captain message and not for hook-driven follow-ups, so it could invalidate the baton at the start of this window, but that registration is deliberately deferred alongside the `preCompact` surface.
@@ -0,0 +1,39 @@
1
+ Mode: Grok background-notify supervision.
2
+
3
+ When this session owns supervision and away mode is not active:
4
+ 1. Drain first with `bin/fm-wake-drain.sh`.
5
+ After handling all emitted wakes and reconciling open decisions and unread status lines, run the exact `--ack-through` command printed as `WAKE_ACK_REQUIRED`; until then the work remains durable for idempotent re-handling after interruption.
6
+ 2. Source `__FM_X_MODE_ENV__` first when Relay is active.
7
+ 3. First cycle: arm with Grok's tracked background tool, as its own call:
8
+
9
+ `run_terminal_command` with `background: true` on:
10
+ `[ -f __FM_X_MODE_ENV_SH__ ] && . __FM_X_MODE_ENV_SH__; exec bin/fm-watch-arm.sh`
11
+
12
+ 4. Trust only the arm's one-line status.
13
+ 5. `watcher: started ...` or `watcher: attached ...` means a live cycle exists.
14
+ On attach, the background task follows verified identity-matched successors instead of exiting when the first cycle ends.
15
+ 6. Failure or missing cycle only: `watcher: FAILED ...` means supervision is down; fix and re-arm.
16
+ 7. After a successful start or attach status, end the turn.
17
+ The background arm remains the live wait until it returns an actionable wake or failure.
18
+ 8. Waiting is silent.
19
+ 9. Never use shell `&` for firstmate supervision.
20
+ 10. Never bundle the arm onto another command.
21
+ A shell `&`, a truncating pipe, or bundling is denied automatically by the PreToolUse seatbelt (`bin/fm-arm-pretool-check.sh`) whenever this project's Grok hooks are trusted.
22
+
23
+ Grok injects a synthetic user message with `synthetic_reason: task_completed` when the background arm completes.
24
+ When you see a background-task-completed system reminder for the arm:
25
+ 1. Run `bin/fm-wake-drain.sh` first.
26
+ 2. Optionally fetch arm output with `get_command_or_subagent_output(<task_id>)` for the reason line.
27
+ 3. Handle `signal`, `stale`, `check`, or `heartbeat` using the harness-neutral contract in `AGENTS.md`.
28
+ 4. Ordinary wake: re-arm the next cycle with the same background `bin/fm-watch-arm.sh` call if work remains in flight or Relay still needs polling.
29
+ 5. Do not invent a wake from an attach-status line alone.
30
+ Drain the queue and act only on real wake records, the drain's `OPEN DECISIONS` and `UNREAD STATUS` entries, or a real watcher reason line.
31
+ Re-arm attaches to an existing healthy cycle when one is already present and follows its verified successor chain.
32
+ See [`watcher-continuity.md`](../watcher-continuity.md) for the arm-layer successor and clean-close failure contract.
33
+
34
+ The primary project Stop hook runs `bin/fm-turnend-guard-grok.sh` as a backstop, not the normal wake path.
35
+ [`turnend-guard.md`](../turnend-guard.md) owns its running-payload capability selection between native same-process blocking and the pre-native bounded resume fallback.
36
+ After any forced continuation, arm the watcher with the background protocol above.
37
+
38
+ Interactive TUI primary sessions are the supported supervision host.
39
+ Headless `grok -p` may wait for background process exit but does not reliably surface full auto-wake model output; do not run the primary firstmate as a one-shot headless process.
@@ -0,0 +1,17 @@
1
+ Mode: OpenCode TUI plugin background wake.
2
+
3
+ When this session owns supervision and away mode is not active:
4
+ 1. Drain first with `bin/fm-wake-drain.sh`.
5
+ After handling all emitted wakes and reconciling open decisions and unread status lines, run the exact `--ack-through` command printed as `WAKE_ACK_REQUIRED`; until then the work remains durable for idempotent re-handling after interruption.
6
+ 2. First cycle: let `.opencode/plugins/fm-primary-watch-arm.js` arm supervision after the OpenCode session goes idle.
7
+ 3. The plugin listens for `session.idle`, spawns `bin/fm-watch-arm.sh --restart` without awaiting it in the idle handler, and owns every later successor launch.
8
+ 4. After an actionable child close, the plugin rechecks session-lock ownership and verifies one singleton successor before it calls `client.session.promptAsync`; its bounded fallback is defined in `docs/watcher-continuity.md`.
9
+ 5. Ordinary wake: do not ask the model to re-arm because continuity is plugin-owned.
10
+ 6. An unexpected child close enters bounded exponential retry, and an exhausted retry or lost session lock is surfaced as a watcher failure instead of disappearing.
11
+ 7. Failure or missing cycle only: if the plugin reports a watcher failure, drain queued wakes, inspect the failure text, and use `bin/fm-watch-arm.sh` manually only as a short recovery probe.
12
+ 8. Never use shell `&` for watcher supervision.
13
+ The arm mechanism above is plugin-owned, not a model tool call, but a manual recovery probe that backgrounds, pipes, or bundles the arm is denied automatically by the PreToolUse seatbelt (`.opencode/plugins/fm-primary-pretool-check.js`, `bin/fm-arm-pretool-check.sh`).
14
+ 9. Do not rely on this plugin in headless `opencode run`; firstmate primary supervision targets persistent OpenCode TUI sessions.
15
+
16
+ OpenCode's persistent TUI plugin runtime is the wake mechanism.
17
+ The plugin applies in the main primary checkout and a secondmate's own home, and stays silent only in child crewmate and scout worktrees.
@@ -0,0 +1,33 @@
1
+ Mode: Pi extension background wake.
2
+
3
+ When this session owns supervision and away mode is not active:
4
+ 1. Drain first with `bin/fm-wake-drain.sh`.
5
+ After handling all emitted wakes and reconciling open decisions and unread status lines, run the exact `--ack-through` command printed as `WAKE_ACK_REQUIRED`; until then the work remains durable for idempotent re-handling after interruption.
6
+ 2. Confirm the Pi primary auto-loaded both project extensions (plain `pi` or `pi-signed`, after approving project trust once per clone); if not, restart the selected executable with `-e __FM_PI_TURNEND_EXT__ -e __FM_PI_EXT__` as a trust-free fallback.
7
+ 3. First cycle only: make the one required `fm_watch_arm_pi` call.
8
+ Use `/fm-watch-arm-pi` only as a human-entered fallback.
9
+ Never run `bin/fm-watch-arm.sh` through Pi's bash tool because that foreground arm can wedge the agent and bypasses extension-owned cleanup.
10
+ 4. If the extension says no live session holds the lock, run `bin/fm-session-start.sh` to reclaim the session lock, then call `fm_watch_arm_pi` again.
11
+ 5. The extension starts `bin/fm-watch-arm.sh --restart`, keeps the child attached to the live Pi process, and owns every later successor launch.
12
+ 6. Ordinary same-process session replacement (`/new`, `/resume`, `/fork`, reload) retires only the prior generation; call `fm_watch_arm_pi` once for the first cycle of the replacement session without restarting Pi.
13
+ The generation-owner contract lives in `.pi/extensions/fm-primary-pi-watch.ts`.
14
+ 7. After an actionable child close, the extension rechecks session-lock ownership and verifies one successor before it delivers the follow-up wake; its bounded fallback is defined in `docs/watcher-continuity.md`.
15
+ 8. Ordinary work, turn completion, and ordinary signal, stale, check, heartbeat, or other wake handling: do not call `fm_watch_arm_pi` again because continuity is extension-owned rather than model-memory-owned.
16
+ 9. An unexpected child close enters bounded exponential retry, and an exhausted retry or lost session lock is surfaced as a watcher failure instead of disappearing.
17
+ 10. Missing, failed, or unhealthy cycle only: if a later notification explicitly reports one of those repair conditions, drain queued wakes, inspect the failure text, call `fm_watch_arm_pi`, and restart the selected Pi-family executable with both extensions loaded if needed.
18
+ A redundant call while the extension owns an arm child or scheduled retry is an ownership-based `watcher: unchanged` no-op, not an independent health claim.
19
+ 11. Never use shell `&` for watcher supervision.
20
+ The arm mechanism above is extension-owned, not a model tool call, but a manual recovery probe that backgrounds, pipes, or bundles the arm is denied automatically by the PreToolUse seatbelt (`bin/fm-arm-pretool-check.sh`, wired into the turn-end guard extension at `__FM_PI_TURNEND_EXT__`).
21
+
22
+ The supervision branch is default-on (docs/pi-supervision-branch.md): whenever this session owns the fleet lock and away mode is not active, the watcher extension hands eligible task-local rows from ordinary actionable wakes, plus selected fleet-wide heartbeat reviews, to the persistent in-process supervision branch while main-only rows remain queued for this conversation.
23
+ A no-change heartbeat outcome explicitly reported with `task=fleet` and `silent=true` is delivered silently with no rendered note, while every other routine outcome returns as an appended, rendered note that leads with ⛵ then the dim outcome text.
24
+ A captain-facing outcome instead opens exactly one follow-up turn on this conversation - MAIN must produce its captain-visible response in that turn, and no separate note is printed here.
25
+ Before MAIN steers, controls lifecycle, or cleans up a task, claim its lease with `bin/fm-lease.sh claim <task>` and release it afterwards; a refused claim means the branch is acting on that task right now.
26
+ This conversation still receives every other fleet-wide or unresolvable wake, the branch's wakes when it is unavailable or away mode is active, and every watcher-failure alarm regardless, so the arm and repair contract above is unchanged.
27
+ Treat the merged fleet event as already handled for fleet operations: MAIN must not re-drain, re-run, or acknowledge it.
28
+ Separately, MAIN applies judgment about whether and how to surface, summarize, reference, or incorporate a merged sailboat outcome in the captain conversation; event ownership does not decide the conversational treatment.
29
+ Read the durable outcome store with the fm_branch_outcomes tool when the captain asks what happened.
30
+
31
+ The turn-end guard extension lives at `__FM_PI_TURNEND_EXT__`.
32
+ The watcher extension lives at `__FM_PI_EXT__`.
33
+ Both are tracked, project-local `.pi/extensions/*.ts` files that Pi auto-discovers once the project is trusted; `bin/fm-session-start.sh` reports when the running Pi session has not loaded both required extensions.
@@ -0,0 +1,13 @@
1
+ Mode: Unknown harness fallback.
2
+
3
+ This primary harness does not have a verified watcher wake adapter.
4
+ Follow the generic supervision contract in `AGENTS.md`.
5
+ First cycle: drain queued wakes, then choose a supervision wait that the harness can actually wake from.
6
+ Ordinary wake: drain, handle all emitted wakes, reconcile open decisions and unread status lines, and run the exact `--ack-through` command printed as `WAKE_ACK_REQUIRED`, then repeat that verified wait while supervision is still required.
7
+ Before that acknowledgement, interruption leaves the work durable for idempotent re-handling.
8
+ Use `bin/fm-watch-arm.sh` only when the harness has a tracked background mechanism that survives the tool call and notifies the model on process exit.
9
+ Use a bounded foreground wait over `bin/fm-watch.sh` when that wake mechanism is not verified.
10
+ Never use shell `&` for watcher supervision.
11
+ Failure or missing cycle only: inspect the failure and restore the same verified wait shape.
12
+
13
+ Record new verification evidence before promoting an unknown harness to a named snippet.