@zerwiz/ymir 0.1.4 → 0.1.5

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 (850) hide show
  1. package/.agents/README.md +291 -0
  2. package/.agents/agents/bragi-marketer.md +111 -0
  3. package/.agents/agents/brokk.md +352 -0
  4. package/.agents/agents/forseti-reviewer.md +93 -0
  5. package/.agents/agents/frigg-consent.md +77 -0
  6. package/.agents/agents/groa-update.md +77 -0
  7. package/.agents/agents/hnoss-designer.md +69 -0
  8. package/.agents/agents/huginn-researcher.md +99 -0
  9. package/.agents/agents/jord-projects.md +78 -0
  10. package/.agents/agents/kvasir-scout.md +91 -0
  11. package/.agents/agents/mimir-planner.md +92 -0
  12. package/.agents/agents/muninn-stow.md +77 -0
  13. package/.agents/agents/saga-bearings.md +78 -0
  14. package/.agents/agents/sindri-developer.md +114 -0
  15. package/.agents/agents/snotra-documenter.md +105 -0
  16. package/.agents/agents/syn-recovery.md +78 -0
  17. package/.agents/agents/tyr-check.md +78 -0
  18. package/.agents/agents/urdh-hold.md +77 -0
  19. package/.agents/agents/volundr-smith.md +80 -0
  20. package/.agents/agents/vor-diagnostics.md +77 -0
  21. package/.agents/assets/agents/naming.md +50 -0
  22. package/.agents/assets/agents/registry.md +93 -0
  23. package/.agents/assets/agents/runtime.md +36 -0
  24. package/.agents/assets/agents/toon-tasks-cli.md +42 -0
  25. package/.agents/assets/templates/company_entity.template.md +37 -0
  26. package/.agents/backend/backends/cmux.sh +649 -0
  27. package/.agents/backend/backends/herdr-eventwait.py +157 -0
  28. package/.agents/backend/backends/herdr-workspace-move.py +114 -0
  29. package/.agents/backend/backends/herdr.sh +3386 -0
  30. package/.agents/backend/backends/orca.sh +290 -0
  31. package/.agents/backend/backends/tmux.sh +357 -0
  32. package/.agents/backend/backends/zellij.sh +700 -0
  33. package/.agents/backend/fm-afk-launch.sh +654 -0
  34. package/.agents/backend/fm-afk-return.sh +241 -0
  35. package/.agents/backend/fm-afk-start.sh +172 -0
  36. package/.agents/backend/fm-arm-command-policy.mjs +960 -0
  37. package/.agents/backend/fm-arm-pretool-check.sh +200 -0
  38. package/.agents/backend/fm-backend-hometag-lib.sh +52 -0
  39. package/.agents/backend/fm-backend.sh +981 -0
  40. package/.agents/backend/fm-backlog-handoff.sh +909 -0
  41. package/.agents/backend/fm-backlog-receive.sh +187 -0
  42. package/.agents/backend/fm-backlog-transition-lib.sh +779 -0
  43. package/.agents/backend/fm-bearings-board.sh +204 -0
  44. package/.agents/backend/fm-bearings-snapshot.sh +546 -0
  45. package/.agents/backend/fm-bootstrap.sh +1568 -0
  46. package/.agents/backend/fm-branch-outcome.sh +203 -0
  47. package/.agents/backend/fm-branch-prompt.sh +101 -0
  48. package/.agents/backend/fm-brief.sh +452 -0
  49. package/.agents/backend/fm-busy-event.sh +234 -0
  50. package/.agents/backend/fm-busy-lib.sh +986 -0
  51. package/.agents/backend/fm-captain-hold.sh +1001 -0
  52. package/.agents/backend/fm-cd-command-policy.mjs +153 -0
  53. package/.agents/backend/fm-cd-pretool-check.sh +190 -0
  54. package/.agents/backend/fm-check-lib.sh +74 -0
  55. package/.agents/backend/fm-check-register.sh +42 -0
  56. package/.agents/backend/fm-check-unregister.sh +52 -0
  57. package/.agents/backend/fm-classify-lib.sh +1770 -0
  58. package/.agents/backend/fm-claude-stop-autoarm.sh +334 -0
  59. package/.agents/backend/fm-composer-lib.sh +1416 -0
  60. package/.agents/backend/fm-config-inherit-lib.sh +1202 -0
  61. package/.agents/backend/fm-config-push.sh +254 -0
  62. package/.agents/backend/fm-control-lib.sh +251 -0
  63. package/.agents/backend/fm-control.sh +878 -0
  64. package/.agents/backend/fm-crew-state.sh +625 -0
  65. package/.agents/backend/fm-cursor-lib.sh +243 -0
  66. package/.agents/backend/fm-decision-hold.sh +232 -0
  67. package/.agents/backend/fm-doc-audience-check.sh +269 -0
  68. package/.agents/backend/fm-dod-lib.sh +67 -0
  69. package/.agents/backend/fm-ensure-agents-md.sh +253 -0
  70. package/.agents/backend/fm-extension-launch-barrier.mjs +129 -0
  71. package/.agents/backend/fm-extension.mjs +2577 -0
  72. package/.agents/backend/fm-extension.sh +16 -0
  73. package/.agents/backend/fm-ff-lib.sh +420 -0
  74. package/.agents/backend/fm-fleet-snapshot.sh +1463 -0
  75. package/.agents/backend/fm-fleet-sync.sh +458 -0
  76. package/.agents/backend/fm-fleet-view.sh +96 -0
  77. package/.agents/backend/fm-gate-refuse-lib.sh +102 -0
  78. package/.agents/backend/fm-guard.sh +242 -0
  79. package/.agents/backend/fm-harness.sh +195 -0
  80. package/.agents/backend/fm-herdr-ci-cleanup.sh +115 -0
  81. package/.agents/backend/fm-herdr-lab.sh +346 -0
  82. package/.agents/backend/fm-herdr-session-cleanup.sh +337 -0
  83. package/.agents/backend/fm-home-seed.sh +984 -0
  84. package/.agents/backend/fm-home-summary-refresh.sh +255 -0
  85. package/.agents/backend/fm-hook-host-lib.sh +36 -0
  86. package/.agents/backend/fm-inactive-reconcile.sh +531 -0
  87. package/.agents/backend/fm-inbox.sh +399 -0
  88. package/.agents/backend/fm-install-actionlint.sh +84 -0
  89. package/.agents/backend/fm-install-herdr.sh +96 -0
  90. package/.agents/backend/fm-install-shellcheck.sh +84 -0
  91. package/.agents/backend/fm-install-treehouse.sh +95 -0
  92. package/.agents/backend/fm-kimi-turnend-hook.sh +276 -0
  93. package/.agents/backend/fm-lease-lib.sh +218 -0
  94. package/.agents/backend/fm-lease.sh +189 -0
  95. package/.agents/backend/fm-line-cap-lib.sh +51 -0
  96. package/.agents/backend/fm-lint-workflows.sh +137 -0
  97. package/.agents/backend/fm-lint.sh +591 -0
  98. package/.agents/backend/fm-lock-lib.sh +104 -0
  99. package/.agents/backend/fm-lock.sh +107 -0
  100. package/.agents/backend/fm-marker-lib.sh +12 -0
  101. package/.agents/backend/fm-merge-local.sh +74 -0
  102. package/.agents/backend/fm-merge-outcome-lib.sh +138 -0
  103. package/.agents/backend/fm-nm-run-lib.sh +124 -0
  104. package/.agents/backend/fm-on.sh +125 -0
  105. package/.agents/backend/fm-operational-input.sh +253 -0
  106. package/.agents/backend/fm-peek.sh +46 -0
  107. package/.agents/backend/fm-pending-reply-lib.sh +1409 -0
  108. package/.agents/backend/fm-pr-check.sh +135 -0
  109. package/.agents/backend/fm-pr-lib.sh +1016 -0
  110. package/.agents/backend/fm-pr-merge.sh +707 -0
  111. package/.agents/backend/fm-pr-poll.sh +110 -0
  112. package/.agents/backend/fm-primary-scope-lib.sh +33 -0
  113. package/.agents/backend/fm-procevent-extension-capture.pl +259 -0
  114. package/.agents/backend/fm-procevent-lavish.sh +628 -0
  115. package/.agents/backend/fm-procevent-lib.sh +892 -0
  116. package/.agents/backend/fm-procevent-quota.sh +290 -0
  117. package/.agents/backend/fm-procevent-remote-reply.sh +577 -0
  118. package/.agents/backend/fm-procevent-when.sh +504 -0
  119. package/.agents/backend/fm-procevent.sh +1466 -0
  120. package/.agents/backend/fm-project-mode.sh +92 -0
  121. package/.agents/backend/fm-project-origin-lib.sh +180 -0
  122. package/.agents/backend/fm-promote.sh +282 -0
  123. package/.agents/backend/fm-public-followup-emit.sh +260 -0
  124. package/.agents/backend/fm-public-followup-lib.sh +398 -0
  125. package/.agents/backend/fm-public-followup.sh +1330 -0
  126. package/.agents/backend/fm-push-transition-lib.sh +168 -0
  127. package/.agents/backend/fm-quota-axi-lib.sh +93 -0
  128. package/.agents/backend/fm-quota-choose.sh +384 -0
  129. package/.agents/backend/fm-remote-delta-read.sh +189 -0
  130. package/.agents/backend/fm-remote-doctor.sh +800 -0
  131. package/.agents/backend/fm-remote-entrypoint.sh +193 -0
  132. package/.agents/backend/fm-remote-file.sh +234 -0
  133. package/.agents/backend/fm-remote-home-provision.sh +259 -0
  134. package/.agents/backend/fm-remote-home-seed.sh +259 -0
  135. package/.agents/backend/fm-remote-inherit-push.sh +90 -0
  136. package/.agents/backend/fm-remote-inherit.sh +179 -0
  137. package/.agents/backend/fm-remote-job-lib.sh +1241 -0
  138. package/.agents/backend/fm-remote-job-reap-orphans.sh +137 -0
  139. package/.agents/backend/fm-remote-job-worker.sh +1124 -0
  140. package/.agents/backend/fm-remote-readiness-lib.sh +44 -0
  141. package/.agents/backend/fm-remote-secondmate-control.sh +340 -0
  142. package/.agents/backend/fm-review-diff.sh +158 -0
  143. package/.agents/backend/fm-secondmate-charter-lib.sh +44 -0
  144. package/.agents/backend/fm-secondmate-nudge-lib.sh +68 -0
  145. package/.agents/backend/fm-secondmate-parent-lib.sh +70 -0
  146. package/.agents/backend/fm-secondmate-reconcile.sh +371 -0
  147. package/.agents/backend/fm-secondmate-registry-lib.sh +311 -0
  148. package/.agents/backend/fm-secondmate-report.sh +88 -0
  149. package/.agents/backend/fm-send.sh +1059 -0
  150. package/.agents/backend/fm-session-lock-lib.sh +176 -0
  151. package/.agents/backend/fm-session-start.sh +970 -0
  152. package/.agents/backend/fm-sessionstart-cursor.sh +40 -0
  153. package/.agents/backend/fm-sessionstart-nudge.sh +45 -0
  154. package/.agents/backend/fm-sessionstart-run.sh +145 -0
  155. package/.agents/backend/fm-spawn.sh +3153 -0
  156. package/.agents/backend/fm-startup-memory-budget-lib.sh +224 -0
  157. package/.agents/backend/fm-startup-memory-budget.sh +94 -0
  158. package/.agents/backend/fm-startup-network.sh +642 -0
  159. package/.agents/backend/fm-stow-cascade.sh +251 -0
  160. package/.agents/backend/fm-subagent-pretool-check.sh +207 -0
  161. package/.agents/backend/fm-supervise-daemon.sh +1732 -0
  162. package/.agents/backend/fm-supervision-instructions.sh +215 -0
  163. package/.agents/backend/fm-supervision-lib.sh +90 -0
  164. package/.agents/backend/fm-supervisor-target-lib.sh +78 -0
  165. package/.agents/backend/fm-tangle-lib.sh +53 -0
  166. package/.agents/backend/fm-task-inbox-lib.sh +402 -0
  167. package/.agents/backend/fm-tasks-axi-lib.sh +123 -0
  168. package/.agents/backend/fm-teardown.sh +2922 -0
  169. package/.agents/backend/fm-test-isolation-proof.sh +588 -0
  170. package/.agents/backend/fm-test-run.sh +2191 -0
  171. package/.agents/backend/fm-timeout-lib.sh +141 -0
  172. package/.agents/backend/fm-timing-lib.sh +179 -0
  173. package/.agents/backend/fm-tmux-lib.sh +291 -0
  174. package/.agents/backend/fm-tool-update-check.sh +898 -0
  175. package/.agents/backend/fm-trace-context-lib.sh +227 -0
  176. package/.agents/backend/fm-transition-lib.sh +103 -0
  177. package/.agents/backend/fm-turnend-guard-cursor.sh +391 -0
  178. package/.agents/backend/fm-turnend-guard-grok.sh +90 -0
  179. package/.agents/backend/fm-turnend-guard.sh +432 -0
  180. package/.agents/backend/fm-update.sh +110 -0
  181. package/.agents/backend/fm-vendor-auth-probe.sh +190 -0
  182. package/.agents/backend/fm-voice-client.py +1373 -0
  183. package/.agents/backend/fm-voice-relay.py +1256 -0
  184. package/.agents/backend/fm-wake-drain.sh +592 -0
  185. package/.agents/backend/fm-wake-grant.sh +127 -0
  186. package/.agents/backend/fm-wake-lib.sh +1849 -0
  187. package/.agents/backend/fm-watch-arm.sh +602 -0
  188. package/.agents/backend/fm-watch-checkpoint.sh +109 -0
  189. package/.agents/backend/fm-watch.sh +1962 -0
  190. package/.agents/backend/fm-x-dismiss.sh +116 -0
  191. package/.agents/backend/fm-x-followup.sh +286 -0
  192. package/.agents/backend/fm-x-lib.sh +1000 -0
  193. package/.agents/backend/fm-x-link.sh +234 -0
  194. package/.agents/backend/fm-x-poll.sh +185 -0
  195. package/.agents/backend/fm-x-reply.sh +385 -0
  196. package/.agents/backend/fm_voice_frame.py +166 -0
  197. package/.agents/backend/fm_voice_records.py +574 -0
  198. package/.agents/backend/model-bridge.py +290 -0
  199. package/.agents/backend/opencode-go-bridge.py +30 -0
  200. package/.agents/bus/README.md +12 -0
  201. package/.agents/bus/messages.json +1 -0
  202. package/.agents/config/agents.machine.example.yaml +16 -0
  203. package/.agents/config/agents.yaml +50 -0
  204. package/.agents/config/agents.yaml.example +72 -0
  205. package/.agents/config/crew-dispatch.json +22 -0
  206. package/.agents/config/cron.yaml +9 -0
  207. package/.agents/config/eindri-dispatch.json +67 -0
  208. package/.agents/config/eindri-harness +1 -0
  209. package/.agents/config/startup-memory-budget +1 -0
  210. package/.agents/config/tailscale-sync.yaml +27 -0
  211. package/.agents/filebrowser/.filebrowser.json +6 -0
  212. package/.agents/filebrowser/README.md +11 -0
  213. package/.agents/gateway/README.md +12 -0
  214. package/.agents/github/README.md +15 -0
  215. package/.agents/github/webhooks/.gitkeep +0 -0
  216. package/.agents/github/workflows/.gitkeep +0 -0
  217. package/.agents/harness/opencode/plugins/lib/rodd-operational-input.js +37 -0
  218. package/.agents/harness/opencode/plugins/package.json +4 -0
  219. package/.agents/harness/opencode/plugins/saga-sessionstart.js +77 -0
  220. package/.agents/harness/opencode/plugins/syn-cd-check.js +61 -0
  221. package/.agents/harness/opencode/plugins/syn-pretool-check.js +68 -0
  222. package/.agents/harness/opencode/plugins/syn-turnend-guard.js +105 -0
  223. package/.agents/harness/opencode/plugins/syn-watch-arm.js +521 -0
  224. package/.agents/memory/README.md +46 -0
  225. package/.agents/memory/kaia.engram +0 -0
  226. package/.agents/memory/kaia.engram-shm +0 -0
  227. package/.agents/memory/kaia.engram-wal +0 -0
  228. package/.agents/memory/runes_audit.md +5 -0
  229. package/.agents/memory/well/episodes.jsonl +367 -0
  230. package/.agents/memory/well/workspace.jsonl +27 -0
  231. package/.agents/migrations/0001-hodd-layout.sh +38 -0
  232. package/.agents/migrations/0002-a2a-mcp.sh +27 -0
  233. package/.agents/migrations/0003-private-data-separation.sh +111 -0
  234. package/.agents/migrations/0004-hoard-and-realms.sh +104 -0
  235. package/.agents/sandbox/Dockerfile.utgard +19 -0
  236. package/.agents/sandbox/README.md +14 -0
  237. package/.agents/sandbox/utgard.config.json +14 -0
  238. package/.agents/skills/README.md +59 -0
  239. package/.agents/skills/bragi-marketing/README.md +3 -0
  240. package/.agents/skills/bragi-marketing/SKILL.md +82 -0
  241. package/.agents/skills/eindri-homes/README.md +3 -0
  242. package/.agents/skills/eindri-homes/SKILL.md +291 -0
  243. package/.agents/skills/eindri-homes/assets/control-plane.md +122 -0
  244. package/.agents/skills/eindri-homes/assets/dispatch.md +80 -0
  245. package/.agents/skills/frigg-consent/README.md +3 -0
  246. package/.agents/skills/frigg-consent/SKILL.md +56 -0
  247. package/.agents/skills/galdr-ymirsystem/README.md +3 -0
  248. package/.agents/skills/galdr-ymirsystem/SKILL.md +158 -0
  249. package/.agents/skills/galdr-ymirsystem/assets/README.md +74 -0
  250. package/.agents/skills/galdr-ymirsystem/assets/brokk-distro-runtime.md +390 -0
  251. package/.agents/skills/galdr-ymirsystem/assets/build-method.md +113 -0
  252. package/.agents/skills/galdr-ymirsystem/assets/build-tool-categories.md +178 -0
  253. package/.agents/skills/galdr-ymirsystem/assets/eindri-orchestration.md +518 -0
  254. package/.agents/skills/galdr-ymirsystem/assets/eindri-profiles.md +136 -0
  255. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +673 -0
  256. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/claude-code.md +197 -0
  257. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/codex.md +191 -0
  258. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/cursor.md +193 -0
  259. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/opencode.md +262 -0
  260. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/pi.md +295 -0
  261. package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +496 -0
  262. package/.agents/skills/galdr-ymirsystem/assets/installation.md +571 -0
  263. package/.agents/skills/galdr-ymirsystem/assets/local-models/HOST-DATA.example.md +72 -0
  264. package/.agents/skills/galdr-ymirsystem/assets/local-models.md +274 -0
  265. package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +161 -0
  266. package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +377 -0
  267. package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
  268. package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +129 -0
  269. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/einherjar-spawn.schema.json +81 -0
  270. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/herdr-profile.toml +75 -0
  271. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +96 -0
  272. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/supervision-tree.yml +163 -0
  273. package/.agents/skills/galdr-ymirsystem/assets/pi-boot-guide.md +58 -0
  274. package/.agents/skills/galdr-ymirsystem/assets/porting-upstream-to-norse.md +301 -0
  275. package/.agents/skills/galdr-ymirsystem/assets/principles.md +385 -0
  276. package/.agents/skills/galdr-ymirsystem/assets/reference-adoption.md +99 -0
  277. package/.agents/skills/galdr-ymirsystem/assets/registry.md +379 -0
  278. package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +463 -0
  279. package/.agents/skills/galdr-ymirsystem/assets/runtime-components.md +553 -0
  280. package/.agents/skills/galdr-ymirsystem/assets/smidja.md +175 -0
  281. package/.agents/skills/galdr-ymirsystem/schemas/toon-schemas.md +190 -0
  282. package/.agents/skills/galdr-ymirsystem/scripts/README.md +45 -0
  283. package/.agents/skills/galdr-ymirsystem/scripts/bench-one.sh +248 -0
  284. package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +360 -0
  285. package/.agents/skills/galdr-ymirsystem/scripts/gpu-sample.sh +43 -0
  286. package/.agents/skills/galdr-ymirsystem/scripts/stop-all.sh +34 -0
  287. package/.agents/skills/galdr-ymirsystem/scripts/toon-check.py +188 -0
  288. package/.agents/skills/gjallarhorn-relay/README.md +3 -0
  289. package/.agents/skills/gjallarhorn-relay/SKILL.md +283 -0
  290. package/.agents/skills/groa-update/SKILL.md +70 -0
  291. package/.agents/skills/hamr-adapters/README.md +3 -0
  292. package/.agents/skills/hamr-adapters/SKILL.md +123 -0
  293. package/.agents/skills/hamr-adapters/references/common/control-and-recovery.md +37 -0
  294. package/.agents/skills/hamr-adapters/references/common/dispatch.md +32 -0
  295. package/.agents/skills/hamr-adapters/references/common/model-and-effort.md +42 -0
  296. package/.agents/skills/hamr-adapters/references/common/primary-hooks.md +40 -0
  297. package/.agents/skills/hamr-adapters/references/harness/claude.md +55 -0
  298. package/.agents/skills/hamr-adapters/references/harness/codex.md +43 -0
  299. package/.agents/skills/hamr-adapters/references/harness/cursor.md +75 -0
  300. package/.agents/skills/hamr-adapters/references/harness/grok.md +69 -0
  301. package/.agents/skills/hamr-adapters/references/harness/kimi.md +51 -0
  302. package/.agents/skills/hamr-adapters/references/harness/muse.md +70 -0
  303. package/.agents/skills/hamr-adapters/references/harness/opencode.md +42 -0
  304. package/.agents/skills/hamr-adapters/references/harness/pi.md +56 -0
  305. package/.agents/skills/herdr-panes/README.md +3 -0
  306. package/.agents/skills/herdr-panes/SKILL.md +92 -0
  307. package/.agents/skills/herdr-panes/assets/herdr-backend.md +346 -0
  308. package/.agents/skills/herdr-panes/assets/tmux-backend.md +115 -0
  309. package/.agents/skills/hnoss-design/DESIGN.md +19 -0
  310. package/.agents/skills/hnoss-design/README.md +3 -0
  311. package/.agents/skills/hnoss-design/SKILL.md +83 -0
  312. package/.agents/skills/hvild-afk/README.md +3 -0
  313. package/.agents/skills/hvild-afk/SKILL.md +241 -0
  314. package/.agents/skills/jord-projects/README.md +3 -0
  315. package/.agents/skills/jord-projects/SKILL.md +92 -0
  316. package/.agents/skills/lifecycle/SKILL.md +56 -0
  317. package/.agents/skills/lifecycle/smoke_test.sh +52 -0
  318. package/.agents/skills/lifecycle/start.sh +21 -0
  319. package/.agents/skills/lifecycle/status.sh +35 -0
  320. package/.agents/skills/lifecycle/stop.sh +16 -0
  321. package/.agents/skills/muninn-stow/README.md +3 -0
  322. package/.agents/skills/muninn-stow/SKILL.md +309 -0
  323. package/.agents/skills/no-mistakes/SKILL.md +379 -0
  324. package/.agents/skills/nornir-schedule/README.md +3 -0
  325. package/.agents/skills/nornir-schedule/SKILL.md +25 -0
  326. package/.agents/skills/nornir-schedule/assets/events.md +137 -0
  327. package/.agents/skills/nornir-schedule/assets/quota.md +131 -0
  328. package/.agents/skills/nsr-compliance/README.md +3 -0
  329. package/.agents/skills/nsr-compliance/SKILL.md +29 -0
  330. package/.agents/skills/nsr-compliance/assets/nsr/README.md +40 -0
  331. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/README.md +15 -0
  332. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/git-workflow.md +18 -0
  333. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/security.md +22 -0
  334. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/tech-stack.md +8 -0
  335. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/SKILL.md +71 -0
  336. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/config/core_four.yaml +16 -0
  337. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_danger.sh +102 -0
  338. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_wiring.sh +69 -0
  339. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/validate_code.sh +29 -0
  340. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/verify_docs.py +66 -0
  341. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/generate.sh +109 -0
  342. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/result_envelope.json +14 -0
  343. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/task_envelope.json +20 -0
  344. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/runner.py +303 -0
  345. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/telemetry/logger.py +112 -0
  346. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/README.md +18 -0
  347. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/config/README.md +10 -0
  348. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/README.md +12 -0
  349. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_env.sh +23 -0
  350. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_paths.sh +20 -0
  351. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_platform.sh +23 -0
  352. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/README.md +11 -0
  353. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/envelopes/README.md +10 -0
  354. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/README.md +10 -0
  355. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/install.sh +125 -0
  356. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/telemetry/README.md +11 -0
  357. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/README.md +20 -0
  358. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/README.md +36 -0
  359. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/SKILL-template.md +14 -0
  360. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/README.md +25 -0
  361. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/SKILL.md +13 -0
  362. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/create_branch.sh +6 -0
  363. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/safe_commit.sh +9 -0
  364. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/sync_upstream.sh +6 -0
  365. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/README.md +27 -0
  366. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/SKILL.md +15 -0
  367. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/smoke_test.sh +12 -0
  368. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/start.sh +19 -0
  369. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/status.sh +12 -0
  370. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/stop.sh +15 -0
  371. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/README.md +18 -0
  372. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/README.md +10 -0
  373. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/core_four.yaml +13 -0
  374. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/README.md +12 -0
  375. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_danger.sh +102 -0
  376. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_env.sh +23 -0
  377. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_paths.sh +20 -0
  378. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_platform.sh +23 -0
  379. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_wiring.sh +69 -0
  380. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/README.md +11 -0
  381. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/README.md +10 -0
  382. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/result_envelope.json +14 -0
  383. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/task_envelope.json +20 -0
  384. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/runner.py +303 -0
  385. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/README.md +10 -0
  386. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/install.sh +125 -0
  387. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/README.md +11 -0
  388. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/logger.py +112 -0
  389. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/README.md +14 -0
  390. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/api.md +17 -0
  391. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/compliance-wiring.md +155 -0
  392. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/frontend.md +15 -0
  393. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/README.md +11 -0
  394. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/README.md +23 -0
  395. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/README.md +16 -0
  396. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/template-README.md +11 -0
  397. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/deploy.sh +49 -0
  398. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/README.md +12 -0
  399. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/development.env.example +17 -0
  400. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/production.env.example +17 -0
  401. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/staging.env.example +17 -0
  402. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/README.md +11 -0
  403. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/template.env.example +8 -0
  404. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/README.md +26 -0
  405. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/agent.md +7 -0
  406. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/developers/dev-1.md +20 -0
  407. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/linux.md +9 -0
  408. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/macos.md +10 -0
  409. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/shared.md +19 -0
  410. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/windows.md +14 -0
  411. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/README.md +25 -0
  412. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/_template.md +41 -0
  413. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/hosting-1.md +41 -0
  414. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/README.md +21 -0
  415. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/README.md +15 -0
  416. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/deployment.md +19 -0
  417. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/disaster-recovery.md +15 -0
  418. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/ops-tasks.md +14 -0
  419. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/features/README.md +16 -0
  420. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/project-skills.md +146 -0
  421. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/README.md +11 -0
  422. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/software-compliance-research.md +269 -0
  423. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/working-with-agents.md +260 -0
  424. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/AGENTS.md +38 -0
  425. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/ARCHITECTURE.md +36 -0
  426. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/BEST_PRACTICES.md +27 -0
  427. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/CI_CD.md +23 -0
  428. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/FEATURES.md +15 -0
  429. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/README.md +22 -0
  430. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/RULES.md +37 -0
  431. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/STRUCTURE.md +45 -0
  432. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/TECH_STACK.md +28 -0
  433. package/.agents/skills/nsr-compliance/assets/nsr/scaffold-spec.md +63 -0
  434. package/.agents/skills/nsr-compliance/assets/nsr/scripts/scaffold.sh +103 -0
  435. package/.agents/skills/nsr-compliance/assets/nsr/scripts/validate.sh +52 -0
  436. package/.agents/skills/nsr-compliance/assets/nsrcompliance/SKILL.md +71 -0
  437. package/.agents/skills/nsr-compliance/assets/nsrcompliance/config/core_four.yaml +16 -0
  438. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_danger.sh +102 -0
  439. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_wiring.sh +69 -0
  440. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/validate_code.sh +29 -0
  441. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/verify_docs.py +66 -0
  442. package/.agents/skills/nsr-compliance/assets/nsrcompliance/generate.sh +109 -0
  443. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/result_envelope.json +14 -0
  444. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/task_envelope.json +20 -0
  445. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/runner.py +303 -0
  446. package/.agents/skills/nsr-compliance/assets/nsrcompliance/telemetry/logger.py +112 -0
  447. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/README.md +18 -0
  448. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/config/README.md +10 -0
  449. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/README.md +12 -0
  450. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_env.sh +23 -0
  451. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_paths.sh +20 -0
  452. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_platform.sh +23 -0
  453. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/README.md +11 -0
  454. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/envelopes/README.md +10 -0
  455. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/README.md +10 -0
  456. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/install.sh +125 -0
  457. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/telemetry/README.md +11 -0
  458. package/.agents/skills/pr-ops/README.md +3 -0
  459. package/.agents/skills/pr-ops/SKILL.md +109 -0
  460. package/.agents/skills/ratatoskr-a2a/README.md +3 -0
  461. package/.agents/skills/ratatoskr-a2a/SKILL.md +148 -0
  462. package/.agents/skills/rules-check-drift/SKILL.md +81 -0
  463. package/.agents/skills/saga-bearings/README.md +3 -0
  464. package/.agents/skills/saga-bearings/SKILL.md +26 -0
  465. package/.agents/skills/saga-bearings/assets/bearings.md +161 -0
  466. package/.agents/skills/saga-bearings/assets/board-template.html +731 -0
  467. package/.agents/skills/saga-bearings/assets/recap.md +56 -0
  468. package/.agents/skills/syn-recovery/README.md +3 -0
  469. package/.agents/skills/syn-recovery/SKILL.md +54 -0
  470. package/.agents/skills/tyr-check/README.md +3 -0
  471. package/.agents/skills/tyr-check/SKILL.md +218 -0
  472. package/.agents/skills/urdh-hold/README.md +3 -0
  473. package/.agents/skills/urdh-hold/SKILL.md +25 -0
  474. package/.agents/skills/urdh-hold/assets/decisions.md +15 -0
  475. package/.agents/skills/urdh-hold/assets/hold.md +54 -0
  476. package/.agents/skills/vor-diagnostics/README.md +3 -0
  477. package/.agents/skills/vor-diagnostics/SKILL.md +68 -0
  478. package/.agents/skills/ymir-host/README.md +3 -0
  479. package/.agents/skills/ymir-host/SKILL.md +34 -0
  480. package/.agents/skills/ymir-host/assets/desktop.md +36 -0
  481. package/.agents/skills/ymir-host/assets/omarchy.md +297 -0
  482. package/.agents/skills/ymir-host/assets/runbooks.md +15 -0
  483. package/.agents/skills/ymir-host/assets/thjazi.md +220 -0
  484. package/.agents/skills/ymir-host/assets/update.md +64 -0
  485. package/.agents/tests/assets/board-render-harness.mjs +117 -0
  486. package/.agents/tests/cmux-test-safety.sh +51 -0
  487. package/.agents/tests/fixtures.sh +291 -0
  488. package/.agents/tests/fm-afk-inject-e2e.test.sh +428 -0
  489. package/.agents/tests/fm-afk-inject-herdr-e2e.test.sh +536 -0
  490. package/.agents/tests/fm-afk-launch.test.sh +961 -0
  491. package/.agents/tests/fm-afk-pi-herdr-return-e2e.test.sh +297 -0
  492. package/.agents/tests/fm-afk-return.test.sh +283 -0
  493. package/.agents/tests/fm-arm-pretool-check.test.sh +482 -0
  494. package/.agents/tests/fm-ask-user-authority.test.sh +42 -0
  495. package/.agents/tests/fm-backend-autodetect-smoke.test.sh +176 -0
  496. package/.agents/tests/fm-backend-cmux-smoke.test.sh +188 -0
  497. package/.agents/tests/fm-backend-cmux.test.sh +1166 -0
  498. package/.agents/tests/fm-backend-herdr-eventwait-smoke.test.sh +136 -0
  499. package/.agents/tests/fm-backend-herdr-eventwait.test.py +106 -0
  500. package/.agents/tests/fm-backend-herdr-focus-flash-e2e.test.sh +409 -0
  501. package/.agents/tests/fm-backend-herdr-launcher-workspace-e2e.test.sh +437 -0
  502. package/.agents/tests/fm-backend-herdr-presentation-e2e.test.sh +1424 -0
  503. package/.agents/tests/fm-backend-herdr-prune-safety-e2e.test.sh +180 -0
  504. package/.agents/tests/fm-backend-herdr-respawn-idem-e2e.test.sh +182 -0
  505. package/.agents/tests/fm-backend-herdr-smoke.test.sh +355 -0
  506. package/.agents/tests/fm-backend-herdr-workspace-per-home-e2e.test.sh +251 -0
  507. package/.agents/tests/fm-backend-herdr.test.sh +4606 -0
  508. package/.agents/tests/fm-backend-orca.test.sh +1357 -0
  509. package/.agents/tests/fm-backend-tmux-smoke.test.sh +173 -0
  510. package/.agents/tests/fm-backend-zellij-smoke.test.sh +205 -0
  511. package/.agents/tests/fm-backend-zellij.test.sh +1360 -0
  512. package/.agents/tests/fm-backend.test.sh +1142 -0
  513. package/.agents/tests/fm-backlog-atomicity.test.sh +2302 -0
  514. package/.agents/tests/fm-backlog-handoff.test.sh +1360 -0
  515. package/.agents/tests/fm-bearings-board-render.test.sh +135 -0
  516. package/.agents/tests/fm-bearings-board.test.sh +414 -0
  517. package/.agents/tests/fm-bearings-snapshot.test.sh +1990 -0
  518. package/.agents/tests/fm-bootstrap-network-parallel.test.sh +316 -0
  519. package/.agents/tests/fm-bootstrap.test.sh +1178 -0
  520. package/.agents/tests/fm-branch-supervision.test.sh +552 -0
  521. package/.agents/tests/fm-brief.test.sh +785 -0
  522. package/.agents/tests/fm-busy-adapter-wiring.test.sh +324 -0
  523. package/.agents/tests/fm-busy-state.test.sh +465 -0
  524. package/.agents/tests/fm-calm-pi-extension.test.sh +3979 -0
  525. package/.agents/tests/fm-captain-hold-lifecycle.test.sh +1187 -0
  526. package/.agents/tests/fm-cd-pretool-check.test.sh +400 -0
  527. package/.agents/tests/fm-check-unregister.test.sh +198 -0
  528. package/.agents/tests/fm-classify-corr-token.test.sh +543 -0
  529. package/.agents/tests/fm-classify-decision-key.test.sh +340 -0
  530. package/.agents/tests/fm-claude-stop-autoarm-live-e2e.test.sh +164 -0
  531. package/.agents/tests/fm-claude-stop-autoarm.test.sh +1190 -0
  532. package/.agents/tests/fm-cmux-claude-composer-live-e2e.test.sh +110 -0
  533. package/.agents/tests/fm-codex-continuity-live-e2e.test.sh +55 -0
  534. package/.agents/tests/fm-composer-ghost.test.sh +716 -0
  535. package/.agents/tests/fm-composer-lib.test.sh +666 -0
  536. package/.agents/tests/fm-composer-matrix-live-e2e.test.sh +220 -0
  537. package/.agents/tests/fm-control-herdr-smoke.test.sh +149 -0
  538. package/.agents/tests/fm-control-relaunch.test.sh +1530 -0
  539. package/.agents/tests/fm-control.test.sh +910 -0
  540. package/.agents/tests/fm-crew-state.test.sh +1610 -0
  541. package/.agents/tests/fm-cursor-harness.test.sh +404 -0
  542. package/.agents/tests/fm-cursor-primary-live-e2e.test.sh +217 -0
  543. package/.agents/tests/fm-cursor-primary.test.sh +708 -0
  544. package/.agents/tests/fm-daemon.test.sh +2740 -0
  545. package/.agents/tests/fm-documentation-audiences.test.sh +141 -0
  546. package/.agents/tests/fm-ensure-agents-md.test.sh +371 -0
  547. package/.agents/tests/fm-extension-binding.test.sh +2187 -0
  548. package/.agents/tests/fm-fleet-snapshot-view.test.sh +816 -0
  549. package/.agents/tests/fm-fleet-sync.test.sh +721 -0
  550. package/.agents/tests/fm-gate-refuse.test.sh +356 -0
  551. package/.agents/tests/fm-gitignore-config.test.sh +89 -0
  552. package/.agents/tests/fm-gotmp.test.sh +230 -0
  553. package/.agents/tests/fm-grok-continuity-live-e2e.test.sh +112 -0
  554. package/.agents/tests/fm-grok-harness.test.sh +117 -0
  555. package/.agents/tests/fm-grok-stop-live-e2e.test.sh +219 -0
  556. package/.agents/tests/fm-guard-stale-banner.test.sh +709 -0
  557. package/.agents/tests/fm-harness-adapter-instructions-live-e2e.test.sh +134 -0
  558. package/.agents/tests/fm-harness-adapter-references.test.sh +30 -0
  559. package/.agents/tests/fm-harness-liveness-drift-live-e2e.test.sh +151 -0
  560. package/.agents/tests/fm-herdr-lab.test.sh +243 -0
  561. package/.agents/tests/fm-herdr-session-cleanup-e2e.test.sh +146 -0
  562. package/.agents/tests/fm-herdr-session-cleanup.test.sh +329 -0
  563. package/.agents/tests/fm-herdr-submit-confirm-live-e2e.test.sh +125 -0
  564. package/.agents/tests/fm-herdr-version-floor-live-e2e.test.sh +144 -0
  565. package/.agents/tests/fm-home-summary-refresh.test.sh +971 -0
  566. package/.agents/tests/fm-inactive-reconcile.test.sh +485 -0
  567. package/.agents/tests/fm-kimi-harness.test.sh +687 -0
  568. package/.agents/tests/fm-lint-workflows.test.sh +532 -0
  569. package/.agents/tests/fm-lint.test.sh +1023 -0
  570. package/.agents/tests/fm-muse-harness.test.sh +932 -0
  571. package/.agents/tests/fm-muse-signals-live-e2e.test.sh +205 -0
  572. package/.agents/tests/fm-no-mistakes-required.test.sh +72 -0
  573. package/.agents/tests/fm-on.test.sh +507 -0
  574. package/.agents/tests/fm-opencode-primary-live-e2e.test.sh +357 -0
  575. package/.agents/tests/fm-operational-input.test.sh +160 -0
  576. package/.agents/tests/fm-peek-remote.test.sh +110 -0
  577. package/.agents/tests/fm-pending-reply.test.sh +1288 -0
  578. package/.agents/tests/fm-pi-branch-extension.test.sh +3182 -0
  579. package/.agents/tests/fm-pi-branch-live-e2e.test.sh +527 -0
  580. package/.agents/tests/fm-pi-primary-live-e2e.test.sh +345 -0
  581. package/.agents/tests/fm-pi-primary-types.test.sh +68 -0
  582. package/.agents/tests/fm-pi-watch-extension.test.sh +2843 -0
  583. package/.agents/tests/fm-pr-check-security.test.sh +2140 -0
  584. package/.agents/tests/fm-pr-merge.test.sh +2135 -0
  585. package/.agents/tests/fm-procevent-quota.test.sh +225 -0
  586. package/.agents/tests/fm-procevent-when.test.sh +406 -0
  587. package/.agents/tests/fm-procevent.test.sh +1745 -0
  588. package/.agents/tests/fm-project-origin.test.sh +110 -0
  589. package/.agents/tests/fm-public-followup.test.sh +2272 -0
  590. package/.agents/tests/fm-quota-array-dispatch-live-e2e.test.sh +526 -0
  591. package/.agents/tests/fm-quota-choose.test.sh +626 -0
  592. package/.agents/tests/fm-remote-backlog-handoff.test.sh +453 -0
  593. package/.agents/tests/fm-remote-doctor.test.sh +625 -0
  594. package/.agents/tests/fm-remote-entrypoint.test.sh +59 -0
  595. package/.agents/tests/fm-remote-job-orphan-reap.test.sh +205 -0
  596. package/.agents/tests/fm-remote-job.test.sh +768 -0
  597. package/.agents/tests/fm-remote-reply.test.sh +520 -0
  598. package/.agents/tests/fm-remote-secondmate-lifecycle-e2e.test.sh +1256 -0
  599. package/.agents/tests/fm-remote-secondmate-parent-binding.test.sh +326 -0
  600. package/.agents/tests/fm-remote-secondmate-trace-context.test.sh +310 -0
  601. package/.agents/tests/fm-remote-transport-lanes.test.sh +431 -0
  602. package/.agents/tests/fm-review-diff.test.sh +176 -0
  603. package/.agents/tests/fm-secondmate-harness.test.sh +2600 -0
  604. package/.agents/tests/fm-secondmate-lifecycle-e2e.test.sh +259 -0
  605. package/.agents/tests/fm-secondmate-liveness.test.sh +559 -0
  606. package/.agents/tests/fm-secondmate-reconcile.test.sh +732 -0
  607. package/.agents/tests/fm-secondmate-safety.test.sh +2972 -0
  608. package/.agents/tests/fm-secondmate-sync.test.sh +913 -0
  609. package/.agents/tests/fm-send-inbox-doorbell-live-e2e.test.sh +206 -0
  610. package/.agents/tests/fm-send-inbox.test.sh +352 -0
  611. package/.agents/tests/fm-send-popup-settle.test.sh +167 -0
  612. package/.agents/tests/fm-send-remote-delivery.test.sh +796 -0
  613. package/.agents/tests/fm-send-resolve-key.test.sh +551 -0
  614. package/.agents/tests/fm-send-secondmate-marker-herdr-e2e.test.sh +187 -0
  615. package/.agents/tests/fm-send-secondmate-marker.test.sh +278 -0
  616. package/.agents/tests/fm-send-settle.test.sh +147 -0
  617. package/.agents/tests/fm-send-strict.test.sh +241 -0
  618. package/.agents/tests/fm-session-lock-ancestry.test.sh +365 -0
  619. package/.agents/tests/fm-session-start.test.sh +2515 -0
  620. package/.agents/tests/fm-sessionstart-hook-live-e2e.test.sh +631 -0
  621. package/.agents/tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh +230 -0
  622. package/.agents/tests/fm-sessionstart-nudge.test.sh +1039 -0
  623. package/.agents/tests/fm-shared-captain-inheritance.test.sh +404 -0
  624. package/.agents/tests/fm-spawn-batch.test.sh +148 -0
  625. package/.agents/tests/fm-spawn-dispatch-profile.test.sh +827 -0
  626. package/.agents/tests/fm-spawn-pool-base-freshen.test.sh +440 -0
  627. package/.agents/tests/fm-spawn-worktree-settle.test.sh +147 -0
  628. package/.agents/tests/fm-startup-memory-budget.test.sh +333 -0
  629. package/.agents/tests/fm-startup-network.test.sh +712 -0
  630. package/.agents/tests/fm-stow-cascade.test.sh +370 -0
  631. package/.agents/tests/fm-subagent-pretool-check.test.sh +291 -0
  632. package/.agents/tests/fm-supervision-events.test.sh +156 -0
  633. package/.agents/tests/fm-supervision-instructions.test.sh +189 -0
  634. package/.agents/tests/fm-tangle-guard.test.sh +277 -0
  635. package/.agents/tests/fm-task-delivery.test.sh +420 -0
  636. package/.agents/tests/fm-task-inbox.test.sh +513 -0
  637. package/.agents/tests/fm-teardown-endpoint-safety.test.sh +374 -0
  638. package/.agents/tests/fm-teardown.test.sh +2665 -0
  639. package/.agents/tests/fm-test-fixture-cleanup.test.sh +172 -0
  640. package/.agents/tests/fm-test-fixtures.test.sh +132 -0
  641. package/.agents/tests/fm-test-isolation-proof.test.sh +278 -0
  642. package/.agents/tests/fm-test-run.test.sh +1196 -0
  643. package/.agents/tests/fm-tmux-agent-liveness.test.sh +354 -0
  644. package/.agents/tests/fm-tmux-submit-busy.test.sh +340 -0
  645. package/.agents/tests/fm-tool-update-check.test.sh +1044 -0
  646. package/.agents/tests/fm-trace-context-lib.test.sh +253 -0
  647. package/.agents/tests/fm-trace-context-spawn.test.sh +581 -0
  648. package/.agents/tests/fm-transition-lib.test.sh +54 -0
  649. package/.agents/tests/fm-turnend-guard.test.sh +1822 -0
  650. package/.agents/tests/fm-update.test.sh +304 -0
  651. package/.agents/tests/fm-vendor-auth-probe.test.sh +395 -0
  652. package/.agents/tests/fm-voice-relay.test.sh +4741 -0
  653. package/.agents/tests/fm-wake-daemon-lifecycle-e2e.test.sh +171 -0
  654. package/.agents/tests/fm-wake-drain-open-decisions-cursor.test.sh +356 -0
  655. package/.agents/tests/fm-wake-drain-open-decisions.test.sh +226 -0
  656. package/.agents/tests/fm-wake-drain-unread-status.test.sh +379 -0
  657. package/.agents/tests/fm-wake-queue.test.sh +1224 -0
  658. package/.agents/tests/fm-watch-arm.test.sh +821 -0
  659. package/.agents/tests/fm-watch-checkpoint.test.sh +87 -0
  660. package/.agents/tests/fm-watch-recovery-loop.test.sh +223 -0
  661. package/.agents/tests/fm-watch-triage.test.sh +3889 -0
  662. package/.agents/tests/fm-watcher-lock.test.sh +1133 -0
  663. package/.agents/tests/fm-x-mode.test.sh +3042 -0
  664. package/.agents/tests/gleipnir-guard-pretool-check.test.sh +51 -0
  665. package/.agents/tests/gleipnir-machine-lock.test.sh +130 -0
  666. package/.agents/tests/herdr-test-safety.sh +42 -0
  667. package/.agents/tests/lib.sh +315 -0
  668. package/.agents/tests/remote-herdr-fixture.sh +125 -0
  669. package/.agents/tests/secondmate-helpers.sh +193 -0
  670. package/.agents/tests/smoke.test.sh +93 -0
  671. package/.agents/tests/wake-helpers.sh +338 -0
  672. package/.agents/tests/zellij-test-safety.sh +60 -0
  673. package/.agents/tools/README.md +10 -0
  674. package/.agents/tools/bin/tasks-cli +267 -0
  675. package/.agents/tools/tasks-cli.ts +216 -0
  676. package/AGENTS.md +454 -0
  677. package/CHANGELOG.md +881 -0
  678. package/LICENSE +1 -1
  679. package/NOTICE +63 -0
  680. package/README.md +113 -59
  681. package/RULES/01-domains.md +51 -0
  682. package/RULES/02-agents.md +78 -0
  683. package/RULES/03-houses.md +61 -0
  684. package/RULES/04-hoard.md +65 -0
  685. package/RULES/05-platforms.md +67 -0
  686. package/RULES/06-append-only.md +58 -0
  687. package/RULES/07-config.md +45 -0
  688. package/RULES/08-delivery-gate.md +75 -0
  689. package/RULES/README.md +20 -0
  690. package/bin/README.md +107 -0
  691. package/bin/a2a-mcp.sh +98 -0
  692. package/bin/a2a-serve.py +103 -0
  693. package/bin/a2a-serve.sh +8 -0
  694. package/bin/a2a-talk.sh +50 -0
  695. package/bin/a2abridge-ensure.sh +108 -0
  696. package/bin/agent-run.sh +43 -0
  697. package/bin/agents-config.sh +233 -0
  698. package/bin/apodex-smoke-test.sh +92 -0
  699. package/bin/bifrost-bridge.sh +112 -0
  700. package/bin/bifrost-ingress.sh +77 -0
  701. package/bin/bootstrap-macos.sh +107 -0
  702. package/bin/bootstrap-windows.ps1 +112 -0
  703. package/bin/branch-guard.sh +64 -0
  704. package/bin/brokk +73 -0
  705. package/bin/brokk-classify-lib.sh +1770 -0
  706. package/bin/brokk-lease-lib.sh +222 -0
  707. package/bin/brokk-lease.sh +193 -0
  708. package/bin/brokk-lint.sh +65 -0
  709. package/bin/brokk-send.sh +38 -0
  710. package/bin/brokk-update.sh +9 -0
  711. package/bin/brokk-wake-grant.sh +127 -0
  712. package/bin/brokk-wake-lib.sh +1849 -0
  713. package/bin/changelog-guard.sh +165 -0
  714. package/bin/crash-sense.sh +160 -0
  715. package/bin/desktop-place.sh +224 -0
  716. package/bin/docs-guard.sh +39 -0
  717. package/bin/editor-place.sh +207 -0
  718. package/bin/eindri-acclaim.sh +34 -0
  719. package/bin/eindri-control.sh +28 -0
  720. package/bin/eindri-role.sh +83 -0
  721. package/bin/eindri-seen.sh +41 -0
  722. package/bin/eindri-send.sh +15 -0
  723. package/bin/eindri-start.sh +85 -0
  724. package/bin/eindri-watch.sh +57 -0
  725. package/bin/einherjar-spawn.sh +576 -0
  726. package/bin/eir-doctor.sh +112 -0
  727. package/bin/erindi-brief.sh +271 -0
  728. package/bin/fleet-apply.sh +68 -0
  729. package/bin/github-deploy.sh +81 -0
  730. package/bin/gjallarhorn-expose.sh +102 -0
  731. package/bin/gjallarhorn-notify.sh +58 -0
  732. package/bin/gjallarhorn-purge.sh +54 -0
  733. package/bin/gjallarhorn-tunnel.sh +72 -0
  734. package/bin/gleipnir-lock-lib.sh +284 -0
  735. package/bin/groa-update.sh +122 -0
  736. package/bin/hall-snapshot.sh +92 -0
  737. package/bin/hamr-harness.sh +246 -0
  738. package/bin/heimdall-ssh-keys.sh +374 -0
  739. package/bin/herdr-agents.py +44 -0
  740. package/bin/herdr-ensure.sh +104 -0
  741. package/bin/herdr-run.sh +431 -0
  742. package/bin/hermes-ensure.sh +88 -0
  743. package/bin/hoard-lib.sh +25 -0
  744. package/bin/hodd.sh +78 -0
  745. package/bin/host-sense.sh +199 -0
  746. package/bin/huginn-research-worker.sh +108 -0
  747. package/bin/local-model-lock.sh +51 -0
  748. package/bin/mimir-bridge.py +226 -0
  749. package/bin/mimir-bridge.sh +87 -0
  750. package/bin/mimir-ingest.sh +131 -0
  751. package/bin/mimir.sh +113 -0
  752. package/bin/mjollnir-webhook.sh +59 -0
  753. package/bin/mjollnir.sh +85 -0
  754. package/bin/model-hardware.sh +82 -0
  755. package/bin/model-resolve.sh +147 -0
  756. package/bin/models-detect.sh +178 -0
  757. package/bin/nornir-cron-start.sh +162 -0
  758. package/bin/nornir-job-daily-briefing.sh +191 -0
  759. package/bin/nornir-job-git-sync.sh +152 -0
  760. package/bin/nornir-job-memory-housekeeping.sh +114 -0
  761. package/bin/nornir-job-observer.sh +166 -0
  762. package/bin/nornir-job-skillopt-sleep.sh +76 -0
  763. package/bin/omarchy-hook-install.sh +60 -0
  764. package/bin/omarchy-install.sh +148 -0
  765. package/bin/omarchy-plugins.sh +174 -0
  766. package/bin/omarchy-sense.sh +149 -0
  767. package/bin/perm-guard.sh +30 -0
  768. package/bin/pi-agent.sh +68 -0
  769. package/bin/pi-ensure.sh +77 -0
  770. package/bin/pi-local.sh +37 -0
  771. package/bin/pi-seat.sh +63 -0
  772. package/bin/prereq-ensure.sh +166 -0
  773. package/bin/private-guard.sh +38 -0
  774. package/bin/project-git.sh +55 -0
  775. package/bin/public-guard.sh +70 -0
  776. package/bin/ratatoskr.sh +49 -0
  777. package/bin/realm-lib.sh +32 -0
  778. package/bin/repo-scrub.sh +62 -0
  779. package/bin/rodd-operational-input.sh +250 -0
  780. package/bin/runes-append.sh +198 -0
  781. package/bin/saga-session-start.sh +172 -0
  782. package/bin/saga-sessionstart-run.sh +79 -0
  783. package/bin/saga-wake-drain.sh +41 -0
  784. package/bin/secret-guard.sh +63 -0
  785. package/bin/sessrumnir-ensure.sh +100 -0
  786. package/bin/sessrumnir-sync.sh +237 -0
  787. package/bin/sessrumnir.sh +91 -0
  788. package/bin/skillopt-setup.sh +76 -0
  789. package/bin/skuld-branch-outcome.sh +203 -0
  790. package/bin/skuld-branch-prompt.sh +101 -0
  791. package/bin/smidja-bootstrap.sh +90 -0
  792. package/bin/smidja-observe.sh +86 -0
  793. package/bin/syn-arm-pretool-check.sh +57 -0
  794. package/bin/syn-asset-pretool-check.sh +76 -0
  795. package/bin/syn-cd-pretool-check.sh +28 -0
  796. package/bin/syn-guard-pretool-check.sh +83 -0
  797. package/bin/syn-turnend-guard.sh +36 -0
  798. package/bin/syn-watch-arm.sh +99 -0
  799. package/bin/tailscale-sync.sh +190 -0
  800. package/bin/telegram-bot.sh +69 -0
  801. package/bin/toolchain.sh +74 -0
  802. package/bin/utgard.sh +111 -0
  803. package/bin/valhalla.sh +115 -0
  804. package/bin/valknut-load.sh +260 -0
  805. package/bin/vor-crew-state.sh +124 -0
  806. package/bin/wedge-notify.sh +47 -0
  807. package/bin/workspace-provision.sh +80 -0
  808. package/bin/workspace-rag.sh +150 -0
  809. package/bin/wyrd-db.sh +47 -0
  810. package/bin/yggdrasil.sh +152 -0
  811. package/bin/ymir-edit.sh +145 -0
  812. package/bin/ymir-install.sh +641 -0
  813. package/bin/ymir-invite.sh +93 -0
  814. package/bin/ymir-isolation.sh +88 -0
  815. package/bin/ymir-migrate.sh +61 -0
  816. package/bin/ymir-platform.sh +348 -0
  817. package/bin/ymir-say.sh +97 -0
  818. package/bin/ymir-setup-auth.sh +108 -0
  819. package/bin/ymir-validate.sh +173 -0
  820. package/bin/ymir.js +74 -16
  821. package/docs/Architecture.md +339 -0
  822. package/docs/design.md +351 -0
  823. package/docs/installations/README.md +63 -0
  824. package/docs/installations/ymir-home.md +93 -0
  825. package/docs/integration.md +105 -0
  826. package/docs/lore.md +1056 -0
  827. package/docs/research/agent-harnesses.md +290 -0
  828. package/docs/runbooks/README.md +19 -0
  829. package/docs/runbooks/a2a.md +91 -0
  830. package/docs/runbooks/agent-permissions.md +99 -0
  831. package/docs/runbooks/agents.md +57 -0
  832. package/docs/runbooks/models.md +69 -0
  833. package/docs/runbooks/realm-onboarding.md +60 -0
  834. package/docs/runbooks/secrets-and-hoard.md +60 -0
  835. package/docs/runbooks/updates-and-migrations.md +110 -0
  836. package/docs/runbooks/worktrees-isolation.md +72 -0
  837. package/docs/session-start.md +105 -0
  838. package/docs/skillopt-integration.md +321 -0
  839. package/docs/sync-system.md +66 -0
  840. package/docs/workspaces.md +104 -0
  841. package/docs/ymir-rut.md +260 -0
  842. package/package.json +44 -14
  843. package/packaging/build.sh +74 -0
  844. package/packaging/macos/Ymir Installer.command +43 -0
  845. package/packaging/windows/ymir-setup.nsi +74 -0
  846. package/scripts/README.md +22 -0
  847. package/scripts/electron.sh +210 -0
  848. package/scripts/start.sh +167 -0
  849. package/scripts/stop.sh +113 -0
  850. package/install.sh +0 -35
@@ -0,0 +1,709 @@
1
+ #!/usr/bin/env bash
2
+ # Regression tests for fm-guard's watcher-down banner deduplication.
3
+ #
4
+ # The first stale command in one FM_HOME must print the full actionable watcher
5
+ # banner.
6
+ # Repeated commands in that same stale episode should print only a concise
7
+ # reminder, while unrelated alarms such as queued wakes stay independent.
8
+ set -u
9
+
10
+ # shellcheck source=tests/lib.sh
11
+ . "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
12
+
13
+ TMP_ROOT=$(fm_test_tmproot fm-guard-stale-banner)
14
+
15
+ make_guard_case() {
16
+ local name=$1 dir home root
17
+ dir="$TMP_ROOT/$name"
18
+ home="$dir/home"
19
+ root="$dir/root"
20
+ mkdir -p "$home/state" "$home/config" "$root"
21
+ fm_write_meta "$home/state/task.meta" "window=firstmate:fm-task" "kind=ship"
22
+ printf '%s\n' "$dir"
23
+ }
24
+
25
+ case_home() {
26
+ printf '%s/home\n' "$1"
27
+ }
28
+
29
+ case_root() {
30
+ printf '%s/root\n' "$1"
31
+ }
32
+
33
+ record_live_watcher() {
34
+ local dir=$1 pid=$2 home identity
35
+ home=$(case_home "$dir")
36
+ identity=$(FM_STATE_OVERRIDE="$home/state" bash -c '. "$1"; fm_pid_identity "$2"' _ "$ROOT/bin/fm-wake-lib.sh" "$pid") || return 1
37
+ mkdir -p "$home/state/.watch.lock"
38
+ printf '%s\n' "$pid" > "$home/state/.watch.lock/pid"
39
+ printf '%s\n' "$home" > "$home/state/.watch.lock/fm-home"
40
+ printf '%s\n' "$ROOT/bin/fm-watch.sh" > "$home/state/.watch.lock/watcher-path"
41
+ printf '%s\n' "$identity" > "$home/state/.watch.lock/pid-identity"
42
+ }
43
+
44
+ # These cases exercise the persistent-watcher model (a live pid is the real
45
+ # liveness signal), so pin the model rather than letting the host test runner's
46
+ # ambient harness ancestry pick it.
47
+ run_guard_case() {
48
+ local dir=$1
49
+ FM_ROOT_OVERRIDE="$(case_root "$dir")" \
50
+ FM_HOME="$(case_home "$dir")" \
51
+ FM_GUARD_GRACE=999 \
52
+ FM_SUPERVISION_MODEL=persistent \
53
+ "$ROOT/bin/fm-guard.sh" 2>&1
54
+ }
55
+
56
+ run_guard_case_read_only() {
57
+ local dir=$1
58
+ FM_ROOT_OVERRIDE="$(case_root "$dir")" \
59
+ FM_HOME="$(case_home "$dir")" \
60
+ FM_GUARD_GRACE=999 \
61
+ FM_SUPERVISION_MODEL=persistent \
62
+ FM_GUARD_READ_ONLY=1 \
63
+ "$ROOT/bin/fm-guard.sh" 2>&1
64
+ }
65
+
66
+ # The Claude Stop auto-arm model: the watcher runs only between turns, so a fresh
67
+ # beacon with no live watcher process is the healthy mid-turn state.
68
+ run_guard_case_autoarm() {
69
+ local dir=$1
70
+ FM_ROOT_OVERRIDE="$(case_root "$dir")" \
71
+ FM_HOME="$(case_home "$dir")" \
72
+ FM_GUARD_GRACE=999 \
73
+ FM_SUPERVISION_MODEL=autoarm \
74
+ "$ROOT/bin/fm-guard.sh" 2>&1
75
+ }
76
+
77
+ # The Pi extension model: .pi/extensions/fm-primary-pi-watch.ts tears the watcher
78
+ # down on every actionable wake and spawns the replacement itself, so the lock is
79
+ # legitimately unheld during a hand-off.
80
+ run_guard_case_extension() {
81
+ local dir=$1
82
+ FM_ROOT_OVERRIDE="$(case_root "$dir")" \
83
+ FM_HOME="$(case_home "$dir")" \
84
+ FM_GUARD_GRACE=999 \
85
+ FM_SUPERVISION_MODEL=extension \
86
+ "$ROOT/bin/fm-guard.sh" 2>&1
87
+ }
88
+
89
+ # Stand up the durable evidence a live Pi session leaves behind: both primary
90
+ # extensions present under the case root, and a marker per extension recording
91
+ # that extension's current build plus the session pid in state/.lock.
92
+ # Each named part can be broken independently so a test can prove which one the
93
+ # verdict actually depends on.
94
+ # session_pid the pid state/.lock names (a live one unless the test wants a dead
95
+ # session); "" writes no session lock at all
96
+ # omit "" | watch | turnend - skip that extension's marker
97
+ # drift "" | watch | turnend - write a marker whose version is not the
98
+ # current build, i.e. the session loaded an older extension
99
+ record_pi_extension_session() {
100
+ local dir=$1 session_pid=${2:-} omit=${3:-} drift=${4:-} home root pair source marker version
101
+ home=$(case_home "$dir")
102
+ root=$(case_root "$dir")
103
+ mkdir -p "$root/.pi/extensions"
104
+ for pair in \
105
+ "fm-primary-pi-watch.ts:.pi-watch-extension-loaded:watch" \
106
+ "fm-primary-turnend-guard.ts:.pi-turnend-extension-loaded:turnend"; do
107
+ source=${pair%%:*}
108
+ marker=${pair#*:}; marker=${marker%%:*}
109
+ printf '// %s for %s\n' "${pair##*:}" "$(basename "$dir")" > "$root/.pi/extensions/$source"
110
+ [ "$omit" = "${pair##*:}" ] && continue
111
+ if [ "$drift" = "${pair##*:}" ]; then
112
+ version="sha256:0000000000000000000000000000000000000000000000000000000000000000"
113
+ else
114
+ version=$(FM_STATE_OVERRIDE="$home/state" bash -c '. "$1"; fm_pi_extension_version "$2"' \
115
+ _ "$ROOT/bin/fm-wake-lib.sh" "$root/.pi/extensions/$source") || return 1
116
+ fi
117
+ printf '%s\n%s\n' "$version" "$session_pid" > "$home/state/$marker"
118
+ done
119
+ [ -n "$session_pid" ] && printf '%s\n' "$session_pid" > "$home/state/.lock"
120
+ return 0
121
+ }
122
+
123
+ count_text() {
124
+ local haystack=$1 needle=$2
125
+ awk -v needle="$needle" 'index($0, needle) { c++ } END { print c + 0 }' <<EOF
126
+ $haystack
127
+ EOF
128
+ }
129
+
130
+ test_first_stale_call_prints_full_banner() {
131
+ local dir out
132
+ dir=$(make_guard_case first-stale)
133
+ out=$(run_guard_case "$dir")
134
+ [ "$(count_text "$out" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
135
+ || fail "first stale guard call did not print exactly one full banner: $out"
136
+ assert_contains "$out" "Trust the emitted supervision protocol" \
137
+ "full banner must keep the actionable watcher-repair instruction"
138
+ assert_contains "$out" "WILL still run" \
139
+ "full banner must keep the guarded-operation continuation line"
140
+ pass "fm-guard stale banner: first stale call prints the full actionable banner"
141
+ }
142
+
143
+ test_repeated_same_episode_prints_reminder_only() {
144
+ local dir out1 out2 marker lines
145
+ dir=$(make_guard_case repeated-stale)
146
+ out1=$(run_guard_case "$dir")
147
+ out2=$(run_guard_case "$dir")
148
+ [ "$(count_text "$out1" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
149
+ || fail "first stale call did not print the full banner: $out1"
150
+ [ "$(count_text "$out2" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 0 ] \
151
+ || fail "second stale call repeated the full banner: $out2"
152
+ assert_contains "$out2" "full banner already printed this episode" \
153
+ "second stale call did not print the concise reminder"
154
+ marker="$(case_home "$dir")/state/.guard-watcher-stale-banner"
155
+ assert_present "$marker" "stale banner marker was not written under the owning home"
156
+ lines=$(awk 'END { print NR + 0 }' "$marker")
157
+ [ "$lines" -le 1 ] || fail "stale banner marker must stay bounded to one line, got $lines"
158
+ pass "fm-guard stale banner: repeated same-episode calls print a concise reminder only"
159
+ }
160
+
161
+ test_fresh_beacon_without_live_watcher_stays_alarm() {
162
+ local dir out
163
+ dir=$(make_guard_case fresh-no-live)
164
+ touch "$(case_home "$dir")/state/.last-watcher-beat"
165
+ out=$(run_guard_case "$dir")
166
+ [ "$(count_text "$out" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
167
+ || fail "a fresh leftover beacon without a live watcher must still alarm: $out"
168
+ pass "fm-guard stale banner: a fresh beacon without a live watcher remains unhealthy"
169
+ }
170
+
171
+ test_x_mode_without_live_watcher_stays_alarm() {
172
+ local dir home out
173
+ dir=$(make_guard_case x-mode-no-live)
174
+ home=$(case_home "$dir")
175
+ rm -f "$home/state/task.meta"
176
+ : > "$home/state/x-watch.check.sh"
177
+ out=$(run_guard_case "$dir")
178
+ assert_contains "$out" "X-mode relay polling needs supervision" "X-mode-only need must remain guarded"
179
+ pass "fm-guard stale banner: X-mode polling without a live watcher remains unhealthy"
180
+ }
181
+
182
+ test_healthy_recovery_rearms_next_stale_episode() {
183
+ local dir home out1 healthy out2 pid
184
+ dir=$(make_guard_case healthy-recovery)
185
+ home=$(case_home "$dir")
186
+ out1=$(run_guard_case "$dir")
187
+ [ "$(count_text "$out1" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
188
+ || fail "first stale episode did not print the full banner: $out1"
189
+
190
+ sleep 60 &
191
+ pid=$!
192
+ record_live_watcher "$dir" "$pid" || fail "could not record the live watcher for recovery"
193
+ touch "$home/state/.last-watcher-beat"
194
+ healthy=$(run_guard_case "$dir")
195
+ kill "$pid" 2>/dev/null || true
196
+ wait "$pid" 2>/dev/null || true
197
+ [ -z "$healthy" ] || fail "guard should be silent after watcher recovery, got: $healthy"
198
+ assert_absent "$home/state/.guard-watcher-stale-banner" \
199
+ "healthy recovery must clear the stale-banner marker"
200
+
201
+ rm -f "$home/state/.last-watcher-beat"
202
+ rm -rf "$home/state/.watch.lock"
203
+ out2=$(run_guard_case "$dir")
204
+ [ "$(count_text "$out2" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
205
+ || fail "second stale episode did not re-print the full banner: $out2"
206
+ pass "fm-guard stale banner: healthy recovery rearms the next stale episode"
207
+ }
208
+
209
+ test_concurrent_same_episode_prints_one_full_banner() {
210
+ local dir out_dir i pids pid all full reminders
211
+ dir=$(make_guard_case concurrent-stale)
212
+ out_dir="$dir/outs"
213
+ mkdir -p "$out_dir"
214
+ pids=
215
+ i=1
216
+ while [ "$i" -le 30 ]; do
217
+ (
218
+ run_guard_case "$dir" > "$out_dir/$i.out" 2>&1
219
+ ) &
220
+ pids="$pids $!"
221
+ i=$((i + 1))
222
+ done
223
+ for pid in $pids; do
224
+ wait "$pid" 2>/dev/null || fail "concurrent guard subprocess failed"
225
+ done
226
+ all=$(cat "$out_dir"/*.out)
227
+ full=$(count_text "$all" "WATCHER DOWN - SUPERVISION IS OFF")
228
+ reminders=$(count_text "$all" "full banner already printed this episode")
229
+ [ "$full" -eq 1 ] || fail "concurrent same-episode calls printed $full full banners"$'\n'"$all"
230
+ [ "$reminders" -eq 29 ] || fail "concurrent same-episode calls printed $reminders reminders, expected 29"$'\n'"$all"
231
+ pass "fm-guard stale banner: concurrent same-episode calls claim exactly one full banner"
232
+ }
233
+
234
+ test_home_isolation() {
235
+ local dir_a dir_b out_a1 out_a2 out_b1
236
+ dir_a=$(make_guard_case home-a)
237
+ dir_b=$(make_guard_case home-b)
238
+ out_a1=$(run_guard_case "$dir_a")
239
+ out_b1=$(run_guard_case "$dir_b")
240
+ out_a2=$(run_guard_case "$dir_a")
241
+ [ "$(count_text "$out_a1" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
242
+ || fail "home A first stale call did not print a full banner: $out_a1"
243
+ [ "$(count_text "$out_b1" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
244
+ || fail "home B first stale call was suppressed by home A: $out_b1"
245
+ assert_contains "$out_a2" "full banner already printed this episode" \
246
+ "home A repeated stale call did not remember its own episode"
247
+ pass "fm-guard stale banner: deduplication is isolated per FM_HOME"
248
+ }
249
+
250
+ test_queued_wake_warning_stays_independent() {
251
+ local dir home out1 out2
252
+ dir=$(make_guard_case queued-wake)
253
+ home=$(case_home "$dir")
254
+ out1=$(run_guard_case "$dir")
255
+ [ "$(count_text "$out1" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
256
+ || fail "first stale call did not print the full banner before queued wake case: $out1"
257
+ printf 'signal: %s/state/task.status\n' "$home" > "$home/state/.wake-queue"
258
+ out2=$(run_guard_case "$dir")
259
+ assert_contains "$out2" "full banner already printed this episode" \
260
+ "same-episode stale call should still print its concise reminder"
261
+ assert_contains "$out2" "queued wakes pending" \
262
+ "queued wake warning must not be suppressed by stale-banner deduplication"
263
+ pass "fm-guard stale banner: queued-wake warning remains independent"
264
+ }
265
+
266
+ test_read_only_before_writable_does_not_consume_full_banner() {
267
+ local dir home marker lock out_ro out_rw
268
+ dir=$(make_guard_case read-only-before-writable)
269
+ home=$(case_home "$dir")
270
+ marker="$home/state/.guard-watcher-stale-banner"
271
+ lock="$home/state/.guard-watcher-stale-banner.lock"
272
+
273
+ out_ro=$(run_guard_case_read_only "$dir")
274
+ [ "$(count_text "$out_ro" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
275
+ || fail "read-only stale call should print the advisory full banner: $out_ro"
276
+ assert_absent "$marker" "read-only stale call must not create the stale-banner marker"
277
+ assert_absent "$lock" "read-only stale call must not create the stale-banner lock"
278
+
279
+ out_rw=$(run_guard_case "$dir")
280
+ [ "$(count_text "$out_rw" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
281
+ || fail "writable stale call should still receive the full banner after read-only: $out_rw"
282
+ assert_present "$marker" "writable stale call should claim the stale-banner marker"
283
+ pass "fm-guard stale banner: read-only before writable does not consume full banner"
284
+ }
285
+
286
+ test_read_only_during_episode_observes_without_mutating_marker() {
287
+ local dir home marker before after out_ro
288
+ dir=$(make_guard_case read-only-during-episode)
289
+ home=$(case_home "$dir")
290
+ marker="$home/state/.guard-watcher-stale-banner"
291
+
292
+ run_guard_case "$dir" >/dev/null
293
+ before=$(cat "$marker")
294
+ out_ro=$(run_guard_case_read_only "$dir")
295
+ after=$(cat "$marker")
296
+ assert_contains "$out_ro" "full banner already printed this episode" \
297
+ "read-only stale call during a claimed episode should print the concise reminder"
298
+ [ "$after" = "$before" ] || fail "read-only stale call must not update an existing marker"
299
+ pass "fm-guard stale banner: read-only during episode observes without mutating marker"
300
+ }
301
+
302
+ test_healthy_read_only_does_not_clear_marker() {
303
+ local dir home marker before after healthy pid
304
+ dir=$(make_guard_case healthy-read-only)
305
+ home=$(case_home "$dir")
306
+ marker="$home/state/.guard-watcher-stale-banner"
307
+
308
+ run_guard_case "$dir" >/dev/null
309
+ before=$(cat "$marker")
310
+ sleep 60 &
311
+ pid=$!
312
+ record_live_watcher "$dir" "$pid" || fail "could not record the live watcher for read-only recovery"
313
+ touch "$home/state/.last-watcher-beat"
314
+ healthy=$(run_guard_case_read_only "$dir")
315
+ kill "$pid" 2>/dev/null || true
316
+ wait "$pid" 2>/dev/null || true
317
+ [ -z "$healthy" ] || fail "healthy read-only guard should stay silent, got: $healthy"
318
+ assert_present "$marker" "healthy read-only guard must not clear the stale-banner marker"
319
+ after=$(cat "$marker")
320
+ [ "$after" = "$before" ] || fail "healthy read-only guard must not update the marker"
321
+ pass "fm-guard stale banner: healthy read-only does not clear marker"
322
+ }
323
+
324
+ test_read_only_never_mutates_stale_banner_state_files() {
325
+ local dir home marker lock before after no_work
326
+ dir=$(make_guard_case read-only-state-nonmutation)
327
+ home=$(case_home "$dir")
328
+ marker="$home/state/.guard-watcher-stale-banner"
329
+ lock="$home/state/.guard-watcher-stale-banner.lock"
330
+ printf '%s\n' "sentinel-marker" > "$marker"
331
+
332
+ before=$(find "$home/state" -maxdepth 1 -mindepth 1 -name '.guard-watcher-stale-banner*' -print | sort)
333
+ run_guard_case_read_only "$dir" >/dev/null
334
+ after=$(find "$home/state" -maxdepth 1 -mindepth 1 -name '.guard-watcher-stale-banner*' -print | sort)
335
+ [ "$after" = "$before" ] || fail "stale read-only guard changed stale-banner state files"$'\n'"before: $before"$'\n'"after: $after"
336
+ [ "$(cat "$marker")" = "sentinel-marker" ] || fail "stale read-only guard updated the marker content"
337
+ assert_absent "$lock" "stale read-only guard must not create the stale-banner lock"
338
+
339
+ rm -f "$home/state/task.meta"
340
+ no_work=$(run_guard_case_read_only "$dir")
341
+ [ -z "$no_work" ] || fail "read-only guard with no in-flight work should stay silent, got: $no_work"
342
+ after=$(find "$home/state" -maxdepth 1 -mindepth 1 -name '.guard-watcher-stale-banner*' -print | sort)
343
+ [ "$after" = "$before" ] || fail "no-work read-only guard changed stale-banner state files"$'\n'"before: $before"$'\n'"after: $after"
344
+ [ "$(cat "$marker")" = "sentinel-marker" ] || fail "no-work read-only guard updated the marker content"
345
+ pass "fm-guard stale banner: read-only never mutates stale-banner state files"
346
+ }
347
+
348
+ test_autoarm_fresh_beacon_without_watcher_is_healthy() {
349
+ local dir out
350
+ dir=$(make_guard_case autoarm-fresh)
351
+ # A fresh beacon and NO live watcher: the healthy mid-turn state under the
352
+ # Claude Stop auto-arm model, where the watcher only runs between turns.
353
+ touch "$(case_home "$dir")/state/.last-watcher-beat"
354
+ out=$(run_guard_case_autoarm "$dir")
355
+ [ -z "$out" ] \
356
+ || fail "auto-arm model with a fresh beacon and no live watcher must stay silent, got: $out"
357
+ pass "fm-guard stale banner: auto-arm fresh beacon without a live watcher is healthy"
358
+ }
359
+
360
+ test_autoarm_stale_beacon_alarms_with_correct_reason() {
361
+ local dir out
362
+ dir=$(make_guard_case autoarm-stale)
363
+ # No beacon at all -> a genuine supervision lapse even under the auto-arm model.
364
+ out=$(run_guard_case_autoarm "$dir")
365
+ [ "$(count_text "$out" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
366
+ || fail "auto-arm model with an absent/stale beacon must alarm: $out"
367
+ assert_contains "$out" "no watcher has a fresh beacon" \
368
+ "auto-arm stale-beacon banner must name the stale-beacon reason"
369
+ pass "fm-guard stale banner: auto-arm stale beacon alarms with the true reason"
370
+ }
371
+
372
+ test_autoarm_stale_episode_is_stable() {
373
+ local dir out1 out2
374
+ dir=$(make_guard_case autoarm-stable-episode)
375
+ out1=$(run_guard_case_autoarm "$dir")
376
+ out2=$(run_guard_case_autoarm "$dir")
377
+ [ "$(count_text "$out1" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
378
+ || fail "first auto-arm stale call did not print the full banner: $out1"
379
+ [ "$(count_text "$out2" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 0 ] \
380
+ || fail "auto-arm stale episode re-printed the full banner instead of deduping: $out2"
381
+ assert_contains "$out2" "full banner already printed this episode" \
382
+ "second auto-arm stale call did not print the concise reminder"
383
+ pass "fm-guard stale banner: auto-arm stale episode stays one episode across calls"
384
+ }
385
+
386
+ test_persistent_no_watcher_banner_names_missing_process() {
387
+ local dir out
388
+ dir=$(make_guard_case persistent-no-watcher-reason)
389
+ # A fresh beacon with no live watcher under the persistent model: the real
390
+ # failing condition is the missing process, not a stale beacon.
391
+ touch "$(case_home "$dir")/state/.last-watcher-beat"
392
+ out=$(run_guard_case "$dir")
393
+ assert_contains "$out" "no live watcher process holds this home lock" \
394
+ "persistent no-watcher banner must name the missing watcher process"
395
+ assert_not_contains "$out" "no watcher has a fresh beacon" \
396
+ "persistent no-watcher banner must not blame the fresh beacon"
397
+ pass "fm-guard stale banner: persistent no-watcher banner names the true reason"
398
+ }
399
+
400
+ test_persistent_no_watcher_episode_survives_beacon_touch() {
401
+ local dir home out1 out2
402
+ dir=$(make_guard_case persistent-no-watcher-episode)
403
+ home=$(case_home "$dir")
404
+ touch "$home/state/.last-watcher-beat"
405
+ out1=$(run_guard_case "$dir")
406
+ [ "$(count_text "$out1" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
407
+ || fail "first persistent no-watcher call did not print the full banner: $out1"
408
+ # The beacon mtime advancing with NO live watcher must not split the continuous
409
+ # down-episode. The old beacon-mtime episode key re-printed the full banner
410
+ # here; the reason-based key keeps it a single episode. Separate the touches by
411
+ # a second so the mtime genuinely changes at whole-second stat granularity.
412
+ sleep 1
413
+ touch "$home/state/.last-watcher-beat"
414
+ out2=$(run_guard_case "$dir")
415
+ [ "$(count_text "$out2" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 0 ] \
416
+ || fail "advancing the beacon mtime with no live watcher re-printed the banner: $out2"
417
+ assert_contains "$out2" "full banner already printed this episode" \
418
+ "same no-watcher episode did not print the concise reminder after a beacon touch"
419
+ pass "fm-guard stale banner: a no-watcher episode survives a beacon mtime change"
420
+ }
421
+
422
+ # The send-time false alarm this suite exists to pin: on a Pi primary the watcher
423
+ # process is torn down and respawned by the extension on every actionable wake, so
424
+ # a guarded command that lands in a hand-off sees a fresh beacon and an unheld lock
425
+ # - state the persistent model cannot tell apart from supervision being off.
426
+ test_extension_handoff_with_live_session_is_healthy() {
427
+ local dir home out pid
428
+ dir=$(make_guard_case extension-handoff)
429
+ home=$(case_home "$dir")
430
+ sleep 60 &
431
+ pid=$!
432
+ record_pi_extension_session "$dir" "$pid" || fail "could not record the Pi extension session"
433
+ touch "$home/state/.last-watcher-beat"
434
+ out=$(run_guard_case_extension "$dir")
435
+ kill "$pid" 2>/dev/null || true
436
+ wait "$pid" 2>/dev/null || true
437
+ [ -z "$out" ] \
438
+ || fail "an extension-owned hand-off with a live Pi session must stay silent, got: $out"
439
+ assert_absent "$home/state/.guard-watcher-stale-banner" \
440
+ "a healthy extension-owned hand-off must not open a down-episode"
441
+ pass "fm-guard stale banner: extension-owned hand-off with a live session is healthy"
442
+ }
443
+
444
+ # A released owner may leave the lock directory briefly before cleanup. It is
445
+ # still genuinely unheld when it records no pid, so the hand-off stays benign.
446
+ test_extension_handoff_with_empty_lock_is_healthy() {
447
+ local dir home out pid
448
+ dir=$(make_guard_case extension-empty-lock)
449
+ home=$(case_home "$dir")
450
+ sleep 60 &
451
+ pid=$!
452
+ record_pi_extension_session "$dir" "$pid" || fail "could not record the Pi extension session"
453
+ mkdir -p "$home/state/.watch.lock"
454
+ touch "$home/state/.last-watcher-beat"
455
+ out=$(run_guard_case_extension "$dir")
456
+ kill "$pid" 2>/dev/null || true
457
+ wait "$pid" 2>/dev/null || true
458
+ [ -z "$out" ] \
459
+ || fail "an extension-owned hand-off with an empty lock must stay silent, got: $out"
460
+ assert_absent "$home/state/.guard-watcher-stale-banner" \
461
+ "an empty lock during a healthy hand-off must not open a down-episode"
462
+ pass "fm-guard stale banner: extension-owned empty lock is genuinely unheld"
463
+ }
464
+
465
+ # Extension ownership tolerates only a released lock. Every non-empty recorded
466
+ # pid means the lock is held, so any strict watcher-health failure stays loud.
467
+ test_extension_held_unhealthy_locks_stay_alarm() {
468
+ local dir home out session_pid holder_pid case_name
469
+ for case_name in dead-pid malformed-pid wrong-home wrong-path identity-mismatch; do
470
+ dir=$(make_guard_case "extension-held-$case_name")
471
+ home=$(case_home "$dir")
472
+ sleep 60 &
473
+ session_pid=$!
474
+ record_pi_extension_session "$dir" "$session_pid" \
475
+ || fail "could not record the Pi extension session for $case_name"
476
+ holder_pid=
477
+ case "$case_name" in
478
+ malformed-pid)
479
+ mkdir -p "$home/state/.watch.lock"
480
+ printf '%s\n' not-a-pid > "$home/state/.watch.lock/pid"
481
+ ;;
482
+ *)
483
+ sleep 60 &
484
+ holder_pid=$!
485
+ record_live_watcher "$dir" "$holder_pid" \
486
+ || fail "could not record the watcher lock for $case_name"
487
+ case "$case_name" in
488
+ dead-pid)
489
+ kill "$holder_pid" 2>/dev/null || true
490
+ wait "$holder_pid" 2>/dev/null || true
491
+ holder_pid=
492
+ ;;
493
+ wrong-home)
494
+ printf '%s\n' "$home/other" > "$home/state/.watch.lock/fm-home"
495
+ ;;
496
+ wrong-path)
497
+ printf '%s\n' "$home/bin/not-fm-watch.sh" > "$home/state/.watch.lock/watcher-path"
498
+ ;;
499
+ identity-mismatch)
500
+ printf '%s\n' mismatched-identity > "$home/state/.watch.lock/pid-identity"
501
+ ;;
502
+ esac
503
+ ;;
504
+ esac
505
+ touch "$home/state/.last-watcher-beat"
506
+ out=$(run_guard_case_extension "$dir")
507
+ [ -z "$holder_pid" ] || kill "$holder_pid" 2>/dev/null || true
508
+ [ -z "$holder_pid" ] || wait "$holder_pid" 2>/dev/null || true
509
+ kill "$session_pid" 2>/dev/null || true
510
+ wait "$session_pid" 2>/dev/null || true
511
+ [ "$(count_text "$out" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
512
+ || fail "an extension-owned held lock with $case_name must alarm: $out"
513
+ assert_contains "$out" "no live watcher process holds this home lock" \
514
+ "a held unhealthy lock with $case_name must report no-watcher"
515
+ done
516
+ pass "fm-guard stale banner: held unhealthy extension locks stay loud"
517
+ }
518
+
519
+ # The same unheld lock and fresh beacon, with NO extension ownership to prove, is
520
+ # the genuinely-down cycle and must stay exactly as loud as before.
521
+ test_extension_without_ownership_evidence_stays_alarm() {
522
+ local dir home out
523
+ dir=$(make_guard_case extension-no-evidence)
524
+ home=$(case_home "$dir")
525
+ touch "$home/state/.last-watcher-beat"
526
+ out=$(run_guard_case_extension "$dir")
527
+ [ "$(count_text "$out" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
528
+ || fail "an unheld lock with no extension ownership evidence must alarm: $out"
529
+ assert_contains "$out" "no live watcher process holds this home lock" \
530
+ "the unowned extension-model banner must name the missing watcher process"
531
+ pass "fm-guard stale banner: extension model without ownership evidence stays loud"
532
+ }
533
+
534
+ # Drive the ownership signals apart one at a time. Each part is load-bearing on its
535
+ # own, so losing any single one restores the alarm rather than leaving the tolerance
536
+ # resting on whichever signal happens to survive.
537
+ test_extension_ownership_needs_every_signal() {
538
+ local dir home out pid case_name spec
539
+ for spec in \
540
+ "dead-session:dead::" \
541
+ "missing-watch-marker:live:watch:" \
542
+ "missing-turnend-marker:live:turnend:" \
543
+ "drifted-watch-build:live::watch" \
544
+ "drifted-turnend-build:live::turnend"; do
545
+ case_name=${spec%%:*}
546
+ dir=$(make_guard_case "extension-$case_name")
547
+ home=$(case_home "$dir")
548
+ sleep 60 &
549
+ pid=$!
550
+ if [ "$(printf '%s' "$spec" | cut -d: -f2)" = dead ]; then
551
+ kill "$pid" 2>/dev/null || true
552
+ wait "$pid" 2>/dev/null || true
553
+ fi
554
+ record_pi_extension_session "$dir" "$pid" \
555
+ "$(printf '%s' "$spec" | cut -d: -f3)" \
556
+ "$(printf '%s' "$spec" | cut -d: -f4)" \
557
+ || fail "could not record the Pi extension session for $case_name"
558
+ touch "$home/state/.last-watcher-beat"
559
+ out=$(run_guard_case_extension "$dir")
560
+ kill "$pid" 2>/dev/null || true
561
+ wait "$pid" 2>/dev/null || true
562
+ [ "$(count_text "$out" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
563
+ || fail "extension ownership must not survive $case_name; guard output: $out"
564
+ done
565
+ pass "fm-guard stale banner: every extension-ownership signal is load-bearing"
566
+ }
567
+
568
+ # Ownership tolerates the hand-off, never a supervision lapse: once the beacon
569
+ # passes the grace window the extension has not restored the cycle and the banner
570
+ # must fire even with a fully live, correctly loaded Pi session.
571
+ test_extension_stale_beacon_alarms_despite_live_session() {
572
+ local dir home out pid
573
+ dir=$(make_guard_case extension-stale-beacon)
574
+ home=$(case_home "$dir")
575
+ sleep 60 &
576
+ pid=$!
577
+ record_pi_extension_session "$dir" "$pid" || fail "could not record the Pi extension session"
578
+ out=$(FM_ROOT_OVERRIDE="$(case_root "$dir")" \
579
+ FM_HOME="$home" \
580
+ FM_GUARD_GRACE=1 \
581
+ FM_SUPERVISION_MODEL=extension \
582
+ "$ROOT/bin/fm-guard.sh" 2>&1)
583
+ kill "$pid" 2>/dev/null || true
584
+ wait "$pid" 2>/dev/null || true
585
+ [ "$(count_text "$out" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
586
+ || fail "a beacon past grace must alarm even under a live Pi session: $out"
587
+ assert_contains "$out" "no watcher has a fresh beacon" \
588
+ "the extension-model stale-beacon banner must name the stale beacon"
589
+ pass "fm-guard stale banner: extension model still alarms on a genuinely stale beacon"
590
+ }
591
+
592
+ # The queued-wake hazard is independent of the watcher verdict and must survive the
593
+ # hand-off tolerance: a silenced banner must never take this warning down with it.
594
+ test_extension_handoff_keeps_queued_wake_warning() {
595
+ local dir home out pid
596
+ dir=$(make_guard_case extension-queued-wake)
597
+ home=$(case_home "$dir")
598
+ sleep 60 &
599
+ pid=$!
600
+ record_pi_extension_session "$dir" "$pid" || fail "could not record the Pi extension session"
601
+ touch "$home/state/.last-watcher-beat"
602
+ printf '%s\n' "1700000000 1 signal task signal: crewmate needs a decision" > "$home/state/.wake-queue"
603
+ out=$(run_guard_case_extension "$dir")
604
+ kill "$pid" 2>/dev/null || true
605
+ wait "$pid" 2>/dev/null || true
606
+ assert_contains "$out" "queued wakes pending" \
607
+ "the queued-wake warning must still fire during an extension-owned hand-off"
608
+ assert_not_contains "$out" "WATCHER DOWN - SUPERVISION IS OFF" \
609
+ "a queued wake must not resurrect the watcher-down banner for a healthy hand-off"
610
+ pass "fm-guard stale banner: queued-wake warning survives the extension hand-off tolerance"
611
+ }
612
+
613
+ # The tolerance is scoped to the extension model alone. Every persistent-watcher
614
+ # primary (codex, opencode, grok, kimi, tmux, unknown) must keep alarming on the
615
+ # same state, even when Pi extension markers happen to be present on disk.
616
+ test_persistent_model_ignores_pi_extension_evidence() {
617
+ local dir home out pid
618
+ dir=$(make_guard_case persistent-ignores-pi-evidence)
619
+ home=$(case_home "$dir")
620
+ sleep 60 &
621
+ pid=$!
622
+ record_pi_extension_session "$dir" "$pid" || fail "could not record the Pi extension session"
623
+ touch "$home/state/.last-watcher-beat"
624
+ out=$(run_guard_case "$dir")
625
+ kill "$pid" 2>/dev/null || true
626
+ wait "$pid" 2>/dev/null || true
627
+ [ "$(count_text "$out" "WATCHER DOWN - SUPERVISION IS OFF")" -eq 1 ] \
628
+ || fail "a persistent-watcher primary must still alarm with Pi markers present: $out"
629
+ assert_contains "$out" "no live watcher process holds this home lock" \
630
+ "the persistent-model banner must still name the missing watcher process"
631
+ pass "fm-guard stale banner: persistent primaries ignore Pi extension evidence"
632
+ }
633
+
634
+ # An extension-owned home with a genuinely live watcher is the ordinary steady
635
+ # state and must stay silent through the strict path, not through the tolerance.
636
+ test_extension_live_watcher_is_healthy_without_ownership_evidence() {
637
+ local dir home out pid
638
+ dir=$(make_guard_case extension-live-watcher)
639
+ home=$(case_home "$dir")
640
+ sleep 60 &
641
+ pid=$!
642
+ record_live_watcher "$dir" "$pid" || fail "could not record the live watcher"
643
+ touch "$home/state/.last-watcher-beat"
644
+ out=$(run_guard_case_extension "$dir")
645
+ kill "$pid" 2>/dev/null || true
646
+ wait "$pid" 2>/dev/null || true
647
+ [ -z "$out" ] \
648
+ || fail "a live identity-matched watcher must be healthy under the extension model, got: $out"
649
+ pass "fm-guard stale banner: extension model stays silent for a live watcher"
650
+ }
651
+
652
+ # The cases above pin the model. This one takes the end-user path instead: no
653
+ # FM_SUPERVISION_MODEL at all, so bin/fm-harness.sh must route a Pi primary to the
654
+ # extension model on its own. Without that routing the tolerance would never reach
655
+ # a real Pi home. The foreign markers are cleared because fm-harness.sh tests them
656
+ # ahead of Pi, and the host running this suite may carry one.
657
+ test_pi_harness_routes_itself_to_the_extension_model() {
658
+ local dir home out pid harness
659
+ local -a pi_env
660
+ for harness in pi pi-signed; do
661
+ pi_env=(PI_CODING_AGENT=true)
662
+ [ "$harness" = pi ] || pi_env+=(FM_PI_HARNESS=pi-signed)
663
+ dir=$(make_guard_case "harness-routing-$harness")
664
+ home=$(case_home "$dir")
665
+ sleep 60 &
666
+ pid=$!
667
+ record_pi_extension_session "$dir" "$pid" || fail "could not record the Pi extension session"
668
+ touch "$home/state/.last-watcher-beat"
669
+ out=$(env -u CLAUDECODE -u CURSOR_AGENT -u CURSOR_INVOKED_AS -u GROK_AGENT -u FM_SUPERVISION_MODEL \
670
+ "${pi_env[@]}" \
671
+ FM_ROOT_OVERRIDE="$(case_root "$dir")" \
672
+ FM_HOME="$home" \
673
+ FM_GUARD_GRACE=999 \
674
+ "$ROOT/bin/fm-guard.sh" 2>&1)
675
+ kill "$pid" 2>/dev/null || true
676
+ wait "$pid" 2>/dev/null || true
677
+ [ -z "$out" ] \
678
+ || fail "a $harness primary must route itself to the extension model, got: $out"
679
+ done
680
+ pass "fm-guard stale banner: Pi and pi-signed primaries route themselves to the extension model"
681
+ }
682
+
683
+ test_first_stale_call_prints_full_banner
684
+ test_repeated_same_episode_prints_reminder_only
685
+ test_pi_harness_routes_itself_to_the_extension_model
686
+ test_extension_handoff_with_live_session_is_healthy
687
+ test_extension_handoff_with_empty_lock_is_healthy
688
+ test_extension_held_unhealthy_locks_stay_alarm
689
+ test_extension_without_ownership_evidence_stays_alarm
690
+ test_extension_ownership_needs_every_signal
691
+ test_extension_stale_beacon_alarms_despite_live_session
692
+ test_extension_handoff_keeps_queued_wake_warning
693
+ test_persistent_model_ignores_pi_extension_evidence
694
+ test_extension_live_watcher_is_healthy_without_ownership_evidence
695
+ test_autoarm_fresh_beacon_without_watcher_is_healthy
696
+ test_autoarm_stale_beacon_alarms_with_correct_reason
697
+ test_autoarm_stale_episode_is_stable
698
+ test_persistent_no_watcher_banner_names_missing_process
699
+ test_persistent_no_watcher_episode_survives_beacon_touch
700
+ test_fresh_beacon_without_live_watcher_stays_alarm
701
+ test_x_mode_without_live_watcher_stays_alarm
702
+ test_healthy_recovery_rearms_next_stale_episode
703
+ test_concurrent_same_episode_prints_one_full_banner
704
+ test_home_isolation
705
+ test_queued_wake_warning_stays_independent
706
+ test_read_only_before_writable_does_not_consume_full_banner
707
+ test_read_only_during_episode_observes_without_mutating_marker
708
+ test_healthy_read_only_does_not_clear_marker
709
+ test_read_only_never_mutates_stale_banner_state_files