@zerwiz/ymir 0.1.4 → 0.1.7

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 (865) 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/README.md +74 -0
  27. package/.agents/backend/backends/cmux.sh +649 -0
  28. package/.agents/backend/backends/herdr-eventwait.py +157 -0
  29. package/.agents/backend/backends/herdr-workspace-move.py +114 -0
  30. package/.agents/backend/backends/herdr.sh +3386 -0
  31. package/.agents/backend/backends/orca.sh +290 -0
  32. package/.agents/backend/backends/tmux.sh +357 -0
  33. package/.agents/backend/backends/zellij.sh +700 -0
  34. package/.agents/backend/fm-afk-launch.sh +654 -0
  35. package/.agents/backend/fm-afk-return.sh +241 -0
  36. package/.agents/backend/fm-afk-start.sh +172 -0
  37. package/.agents/backend/fm-arm-command-policy.mjs +960 -0
  38. package/.agents/backend/fm-arm-pretool-check.sh +200 -0
  39. package/.agents/backend/fm-backend-hometag-lib.sh +52 -0
  40. package/.agents/backend/fm-backend.sh +981 -0
  41. package/.agents/backend/fm-backlog-handoff.sh +909 -0
  42. package/.agents/backend/fm-backlog-receive.sh +187 -0
  43. package/.agents/backend/fm-backlog-transition-lib.sh +779 -0
  44. package/.agents/backend/fm-bearings-board.sh +204 -0
  45. package/.agents/backend/fm-bearings-snapshot.sh +546 -0
  46. package/.agents/backend/fm-bootstrap.sh +1568 -0
  47. package/.agents/backend/fm-branch-outcome.sh +203 -0
  48. package/.agents/backend/fm-branch-prompt.sh +101 -0
  49. package/.agents/backend/fm-brief.sh +452 -0
  50. package/.agents/backend/fm-busy-event.sh +234 -0
  51. package/.agents/backend/fm-busy-lib.sh +986 -0
  52. package/.agents/backend/fm-captain-hold.sh +1001 -0
  53. package/.agents/backend/fm-cd-command-policy.mjs +153 -0
  54. package/.agents/backend/fm-cd-pretool-check.sh +190 -0
  55. package/.agents/backend/fm-check-lib.sh +74 -0
  56. package/.agents/backend/fm-check-register.sh +42 -0
  57. package/.agents/backend/fm-check-unregister.sh +52 -0
  58. package/.agents/backend/fm-classify-lib.sh +1770 -0
  59. package/.agents/backend/fm-claude-stop-autoarm.sh +334 -0
  60. package/.agents/backend/fm-composer-lib.sh +1416 -0
  61. package/.agents/backend/fm-config-inherit-lib.sh +1202 -0
  62. package/.agents/backend/fm-config-push.sh +254 -0
  63. package/.agents/backend/fm-control-lib.sh +251 -0
  64. package/.agents/backend/fm-control.sh +878 -0
  65. package/.agents/backend/fm-crew-state.sh +625 -0
  66. package/.agents/backend/fm-cursor-lib.sh +243 -0
  67. package/.agents/backend/fm-decision-hold.sh +232 -0
  68. package/.agents/backend/fm-doc-audience-check.sh +269 -0
  69. package/.agents/backend/fm-dod-lib.sh +67 -0
  70. package/.agents/backend/fm-ensure-agents-md.sh +253 -0
  71. package/.agents/backend/fm-extension-launch-barrier.mjs +129 -0
  72. package/.agents/backend/fm-extension.mjs +2577 -0
  73. package/.agents/backend/fm-extension.sh +16 -0
  74. package/.agents/backend/fm-ff-lib.sh +420 -0
  75. package/.agents/backend/fm-fleet-snapshot.sh +1463 -0
  76. package/.agents/backend/fm-fleet-sync.sh +458 -0
  77. package/.agents/backend/fm-fleet-view.sh +96 -0
  78. package/.agents/backend/fm-gate-refuse-lib.sh +102 -0
  79. package/.agents/backend/fm-guard.sh +242 -0
  80. package/.agents/backend/fm-harness.sh +195 -0
  81. package/.agents/backend/fm-herdr-ci-cleanup.sh +115 -0
  82. package/.agents/backend/fm-herdr-lab.sh +346 -0
  83. package/.agents/backend/fm-herdr-session-cleanup.sh +337 -0
  84. package/.agents/backend/fm-home-seed.sh +984 -0
  85. package/.agents/backend/fm-home-summary-refresh.sh +255 -0
  86. package/.agents/backend/fm-hook-host-lib.sh +36 -0
  87. package/.agents/backend/fm-inactive-reconcile.sh +531 -0
  88. package/.agents/backend/fm-inbox.sh +399 -0
  89. package/.agents/backend/fm-install-actionlint.sh +84 -0
  90. package/.agents/backend/fm-install-herdr.sh +96 -0
  91. package/.agents/backend/fm-install-shellcheck.sh +84 -0
  92. package/.agents/backend/fm-install-treehouse.sh +95 -0
  93. package/.agents/backend/fm-kimi-turnend-hook.sh +276 -0
  94. package/.agents/backend/fm-lease-lib.sh +218 -0
  95. package/.agents/backend/fm-lease.sh +189 -0
  96. package/.agents/backend/fm-line-cap-lib.sh +51 -0
  97. package/.agents/backend/fm-lint-workflows.sh +137 -0
  98. package/.agents/backend/fm-lint.sh +591 -0
  99. package/.agents/backend/fm-lock-lib.sh +104 -0
  100. package/.agents/backend/fm-lock.sh +107 -0
  101. package/.agents/backend/fm-marker-lib.sh +12 -0
  102. package/.agents/backend/fm-merge-local.sh +74 -0
  103. package/.agents/backend/fm-merge-outcome-lib.sh +138 -0
  104. package/.agents/backend/fm-nm-run-lib.sh +124 -0
  105. package/.agents/backend/fm-on.sh +125 -0
  106. package/.agents/backend/fm-operational-input.sh +253 -0
  107. package/.agents/backend/fm-peek.sh +46 -0
  108. package/.agents/backend/fm-pending-reply-lib.sh +1409 -0
  109. package/.agents/backend/fm-pr-check.sh +135 -0
  110. package/.agents/backend/fm-pr-lib.sh +1016 -0
  111. package/.agents/backend/fm-pr-merge.sh +707 -0
  112. package/.agents/backend/fm-pr-poll.sh +110 -0
  113. package/.agents/backend/fm-primary-scope-lib.sh +33 -0
  114. package/.agents/backend/fm-procevent-extension-capture.pl +259 -0
  115. package/.agents/backend/fm-procevent-lavish.sh +628 -0
  116. package/.agents/backend/fm-procevent-lib.sh +892 -0
  117. package/.agents/backend/fm-procevent-quota.sh +290 -0
  118. package/.agents/backend/fm-procevent-remote-reply.sh +577 -0
  119. package/.agents/backend/fm-procevent-when.sh +504 -0
  120. package/.agents/backend/fm-procevent.sh +1466 -0
  121. package/.agents/backend/fm-project-mode.sh +92 -0
  122. package/.agents/backend/fm-project-origin-lib.sh +180 -0
  123. package/.agents/backend/fm-promote.sh +282 -0
  124. package/.agents/backend/fm-public-followup-emit.sh +260 -0
  125. package/.agents/backend/fm-public-followup-lib.sh +398 -0
  126. package/.agents/backend/fm-public-followup.sh +1330 -0
  127. package/.agents/backend/fm-push-transition-lib.sh +168 -0
  128. package/.agents/backend/fm-quota-axi-lib.sh +93 -0
  129. package/.agents/backend/fm-quota-choose.sh +384 -0
  130. package/.agents/backend/fm-remote-delta-read.sh +189 -0
  131. package/.agents/backend/fm-remote-doctor.sh +800 -0
  132. package/.agents/backend/fm-remote-entrypoint.sh +193 -0
  133. package/.agents/backend/fm-remote-file.sh +234 -0
  134. package/.agents/backend/fm-remote-home-provision.sh +259 -0
  135. package/.agents/backend/fm-remote-home-seed.sh +259 -0
  136. package/.agents/backend/fm-remote-inherit-push.sh +90 -0
  137. package/.agents/backend/fm-remote-inherit.sh +179 -0
  138. package/.agents/backend/fm-remote-job-lib.sh +1241 -0
  139. package/.agents/backend/fm-remote-job-reap-orphans.sh +137 -0
  140. package/.agents/backend/fm-remote-job-worker.sh +1124 -0
  141. package/.agents/backend/fm-remote-readiness-lib.sh +44 -0
  142. package/.agents/backend/fm-remote-secondmate-control.sh +340 -0
  143. package/.agents/backend/fm-review-diff.sh +158 -0
  144. package/.agents/backend/fm-secondmate-charter-lib.sh +44 -0
  145. package/.agents/backend/fm-secondmate-nudge-lib.sh +68 -0
  146. package/.agents/backend/fm-secondmate-parent-lib.sh +70 -0
  147. package/.agents/backend/fm-secondmate-reconcile.sh +371 -0
  148. package/.agents/backend/fm-secondmate-registry-lib.sh +311 -0
  149. package/.agents/backend/fm-secondmate-report.sh +88 -0
  150. package/.agents/backend/fm-send.sh +1059 -0
  151. package/.agents/backend/fm-session-lock-lib.sh +176 -0
  152. package/.agents/backend/fm-session-start.sh +970 -0
  153. package/.agents/backend/fm-sessionstart-cursor.sh +40 -0
  154. package/.agents/backend/fm-sessionstart-nudge.sh +45 -0
  155. package/.agents/backend/fm-sessionstart-run.sh +145 -0
  156. package/.agents/backend/fm-spawn.sh +3153 -0
  157. package/.agents/backend/fm-startup-memory-budget-lib.sh +224 -0
  158. package/.agents/backend/fm-startup-memory-budget.sh +94 -0
  159. package/.agents/backend/fm-startup-network.sh +642 -0
  160. package/.agents/backend/fm-stow-cascade.sh +251 -0
  161. package/.agents/backend/fm-subagent-pretool-check.sh +207 -0
  162. package/.agents/backend/fm-supervise-daemon.sh +1732 -0
  163. package/.agents/backend/fm-supervision-instructions.sh +215 -0
  164. package/.agents/backend/fm-supervision-lib.sh +90 -0
  165. package/.agents/backend/fm-supervisor-target-lib.sh +78 -0
  166. package/.agents/backend/fm-tangle-lib.sh +53 -0
  167. package/.agents/backend/fm-task-inbox-lib.sh +402 -0
  168. package/.agents/backend/fm-tasks-axi-lib.sh +123 -0
  169. package/.agents/backend/fm-teardown.sh +2922 -0
  170. package/.agents/backend/fm-test-isolation-proof.sh +588 -0
  171. package/.agents/backend/fm-test-run.sh +2191 -0
  172. package/.agents/backend/fm-timeout-lib.sh +141 -0
  173. package/.agents/backend/fm-timing-lib.sh +179 -0
  174. package/.agents/backend/fm-tmux-lib.sh +291 -0
  175. package/.agents/backend/fm-tool-update-check.sh +898 -0
  176. package/.agents/backend/fm-trace-context-lib.sh +227 -0
  177. package/.agents/backend/fm-transition-lib.sh +103 -0
  178. package/.agents/backend/fm-turnend-guard-cursor.sh +391 -0
  179. package/.agents/backend/fm-turnend-guard-grok.sh +90 -0
  180. package/.agents/backend/fm-turnend-guard.sh +432 -0
  181. package/.agents/backend/fm-update.sh +110 -0
  182. package/.agents/backend/fm-vendor-auth-probe.sh +190 -0
  183. package/.agents/backend/fm-voice-client.py +1373 -0
  184. package/.agents/backend/fm-voice-relay.py +1256 -0
  185. package/.agents/backend/fm-wake-drain.sh +592 -0
  186. package/.agents/backend/fm-wake-grant.sh +127 -0
  187. package/.agents/backend/fm-wake-lib.sh +1849 -0
  188. package/.agents/backend/fm-watch-arm.sh +602 -0
  189. package/.agents/backend/fm-watch-checkpoint.sh +109 -0
  190. package/.agents/backend/fm-watch.sh +1962 -0
  191. package/.agents/backend/fm-x-dismiss.sh +116 -0
  192. package/.agents/backend/fm-x-followup.sh +286 -0
  193. package/.agents/backend/fm-x-lib.sh +1000 -0
  194. package/.agents/backend/fm-x-link.sh +234 -0
  195. package/.agents/backend/fm-x-poll.sh +185 -0
  196. package/.agents/backend/fm-x-reply.sh +385 -0
  197. package/.agents/backend/fm_voice_frame.py +166 -0
  198. package/.agents/backend/fm_voice_records.py +574 -0
  199. package/.agents/backend/model-bridge.py +360 -0
  200. package/.agents/backend/opencode-go-bridge.py +30 -0
  201. package/.agents/bus/README.md +12 -0
  202. package/.agents/bus/messages.json +1 -0
  203. package/.agents/config/agents.machine.example.yaml +16 -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 +11 -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/filebrowser/.filebrowser.json +6 -0
  211. package/.agents/filebrowser/README.md +11 -0
  212. package/.agents/gateway/README.md +12 -0
  213. package/.agents/github/README.md +15 -0
  214. package/.agents/github/webhooks/.gitkeep +0 -0
  215. package/.agents/github/workflows/.gitkeep +0 -0
  216. package/.agents/harness/opencode/plugins/lib/rodd-operational-input.js +37 -0
  217. package/.agents/harness/opencode/plugins/package.json +4 -0
  218. package/.agents/harness/opencode/plugins/saga-sessionstart.js +77 -0
  219. package/.agents/harness/opencode/plugins/syn-cd-check.js +61 -0
  220. package/.agents/harness/opencode/plugins/syn-pretool-check.js +68 -0
  221. package/.agents/harness/opencode/plugins/syn-turnend-guard.js +105 -0
  222. package/.agents/harness/opencode/plugins/syn-watch-arm.js +521 -0
  223. package/.agents/memory/README.md +46 -0
  224. package/.agents/memory/runes_audit.md +5 -0
  225. package/.agents/migrations/0001-hodd-layout.sh +38 -0
  226. package/.agents/migrations/0002-a2a-mcp.sh +27 -0
  227. package/.agents/migrations/0003-private-data-separation.sh +121 -0
  228. package/.agents/migrations/0004-hoard-and-realms.sh +104 -0
  229. package/.agents/migrations/0005-roots-out-of-tree.sh +158 -0
  230. package/.agents/sandbox/Dockerfile.utgard +19 -0
  231. package/.agents/sandbox/README.md +14 -0
  232. package/.agents/sandbox/utgard.config.json +14 -0
  233. package/.agents/skills/README.md +59 -0
  234. package/.agents/skills/bragi-marketing/README.md +3 -0
  235. package/.agents/skills/bragi-marketing/SKILL.md +82 -0
  236. package/.agents/skills/eindri-homes/README.md +3 -0
  237. package/.agents/skills/eindri-homes/SKILL.md +291 -0
  238. package/.agents/skills/eindri-homes/assets/control-plane.md +122 -0
  239. package/.agents/skills/eindri-homes/assets/dispatch.md +80 -0
  240. package/.agents/skills/frigg-consent/README.md +3 -0
  241. package/.agents/skills/frigg-consent/SKILL.md +56 -0
  242. package/.agents/skills/galdr-ymirsystem/README.md +3 -0
  243. package/.agents/skills/galdr-ymirsystem/SKILL.md +159 -0
  244. package/.agents/skills/galdr-ymirsystem/assets/README.md +74 -0
  245. package/.agents/skills/galdr-ymirsystem/assets/brokk-distro-runtime.md +390 -0
  246. package/.agents/skills/galdr-ymirsystem/assets/build-method.md +113 -0
  247. package/.agents/skills/galdr-ymirsystem/assets/build-tool-categories.md +178 -0
  248. package/.agents/skills/galdr-ymirsystem/assets/eindri-orchestration.md +518 -0
  249. package/.agents/skills/galdr-ymirsystem/assets/eindri-profiles.md +136 -0
  250. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +736 -0
  251. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/claude-code.md +197 -0
  252. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/codex.md +191 -0
  253. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/cursor.md +193 -0
  254. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/opencode.md +262 -0
  255. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/pi.md +295 -0
  256. package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +539 -0
  257. package/.agents/skills/galdr-ymirsystem/assets/installation.md +763 -0
  258. package/.agents/skills/galdr-ymirsystem/assets/local-models/HOST-DATA.example.md +72 -0
  259. package/.agents/skills/galdr-ymirsystem/assets/local-models.md +274 -0
  260. package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +163 -0
  261. package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +415 -0
  262. package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
  263. package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +133 -0
  264. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/einherjar-spawn.schema.json +81 -0
  265. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/herdr-profile.toml +75 -0
  266. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +101 -0
  267. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/supervision-tree.yml +163 -0
  268. package/.agents/skills/galdr-ymirsystem/assets/pi-boot-guide.md +58 -0
  269. package/.agents/skills/galdr-ymirsystem/assets/porting-upstream-to-norse.md +301 -0
  270. package/.agents/skills/galdr-ymirsystem/assets/principles.md +385 -0
  271. package/.agents/skills/galdr-ymirsystem/assets/reference-adoption.md +99 -0
  272. package/.agents/skills/galdr-ymirsystem/assets/registry.md +379 -0
  273. package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +463 -0
  274. package/.agents/skills/galdr-ymirsystem/assets/runtime-components.md +553 -0
  275. package/.agents/skills/galdr-ymirsystem/assets/smidja.md +180 -0
  276. package/.agents/skills/galdr-ymirsystem/schemas/toon-schemas.md +190 -0
  277. package/.agents/skills/galdr-ymirsystem/scripts/README.md +45 -0
  278. package/.agents/skills/galdr-ymirsystem/scripts/bench-one.sh +248 -0
  279. package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +503 -0
  280. package/.agents/skills/galdr-ymirsystem/scripts/gpu-sample.sh +43 -0
  281. package/.agents/skills/galdr-ymirsystem/scripts/stop-all.sh +34 -0
  282. package/.agents/skills/galdr-ymirsystem/scripts/toon-check.py +188 -0
  283. package/.agents/skills/gjallarhorn-relay/README.md +3 -0
  284. package/.agents/skills/gjallarhorn-relay/SKILL.md +283 -0
  285. package/.agents/skills/groa-update/SKILL.md +90 -0
  286. package/.agents/skills/hamr-adapters/README.md +3 -0
  287. package/.agents/skills/hamr-adapters/SKILL.md +123 -0
  288. package/.agents/skills/hamr-adapters/references/common/control-and-recovery.md +37 -0
  289. package/.agents/skills/hamr-adapters/references/common/dispatch.md +32 -0
  290. package/.agents/skills/hamr-adapters/references/common/model-and-effort.md +42 -0
  291. package/.agents/skills/hamr-adapters/references/common/primary-hooks.md +40 -0
  292. package/.agents/skills/hamr-adapters/references/harness/claude.md +55 -0
  293. package/.agents/skills/hamr-adapters/references/harness/codex.md +43 -0
  294. package/.agents/skills/hamr-adapters/references/harness/cursor.md +75 -0
  295. package/.agents/skills/hamr-adapters/references/harness/grok.md +69 -0
  296. package/.agents/skills/hamr-adapters/references/harness/kimi.md +51 -0
  297. package/.agents/skills/hamr-adapters/references/harness/muse.md +70 -0
  298. package/.agents/skills/hamr-adapters/references/harness/opencode.md +42 -0
  299. package/.agents/skills/hamr-adapters/references/harness/pi.md +56 -0
  300. package/.agents/skills/herdr-panes/README.md +3 -0
  301. package/.agents/skills/herdr-panes/SKILL.md +92 -0
  302. package/.agents/skills/herdr-panes/assets/herdr-backend.md +346 -0
  303. package/.agents/skills/herdr-panes/assets/tmux-backend.md +115 -0
  304. package/.agents/skills/hnoss-design/DESIGN.md +19 -0
  305. package/.agents/skills/hnoss-design/README.md +3 -0
  306. package/.agents/skills/hnoss-design/SKILL.md +83 -0
  307. package/.agents/skills/hvild-afk/README.md +3 -0
  308. package/.agents/skills/hvild-afk/SKILL.md +241 -0
  309. package/.agents/skills/jord-projects/README.md +3 -0
  310. package/.agents/skills/jord-projects/SKILL.md +92 -0
  311. package/.agents/skills/lifecycle/SKILL.md +56 -0
  312. package/.agents/skills/lifecycle/smoke_test.sh +57 -0
  313. package/.agents/skills/lifecycle/start.sh +21 -0
  314. package/.agents/skills/lifecycle/status.sh +35 -0
  315. package/.agents/skills/lifecycle/stop.sh +16 -0
  316. package/.agents/skills/muninn-stow/README.md +3 -0
  317. package/.agents/skills/muninn-stow/SKILL.md +309 -0
  318. package/.agents/skills/no-mistakes/SKILL.md +379 -0
  319. package/.agents/skills/nornir-schedule/README.md +3 -0
  320. package/.agents/skills/nornir-schedule/SKILL.md +25 -0
  321. package/.agents/skills/nornir-schedule/assets/events.md +137 -0
  322. package/.agents/skills/nornir-schedule/assets/quota.md +131 -0
  323. package/.agents/skills/nsr-compliance/README.md +3 -0
  324. package/.agents/skills/nsr-compliance/SKILL.md +29 -0
  325. package/.agents/skills/nsr-compliance/assets/nsr/README.md +40 -0
  326. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/README.md +15 -0
  327. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/git-workflow.md +18 -0
  328. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/security.md +22 -0
  329. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/tech-stack.md +8 -0
  330. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/SKILL.md +71 -0
  331. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/config/core_four.yaml +16 -0
  332. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_danger.sh +102 -0
  333. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_wiring.sh +69 -0
  334. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/validate_code.sh +29 -0
  335. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/verify_docs.py +66 -0
  336. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/generate.sh +109 -0
  337. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/result_envelope.json +14 -0
  338. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/task_envelope.json +20 -0
  339. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/runner.py +303 -0
  340. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/telemetry/logger.py +112 -0
  341. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/README.md +18 -0
  342. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/config/README.md +10 -0
  343. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/README.md +12 -0
  344. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_env.sh +23 -0
  345. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_paths.sh +20 -0
  346. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_platform.sh +23 -0
  347. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/README.md +11 -0
  348. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/envelopes/README.md +10 -0
  349. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/README.md +10 -0
  350. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/install.sh +125 -0
  351. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/telemetry/README.md +11 -0
  352. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/README.md +20 -0
  353. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/README.md +36 -0
  354. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/SKILL-template.md +14 -0
  355. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/README.md +25 -0
  356. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/SKILL.md +13 -0
  357. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/create_branch.sh +6 -0
  358. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/safe_commit.sh +9 -0
  359. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/sync_upstream.sh +6 -0
  360. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/README.md +27 -0
  361. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/SKILL.md +15 -0
  362. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/smoke_test.sh +12 -0
  363. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/start.sh +19 -0
  364. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/status.sh +12 -0
  365. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/stop.sh +15 -0
  366. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/README.md +18 -0
  367. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/README.md +10 -0
  368. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/core_four.yaml +13 -0
  369. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/README.md +12 -0
  370. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_danger.sh +102 -0
  371. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_env.sh +23 -0
  372. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_paths.sh +20 -0
  373. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_platform.sh +23 -0
  374. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_wiring.sh +69 -0
  375. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/README.md +11 -0
  376. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/README.md +10 -0
  377. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/result_envelope.json +14 -0
  378. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/task_envelope.json +20 -0
  379. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/runner.py +303 -0
  380. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/README.md +10 -0
  381. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/install.sh +125 -0
  382. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/README.md +11 -0
  383. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/logger.py +112 -0
  384. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/README.md +14 -0
  385. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/api.md +17 -0
  386. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/compliance-wiring.md +155 -0
  387. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/frontend.md +15 -0
  388. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/README.md +11 -0
  389. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/README.md +23 -0
  390. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/README.md +16 -0
  391. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/template-README.md +11 -0
  392. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/deploy.sh +49 -0
  393. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/README.md +12 -0
  394. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/development.env.example +17 -0
  395. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/production.env.example +17 -0
  396. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/staging.env.example +17 -0
  397. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/README.md +11 -0
  398. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/template.env.example +8 -0
  399. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/README.md +26 -0
  400. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/agent.md +7 -0
  401. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/developers/dev-1.md +20 -0
  402. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/linux.md +9 -0
  403. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/macos.md +10 -0
  404. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/shared.md +19 -0
  405. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/windows.md +14 -0
  406. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/README.md +25 -0
  407. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/_template.md +41 -0
  408. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/hosting-1.md +41 -0
  409. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/README.md +21 -0
  410. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/README.md +15 -0
  411. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/deployment.md +19 -0
  412. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/disaster-recovery.md +15 -0
  413. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/ops-tasks.md +14 -0
  414. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/features/README.md +16 -0
  415. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/project-skills.md +146 -0
  416. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/README.md +11 -0
  417. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/software-compliance-research.md +269 -0
  418. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/working-with-agents.md +260 -0
  419. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/AGENTS.md +38 -0
  420. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/ARCHITECTURE.md +36 -0
  421. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/BEST_PRACTICES.md +27 -0
  422. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/CI_CD.md +23 -0
  423. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/FEATURES.md +15 -0
  424. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/README.md +22 -0
  425. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/RULES.md +37 -0
  426. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/STRUCTURE.md +45 -0
  427. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/TECH_STACK.md +28 -0
  428. package/.agents/skills/nsr-compliance/assets/nsr/scaffold-spec.md +63 -0
  429. package/.agents/skills/nsr-compliance/assets/nsr/scripts/scaffold.sh +103 -0
  430. package/.agents/skills/nsr-compliance/assets/nsr/scripts/validate.sh +52 -0
  431. package/.agents/skills/nsr-compliance/assets/nsrcompliance/SKILL.md +71 -0
  432. package/.agents/skills/nsr-compliance/assets/nsrcompliance/config/core_four.yaml +16 -0
  433. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_danger.sh +102 -0
  434. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_wiring.sh +69 -0
  435. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/validate_code.sh +29 -0
  436. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/verify_docs.py +66 -0
  437. package/.agents/skills/nsr-compliance/assets/nsrcompliance/generate.sh +109 -0
  438. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/result_envelope.json +14 -0
  439. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/task_envelope.json +20 -0
  440. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/runner.py +303 -0
  441. package/.agents/skills/nsr-compliance/assets/nsrcompliance/telemetry/logger.py +112 -0
  442. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/README.md +18 -0
  443. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/config/README.md +10 -0
  444. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/README.md +12 -0
  445. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_env.sh +23 -0
  446. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_paths.sh +20 -0
  447. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_platform.sh +23 -0
  448. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/README.md +11 -0
  449. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/envelopes/README.md +10 -0
  450. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/README.md +10 -0
  451. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/install.sh +125 -0
  452. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/telemetry/README.md +11 -0
  453. package/.agents/skills/pr-ops/README.md +3 -0
  454. package/.agents/skills/pr-ops/SKILL.md +109 -0
  455. package/.agents/skills/ratatoskr-a2a/README.md +3 -0
  456. package/.agents/skills/ratatoskr-a2a/SKILL.md +148 -0
  457. package/.agents/skills/rules-check-drift/SKILL.md +81 -0
  458. package/.agents/skills/saga-bearings/README.md +3 -0
  459. package/.agents/skills/saga-bearings/SKILL.md +26 -0
  460. package/.agents/skills/saga-bearings/assets/bearings.md +161 -0
  461. package/.agents/skills/saga-bearings/assets/board-template.html +731 -0
  462. package/.agents/skills/saga-bearings/assets/recap.md +56 -0
  463. package/.agents/skills/syn-recovery/README.md +3 -0
  464. package/.agents/skills/syn-recovery/SKILL.md +54 -0
  465. package/.agents/skills/tyr-check/README.md +3 -0
  466. package/.agents/skills/tyr-check/SKILL.md +218 -0
  467. package/.agents/skills/urdh-hold/README.md +3 -0
  468. package/.agents/skills/urdh-hold/SKILL.md +25 -0
  469. package/.agents/skills/urdh-hold/assets/decisions.md +15 -0
  470. package/.agents/skills/urdh-hold/assets/hold.md +54 -0
  471. package/.agents/skills/vor-diagnostics/README.md +3 -0
  472. package/.agents/skills/vor-diagnostics/SKILL.md +68 -0
  473. package/.agents/skills/ymir-host/README.md +3 -0
  474. package/.agents/skills/ymir-host/SKILL.md +35 -0
  475. package/.agents/skills/ymir-host/assets/desktop.md +36 -0
  476. package/.agents/skills/ymir-host/assets/install.md +69 -0
  477. package/.agents/skills/ymir-host/assets/omarchy.md +297 -0
  478. package/.agents/skills/ymir-host/assets/runbooks.md +15 -0
  479. package/.agents/skills/ymir-host/assets/thjazi.md +220 -0
  480. package/.agents/skills/ymir-host/assets/update.md +64 -0
  481. package/.agents/tests/assets/board-render-harness.mjs +117 -0
  482. package/.agents/tests/cmux-test-safety.sh +51 -0
  483. package/.agents/tests/fixtures.sh +291 -0
  484. package/.agents/tests/fm-afk-inject-e2e.test.sh +428 -0
  485. package/.agents/tests/fm-afk-inject-herdr-e2e.test.sh +536 -0
  486. package/.agents/tests/fm-afk-launch.test.sh +961 -0
  487. package/.agents/tests/fm-afk-pi-herdr-return-e2e.test.sh +297 -0
  488. package/.agents/tests/fm-afk-return.test.sh +283 -0
  489. package/.agents/tests/fm-arm-pretool-check.test.sh +482 -0
  490. package/.agents/tests/fm-ask-user-authority.test.sh +42 -0
  491. package/.agents/tests/fm-backend-autodetect-smoke.test.sh +176 -0
  492. package/.agents/tests/fm-backend-cmux-smoke.test.sh +188 -0
  493. package/.agents/tests/fm-backend-cmux.test.sh +1166 -0
  494. package/.agents/tests/fm-backend-herdr-eventwait-smoke.test.sh +136 -0
  495. package/.agents/tests/fm-backend-herdr-eventwait.test.py +106 -0
  496. package/.agents/tests/fm-backend-herdr-focus-flash-e2e.test.sh +409 -0
  497. package/.agents/tests/fm-backend-herdr-launcher-workspace-e2e.test.sh +437 -0
  498. package/.agents/tests/fm-backend-herdr-presentation-e2e.test.sh +1424 -0
  499. package/.agents/tests/fm-backend-herdr-prune-safety-e2e.test.sh +180 -0
  500. package/.agents/tests/fm-backend-herdr-respawn-idem-e2e.test.sh +182 -0
  501. package/.agents/tests/fm-backend-herdr-smoke.test.sh +355 -0
  502. package/.agents/tests/fm-backend-herdr-workspace-per-home-e2e.test.sh +251 -0
  503. package/.agents/tests/fm-backend-herdr.test.sh +4606 -0
  504. package/.agents/tests/fm-backend-orca.test.sh +1357 -0
  505. package/.agents/tests/fm-backend-tmux-smoke.test.sh +173 -0
  506. package/.agents/tests/fm-backend-zellij-smoke.test.sh +205 -0
  507. package/.agents/tests/fm-backend-zellij.test.sh +1360 -0
  508. package/.agents/tests/fm-backend.test.sh +1142 -0
  509. package/.agents/tests/fm-backlog-atomicity.test.sh +2302 -0
  510. package/.agents/tests/fm-backlog-handoff.test.sh +1360 -0
  511. package/.agents/tests/fm-bearings-board-render.test.sh +135 -0
  512. package/.agents/tests/fm-bearings-board.test.sh +414 -0
  513. package/.agents/tests/fm-bearings-snapshot.test.sh +1990 -0
  514. package/.agents/tests/fm-bootstrap-network-parallel.test.sh +316 -0
  515. package/.agents/tests/fm-bootstrap.test.sh +1178 -0
  516. package/.agents/tests/fm-branch-supervision.test.sh +552 -0
  517. package/.agents/tests/fm-brief.test.sh +785 -0
  518. package/.agents/tests/fm-busy-adapter-wiring.test.sh +324 -0
  519. package/.agents/tests/fm-busy-state.test.sh +465 -0
  520. package/.agents/tests/fm-calm-pi-extension.test.sh +3979 -0
  521. package/.agents/tests/fm-captain-hold-lifecycle.test.sh +1187 -0
  522. package/.agents/tests/fm-cd-pretool-check.test.sh +400 -0
  523. package/.agents/tests/fm-check-unregister.test.sh +198 -0
  524. package/.agents/tests/fm-classify-corr-token.test.sh +543 -0
  525. package/.agents/tests/fm-classify-decision-key.test.sh +340 -0
  526. package/.agents/tests/fm-claude-stop-autoarm-live-e2e.test.sh +164 -0
  527. package/.agents/tests/fm-claude-stop-autoarm.test.sh +1190 -0
  528. package/.agents/tests/fm-cmux-claude-composer-live-e2e.test.sh +110 -0
  529. package/.agents/tests/fm-codex-continuity-live-e2e.test.sh +55 -0
  530. package/.agents/tests/fm-composer-ghost.test.sh +716 -0
  531. package/.agents/tests/fm-composer-lib.test.sh +666 -0
  532. package/.agents/tests/fm-composer-matrix-live-e2e.test.sh +220 -0
  533. package/.agents/tests/fm-control-herdr-smoke.test.sh +149 -0
  534. package/.agents/tests/fm-control-relaunch.test.sh +1530 -0
  535. package/.agents/tests/fm-control.test.sh +910 -0
  536. package/.agents/tests/fm-crew-state.test.sh +1610 -0
  537. package/.agents/tests/fm-cursor-harness.test.sh +404 -0
  538. package/.agents/tests/fm-cursor-primary-live-e2e.test.sh +217 -0
  539. package/.agents/tests/fm-cursor-primary.test.sh +708 -0
  540. package/.agents/tests/fm-daemon.test.sh +2740 -0
  541. package/.agents/tests/fm-documentation-audiences.test.sh +141 -0
  542. package/.agents/tests/fm-ensure-agents-md.test.sh +371 -0
  543. package/.agents/tests/fm-extension-binding.test.sh +2187 -0
  544. package/.agents/tests/fm-fleet-snapshot-view.test.sh +816 -0
  545. package/.agents/tests/fm-fleet-sync.test.sh +721 -0
  546. package/.agents/tests/fm-gate-refuse.test.sh +356 -0
  547. package/.agents/tests/fm-gitignore-config.test.sh +89 -0
  548. package/.agents/tests/fm-gotmp.test.sh +230 -0
  549. package/.agents/tests/fm-grok-continuity-live-e2e.test.sh +112 -0
  550. package/.agents/tests/fm-grok-harness.test.sh +117 -0
  551. package/.agents/tests/fm-grok-stop-live-e2e.test.sh +219 -0
  552. package/.agents/tests/fm-guard-stale-banner.test.sh +709 -0
  553. package/.agents/tests/fm-harness-adapter-instructions-live-e2e.test.sh +134 -0
  554. package/.agents/tests/fm-harness-adapter-references.test.sh +30 -0
  555. package/.agents/tests/fm-harness-liveness-drift-live-e2e.test.sh +151 -0
  556. package/.agents/tests/fm-herdr-lab.test.sh +243 -0
  557. package/.agents/tests/fm-herdr-session-cleanup-e2e.test.sh +146 -0
  558. package/.agents/tests/fm-herdr-session-cleanup.test.sh +329 -0
  559. package/.agents/tests/fm-herdr-submit-confirm-live-e2e.test.sh +125 -0
  560. package/.agents/tests/fm-herdr-version-floor-live-e2e.test.sh +144 -0
  561. package/.agents/tests/fm-home-summary-refresh.test.sh +971 -0
  562. package/.agents/tests/fm-inactive-reconcile.test.sh +485 -0
  563. package/.agents/tests/fm-kimi-harness.test.sh +687 -0
  564. package/.agents/tests/fm-lint-workflows.test.sh +532 -0
  565. package/.agents/tests/fm-lint.test.sh +1023 -0
  566. package/.agents/tests/fm-muse-harness.test.sh +932 -0
  567. package/.agents/tests/fm-muse-signals-live-e2e.test.sh +205 -0
  568. package/.agents/tests/fm-no-mistakes-required.test.sh +72 -0
  569. package/.agents/tests/fm-on.test.sh +507 -0
  570. package/.agents/tests/fm-opencode-primary-live-e2e.test.sh +357 -0
  571. package/.agents/tests/fm-operational-input.test.sh +160 -0
  572. package/.agents/tests/fm-peek-remote.test.sh +110 -0
  573. package/.agents/tests/fm-pending-reply.test.sh +1288 -0
  574. package/.agents/tests/fm-pi-branch-extension.test.sh +3182 -0
  575. package/.agents/tests/fm-pi-branch-live-e2e.test.sh +527 -0
  576. package/.agents/tests/fm-pi-primary-live-e2e.test.sh +345 -0
  577. package/.agents/tests/fm-pi-primary-types.test.sh +68 -0
  578. package/.agents/tests/fm-pi-watch-extension.test.sh +2843 -0
  579. package/.agents/tests/fm-pr-check-security.test.sh +2140 -0
  580. package/.agents/tests/fm-pr-merge.test.sh +2135 -0
  581. package/.agents/tests/fm-procevent-quota.test.sh +225 -0
  582. package/.agents/tests/fm-procevent-when.test.sh +406 -0
  583. package/.agents/tests/fm-procevent.test.sh +1745 -0
  584. package/.agents/tests/fm-project-origin.test.sh +110 -0
  585. package/.agents/tests/fm-public-followup.test.sh +2272 -0
  586. package/.agents/tests/fm-quota-array-dispatch-live-e2e.test.sh +526 -0
  587. package/.agents/tests/fm-quota-choose.test.sh +626 -0
  588. package/.agents/tests/fm-remote-backlog-handoff.test.sh +453 -0
  589. package/.agents/tests/fm-remote-doctor.test.sh +625 -0
  590. package/.agents/tests/fm-remote-entrypoint.test.sh +59 -0
  591. package/.agents/tests/fm-remote-job-orphan-reap.test.sh +205 -0
  592. package/.agents/tests/fm-remote-job.test.sh +768 -0
  593. package/.agents/tests/fm-remote-reply.test.sh +520 -0
  594. package/.agents/tests/fm-remote-secondmate-lifecycle-e2e.test.sh +1256 -0
  595. package/.agents/tests/fm-remote-secondmate-parent-binding.test.sh +326 -0
  596. package/.agents/tests/fm-remote-secondmate-trace-context.test.sh +310 -0
  597. package/.agents/tests/fm-remote-transport-lanes.test.sh +431 -0
  598. package/.agents/tests/fm-review-diff.test.sh +176 -0
  599. package/.agents/tests/fm-secondmate-harness.test.sh +2600 -0
  600. package/.agents/tests/fm-secondmate-lifecycle-e2e.test.sh +259 -0
  601. package/.agents/tests/fm-secondmate-liveness.test.sh +559 -0
  602. package/.agents/tests/fm-secondmate-reconcile.test.sh +732 -0
  603. package/.agents/tests/fm-secondmate-safety.test.sh +2972 -0
  604. package/.agents/tests/fm-secondmate-sync.test.sh +913 -0
  605. package/.agents/tests/fm-send-inbox-doorbell-live-e2e.test.sh +206 -0
  606. package/.agents/tests/fm-send-inbox.test.sh +352 -0
  607. package/.agents/tests/fm-send-popup-settle.test.sh +167 -0
  608. package/.agents/tests/fm-send-remote-delivery.test.sh +796 -0
  609. package/.agents/tests/fm-send-resolve-key.test.sh +551 -0
  610. package/.agents/tests/fm-send-secondmate-marker-herdr-e2e.test.sh +187 -0
  611. package/.agents/tests/fm-send-secondmate-marker.test.sh +278 -0
  612. package/.agents/tests/fm-send-settle.test.sh +147 -0
  613. package/.agents/tests/fm-send-strict.test.sh +241 -0
  614. package/.agents/tests/fm-session-lock-ancestry.test.sh +365 -0
  615. package/.agents/tests/fm-session-start.test.sh +2515 -0
  616. package/.agents/tests/fm-sessionstart-hook-live-e2e.test.sh +631 -0
  617. package/.agents/tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh +230 -0
  618. package/.agents/tests/fm-sessionstart-nudge.test.sh +1039 -0
  619. package/.agents/tests/fm-shared-captain-inheritance.test.sh +404 -0
  620. package/.agents/tests/fm-spawn-batch.test.sh +148 -0
  621. package/.agents/tests/fm-spawn-dispatch-profile.test.sh +827 -0
  622. package/.agents/tests/fm-spawn-pool-base-freshen.test.sh +440 -0
  623. package/.agents/tests/fm-spawn-worktree-settle.test.sh +147 -0
  624. package/.agents/tests/fm-startup-memory-budget.test.sh +333 -0
  625. package/.agents/tests/fm-startup-network.test.sh +712 -0
  626. package/.agents/tests/fm-stow-cascade.test.sh +370 -0
  627. package/.agents/tests/fm-subagent-pretool-check.test.sh +291 -0
  628. package/.agents/tests/fm-supervision-events.test.sh +156 -0
  629. package/.agents/tests/fm-supervision-instructions.test.sh +189 -0
  630. package/.agents/tests/fm-tangle-guard.test.sh +277 -0
  631. package/.agents/tests/fm-task-delivery.test.sh +420 -0
  632. package/.agents/tests/fm-task-inbox.test.sh +513 -0
  633. package/.agents/tests/fm-teardown-endpoint-safety.test.sh +374 -0
  634. package/.agents/tests/fm-teardown.test.sh +2665 -0
  635. package/.agents/tests/fm-test-fixture-cleanup.test.sh +172 -0
  636. package/.agents/tests/fm-test-fixtures.test.sh +132 -0
  637. package/.agents/tests/fm-test-isolation-proof.test.sh +278 -0
  638. package/.agents/tests/fm-test-run.test.sh +1196 -0
  639. package/.agents/tests/fm-tmux-agent-liveness.test.sh +354 -0
  640. package/.agents/tests/fm-tmux-submit-busy.test.sh +340 -0
  641. package/.agents/tests/fm-tool-update-check.test.sh +1044 -0
  642. package/.agents/tests/fm-trace-context-lib.test.sh +253 -0
  643. package/.agents/tests/fm-trace-context-spawn.test.sh +581 -0
  644. package/.agents/tests/fm-transition-lib.test.sh +54 -0
  645. package/.agents/tests/fm-turnend-guard.test.sh +1822 -0
  646. package/.agents/tests/fm-update.test.sh +304 -0
  647. package/.agents/tests/fm-vendor-auth-probe.test.sh +395 -0
  648. package/.agents/tests/fm-voice-relay.test.sh +4741 -0
  649. package/.agents/tests/fm-wake-daemon-lifecycle-e2e.test.sh +171 -0
  650. package/.agents/tests/fm-wake-drain-open-decisions-cursor.test.sh +356 -0
  651. package/.agents/tests/fm-wake-drain-open-decisions.test.sh +226 -0
  652. package/.agents/tests/fm-wake-drain-unread-status.test.sh +379 -0
  653. package/.agents/tests/fm-wake-queue.test.sh +1224 -0
  654. package/.agents/tests/fm-watch-arm.test.sh +821 -0
  655. package/.agents/tests/fm-watch-checkpoint.test.sh +87 -0
  656. package/.agents/tests/fm-watch-recovery-loop.test.sh +223 -0
  657. package/.agents/tests/fm-watch-triage.test.sh +3889 -0
  658. package/.agents/tests/fm-watcher-lock.test.sh +1133 -0
  659. package/.agents/tests/fm-x-mode.test.sh +3042 -0
  660. package/.agents/tests/gleipnir-guard-pretool-check.test.sh +51 -0
  661. package/.agents/tests/gleipnir-machine-lock.test.sh +130 -0
  662. package/.agents/tests/herdr-test-safety.sh +42 -0
  663. package/.agents/tests/lib.sh +315 -0
  664. package/.agents/tests/remote-herdr-fixture.sh +125 -0
  665. package/.agents/tests/secondmate-helpers.sh +193 -0
  666. package/.agents/tests/smoke.test.sh +93 -0
  667. package/.agents/tests/wake-helpers.sh +338 -0
  668. package/.agents/tests/zellij-test-safety.sh +60 -0
  669. package/.agents/tools/README.md +10 -0
  670. package/.agents/tools/bin/tasks-cli +267 -0
  671. package/.agents/tools/tasks-cli.ts +216 -0
  672. package/AGENTS.md +498 -0
  673. package/CHANGELOG.md +1913 -0
  674. package/LICENSE +1 -1
  675. package/NOTICE +63 -0
  676. package/README.md +114 -60
  677. package/RULES/01-domains.md +51 -0
  678. package/RULES/02-agents.md +78 -0
  679. package/RULES/03-houses.md +61 -0
  680. package/RULES/04-hoard.md +97 -0
  681. package/RULES/05-platforms.md +67 -0
  682. package/RULES/06-append-only.md +108 -0
  683. package/RULES/07-config.md +101 -0
  684. package/RULES/08-delivery-gate.md +75 -0
  685. package/RULES/09-electron.md +58 -0
  686. package/RULES/README.md +22 -0
  687. package/bin/README.md +107 -0
  688. package/bin/a2a-mcp.sh +133 -0
  689. package/bin/a2a-serve.py +103 -0
  690. package/bin/a2a-serve.sh +8 -0
  691. package/bin/a2a-talk.sh +50 -0
  692. package/bin/a2abridge-ensure.sh +108 -0
  693. package/bin/agent-run.sh +43 -0
  694. package/bin/agents-config.sh +256 -0
  695. package/bin/apodex-smoke-test.sh +105 -0
  696. package/bin/bifrost-bridge.sh +131 -0
  697. package/bin/bifrost-ingress.sh +77 -0
  698. package/bin/bootstrap-macos.sh +112 -0
  699. package/bin/bootstrap-windows.ps1 +112 -0
  700. package/bin/branch-guard.sh +64 -0
  701. package/bin/brokk +73 -0
  702. package/bin/brokk-classify-lib.sh +1770 -0
  703. package/bin/brokk-lease-lib.sh +222 -0
  704. package/bin/brokk-lease.sh +193 -0
  705. package/bin/brokk-lint.sh +65 -0
  706. package/bin/brokk-send.sh +51 -0
  707. package/bin/brokk-update.sh +9 -0
  708. package/bin/brokk-wake-grant.sh +127 -0
  709. package/bin/brokk-wake-lib.sh +1849 -0
  710. package/bin/changelog-assemble.sh +154 -0
  711. package/bin/changelog-guard.sh +197 -0
  712. package/bin/crash-sense.sh +173 -0
  713. package/bin/design-check.sh +80 -0
  714. package/bin/design-icon.sh +153 -0
  715. package/bin/desktop-place.sh +224 -0
  716. package/bin/docs-guard.sh +39 -0
  717. package/bin/editor-place.sh +225 -0
  718. package/bin/eindri-acclaim.sh +47 -0
  719. package/bin/eindri-control.sh +28 -0
  720. package/bin/eindri-role.sh +83 -0
  721. package/bin/eindri-seat.sh +83 -0
  722. package/bin/eindri-seen.sh +54 -0
  723. package/bin/eindri-send.sh +15 -0
  724. package/bin/eindri-start.sh +98 -0
  725. package/bin/eindri-watch.sh +57 -0
  726. package/bin/einherjar-spawn.sh +576 -0
  727. package/bin/eir-doctor.sh +158 -0
  728. package/bin/erindi-brief.sh +271 -0
  729. package/bin/feature-inventory.sh +73 -0
  730. package/bin/fleet-apply.sh +81 -0
  731. package/bin/github-deploy.sh +94 -0
  732. package/bin/gjallarhorn-expose.sh +136 -0
  733. package/bin/gjallarhorn-notify.sh +71 -0
  734. package/bin/gjallarhorn-purge.sh +67 -0
  735. package/bin/gjallarhorn-tunnel.sh +85 -0
  736. package/bin/gleipnir-lock-lib.sh +284 -0
  737. package/bin/groa-update.sh +130 -0
  738. package/bin/hall-snapshot.sh +111 -0
  739. package/bin/hamr-harness.sh +246 -0
  740. package/bin/heimdall-ssh-keys.sh +374 -0
  741. package/bin/herdr-agents.py +44 -0
  742. package/bin/herdr-ensure.sh +104 -0
  743. package/bin/herdr-run.sh +452 -0
  744. package/bin/hermes-ensure.sh +88 -0
  745. package/bin/hlidskjalf-agents.sh +172 -0
  746. package/bin/hlidskjalf-usage.sh +196 -0
  747. package/bin/hoard-guard.sh +135 -0
  748. package/bin/hoard-lib.sh +105 -0
  749. package/bin/hodd.sh +102 -0
  750. package/bin/host-sense.sh +199 -0
  751. package/bin/huginn-research-worker.sh +121 -0
  752. package/bin/local-model-lock.sh +70 -0
  753. package/bin/mimir-bridge.py +259 -0
  754. package/bin/mimir-bridge.sh +100 -0
  755. package/bin/mimir-ingest.sh +131 -0
  756. package/bin/mimir-reflect.sh +27 -0
  757. package/bin/mimir.sh +113 -0
  758. package/bin/mjollnir-webhook.sh +72 -0
  759. package/bin/mjollnir.sh +104 -0
  760. package/bin/model-hardware.sh +96 -0
  761. package/bin/model-resolve.sh +160 -0
  762. package/bin/models-detect.sh +178 -0
  763. package/bin/nornir-cron-start.sh +162 -0
  764. package/bin/nornir-job-daily-briefing.sh +191 -0
  765. package/bin/nornir-job-git-sync.sh +152 -0
  766. package/bin/nornir-job-hall-snapshot.sh +28 -0
  767. package/bin/nornir-job-memory-housekeeping.sh +114 -0
  768. package/bin/nornir-job-nsr-compliance.sh +54 -0
  769. package/bin/nornir-job-observer.sh +166 -0
  770. package/bin/nornir-job-skillopt-sleep.sh +76 -0
  771. package/bin/npm-publish.sh +136 -0
  772. package/bin/omarchy-hook-install.sh +60 -0
  773. package/bin/omarchy-install.sh +161 -0
  774. package/bin/omarchy-plugins.sh +187 -0
  775. package/bin/omarchy-sense.sh +162 -0
  776. package/bin/perm-guard.sh +30 -0
  777. package/bin/pi-agent.sh +68 -0
  778. package/bin/pi-ensure.sh +77 -0
  779. package/bin/pi-local.sh +37 -0
  780. package/bin/pi-model-agnostic.sh +71 -0
  781. package/bin/pi-seat.sh +63 -0
  782. package/bin/prereq-ensure.sh +166 -0
  783. package/bin/private-guard.sh +58 -0
  784. package/bin/project-git.sh +69 -0
  785. package/bin/public-guard.sh +71 -0
  786. package/bin/ratatoskr.sh +49 -0
  787. package/bin/realm-lib.sh +32 -0
  788. package/bin/repo-scrub.sh +62 -0
  789. package/bin/rodd-operational-input.sh +250 -0
  790. package/bin/runes-append.sh +198 -0
  791. package/bin/saga-session-start.sh +172 -0
  792. package/bin/saga-sessionstart-run.sh +79 -0
  793. package/bin/saga-wake-drain.sh +41 -0
  794. package/bin/secret-guard.sh +63 -0
  795. package/bin/sessrumnir-ensure.sh +100 -0
  796. package/bin/sessrumnir-sync.sh +237 -0
  797. package/bin/sessrumnir.sh +104 -0
  798. package/bin/skillopt-setup.sh +76 -0
  799. package/bin/skuld-branch-outcome.sh +203 -0
  800. package/bin/skuld-branch-prompt.sh +101 -0
  801. package/bin/smidja-bootstrap.sh +94 -0
  802. package/bin/smidja-observe.sh +86 -0
  803. package/bin/syn-arm-pretool-check.sh +57 -0
  804. package/bin/syn-asset-pretool-check.sh +89 -0
  805. package/bin/syn-cd-pretool-check.sh +28 -0
  806. package/bin/syn-guard-pretool-check.sh +83 -0
  807. package/bin/syn-spawn-pretool-check.sh +58 -0
  808. package/bin/syn-turnend-guard.sh +36 -0
  809. package/bin/syn-watch-arm.sh +99 -0
  810. package/bin/tailscale-sync.sh +203 -0
  811. package/bin/telegram-bot.sh +82 -0
  812. package/bin/toolchain.sh +74 -0
  813. package/bin/ui-truth-check.sh +75 -0
  814. package/bin/utgard.sh +111 -0
  815. package/bin/valhalla.sh +115 -0
  816. package/bin/valknut-load.sh +298 -0
  817. package/bin/vor-crew-state.sh +124 -0
  818. package/bin/wedge-notify.sh +60 -0
  819. package/bin/workspace-provision.sh +80 -0
  820. package/bin/workspace-rag.sh +163 -0
  821. package/bin/wyrd-db.sh +47 -0
  822. package/bin/yggdrasil.sh +152 -0
  823. package/bin/ymir-edit.sh +145 -0
  824. package/bin/ymir-install.sh +806 -0
  825. package/bin/ymir-invite.sh +93 -0
  826. package/bin/ymir-isolation.sh +88 -0
  827. package/bin/ymir-migrate.sh +61 -0
  828. package/bin/ymir-plan.sh +356 -0
  829. package/bin/ymir-platform.sh +348 -0
  830. package/bin/ymir-say.sh +110 -0
  831. package/bin/ymir-setup-auth.sh +119 -0
  832. package/bin/ymir-validate.sh +193 -0
  833. package/bin/ymir.js +74 -16
  834. package/docs/Architecture.md +339 -0
  835. package/docs/design.md +351 -0
  836. package/docs/installations/README.md +63 -0
  837. package/docs/installations/ymir-home.md +93 -0
  838. package/docs/integration.md +105 -0
  839. package/docs/lore.md +1060 -0
  840. package/docs/research/agent-harnesses.md +290 -0
  841. package/docs/runbooks/README.md +19 -0
  842. package/docs/runbooks/a2a.md +91 -0
  843. package/docs/runbooks/agent-permissions.md +99 -0
  844. package/docs/runbooks/agents.md +57 -0
  845. package/docs/runbooks/models.md +69 -0
  846. package/docs/runbooks/realm-onboarding.md +60 -0
  847. package/docs/runbooks/secrets-and-hoard.md +60 -0
  848. package/docs/runbooks/updates-and-migrations.md +110 -0
  849. package/docs/runbooks/worktrees-isolation.md +72 -0
  850. package/docs/session-start.md +105 -0
  851. package/docs/skillopt-integration.md +321 -0
  852. package/docs/sync-system.md +66 -0
  853. package/docs/workspaces.md +104 -0
  854. package/docs/ymir-rut.md +260 -0
  855. package/package.json +54 -14
  856. package/packaging/build.sh +74 -0
  857. package/packaging/macos/Ymir Installer.command +43 -0
  858. package/packaging/windows/ymir-setup.nsi +74 -0
  859. package/scripts/README.md +22 -0
  860. package/scripts/electron.sh +269 -0
  861. package/scripts/lower.sh +34 -0
  862. package/scripts/raise.sh +42 -0
  863. package/scripts/start.sh +217 -0
  864. package/scripts/stop.sh +113 -0
  865. package/install.sh +0 -35
@@ -0,0 +1,370 @@
1
+ #!/usr/bin/env bash
2
+ # Behavioral coverage for the internal /stow cascade enumerator: per-home budget
3
+ # accounting that never sums a fleet total, one stanza per registered home,
4
+ # local versus remote transport routing, the facts a per-home completion receipt
5
+ # is built from, and the bound that keeps one slow home from blocking the sweep.
6
+ set -u
7
+
8
+ # shellcheck source=tests/lib.sh
9
+ . "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
10
+
11
+ ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
12
+ CASCADE="$ROOT/bin/fm-stow-cascade.sh"
13
+ TMP_ROOT=$(fm_test_tmproot fm-stow-cascade)
14
+ mkdir -p "$TMP_ROOT"
15
+ TMP_ROOT=$(cd "$TMP_ROOT" && pwd -P)
16
+ FAKEBIN=$(fm_fakebin "$TMP_ROOT/fakebin")
17
+ BASE_PATH=${FM_TEST_BASE_PATH:-/usr/bin:/bin:/usr/sbin:/sbin}
18
+
19
+ # A fake ssh standing in for the whole remote transport. It decodes fm-on.sh's
20
+ # NUL argv stream so each remote leg can answer as its own command, and its mode
21
+ # selects a reachable host, an unreachable one, or one that never answers.
22
+ cat > "$FAKEBIN/fake-ssh" <<'SH'
23
+ #!/usr/bin/env bash
24
+ set -u
25
+ while [ "$#" -gt 0 ]; do
26
+ case "$1" in
27
+ -o) shift 2 ;;
28
+ --) shift; break ;;
29
+ *) exit 90 ;;
30
+ esac
31
+ done
32
+ # After --: host, entrypoint, protocol, root, home, argv.
33
+ [ "$#" -eq 6 ] || exit 92
34
+ argv=$6
35
+ # tr runs inside the pipeline because command substitution drops NUL bytes.
36
+ command=$(printf '%s' "$argv" | base64 --decode 2>/dev/null | tr '\000' '\n' | head -1)
37
+ case "${FM_FAKE_SSH_MODE:-normal}" in
38
+ unreachable) exit 255 ;;
39
+ hang) sleep 45; exit 0 ;;
40
+ esac
41
+ case "$command" in
42
+ fm-startup-memory-budget.sh)
43
+ cat "$FM_FAKE_REMOTE_BUDGET"
44
+ ;;
45
+ fm-remote-secondmate-control.sh)
46
+ printf '%s\n' "${FM_FAKE_REMOTE_AGENT_STATE:-alive}"
47
+ ;;
48
+ *) exit 91 ;;
49
+ esac
50
+ SH
51
+ chmod +x "$FAKEBIN/fake-ssh"
52
+
53
+ # A tmux whose pane reports a running agent, so the local endpoint probe has a
54
+ # real backend read to classify rather than a stubbed verdict.
55
+ cat > "$FAKEBIN/tmux" <<'SH'
56
+ #!/usr/bin/env bash
57
+ set -u
58
+ case "$*" in
59
+ *list-windows*) printf '%s\n' "${FM_FAKE_TMUX_WINDOW:-}" ;;
60
+ *list-panes*) printf '%s\n' "${FM_FAKE_TMUX_PANE:-}" ;;
61
+ *display-message*'#{pane_current_command}'*) printf '%s\n' "${FM_FAKE_TMUX_COMMAND:-claude}" ;;
62
+ *display-message*'#{pane_pid}'*) printf '%s\n' "$$" ;;
63
+ *display-message*'#{pane_id}'*) printf '%s\n' '%1' ;;
64
+ *display-message*'#{cursor_y}'*) printf '%s\n' 0 ;;
65
+ *capture-pane*) printf '❯\n' ;;
66
+ esac
67
+ exit 0
68
+ SH
69
+ chmod +x "$FAKEBIN/tmux"
70
+
71
+ # new_home <name> [budget] -> path to a seeded local secondmate home.
72
+ new_home() {
73
+ local name=$1 budget=${2:-10} home
74
+ home="$TMP_ROOT/homes/$name"
75
+ mkdir -p "$home/config" "$home/data" "$home/state" "$home/bin" "$home/projects"
76
+ printf '%s\n' "$name" > "$home/.fm-secondmate-home"
77
+ printf '%s\n' '# secondmate instructions' > "$home/AGENTS.md"
78
+ printf '%s\n' "$budget" > "$home/config/startup-memory-budget"
79
+ printf '%s\n' "$home"
80
+ }
81
+
82
+ new_primary() {
83
+ local name=$1 home
84
+ home="$TMP_ROOT/primaries/$name"
85
+ mkdir -p "$home/config" "$home/data" "$home/state"
86
+ printf '%s\n' "$home"
87
+ }
88
+
89
+ # Registry lines use the parser-owned suffix form for local and remote routes.
90
+ local_record() { # <id> <home>
91
+ printf -- '- %s - domain summary (home: %s; scope: %s work; projects: alpha; added 2026-08-07)\n' \
92
+ "$1" "$2" "$1"
93
+ }
94
+ remote_record() { # <id> <host> <root> <home>
95
+ printf -- '- %s - domain summary (host: %s; root: %s; home: %s; scope: %s work; projects: alpha; added 2026-08-07)\n' \
96
+ "$1" "$2" "$3" "$4" "$1"
97
+ }
98
+
99
+ run_cascade() { # <primary-home> [env assignments...]
100
+ local home=$1
101
+ shift
102
+ env -i \
103
+ PATH="$FAKEBIN:$BASE_PATH" \
104
+ HOME="${HOME:-/tmp}" \
105
+ TMPDIR="${TMPDIR:-/tmp}" \
106
+ FM_HOME="$home" \
107
+ FM_SSH_BIN="$FAKEBIN/fake-ssh" \
108
+ "$@" \
109
+ "$CASCADE"
110
+ }
111
+
112
+ # stanza <output> <id>: the block of key=value lines for one secondmate.
113
+ stanza() {
114
+ printf '%s\n' "$1" | awk -v id="secondmate=$2" '
115
+ $0 == id { inside = 1 }
116
+ inside && $0 == "" { inside = 0 }
117
+ inside { print }
118
+ '
119
+ }
120
+
121
+ value_in() { # <stanza> <key>
122
+ printf '%s\n' "$1" | sed -n "s/^$2=//p" | head -1
123
+ }
124
+
125
+ test_budget_is_enforced_per_home_and_never_summed() {
126
+ local primary a b out sa sb
127
+ primary=$(new_primary per-home)
128
+ a=$(new_home over-budget 10)
129
+ b=$(new_home within-budget 10)
130
+ # Home A alone exceeds its own 10-token allowance.
131
+ printf '%s\n' 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' > "$a/data/captain.md"
132
+ # Home B is comfortably inside the same allowance, and would only look
133
+ # over-budget if the cascade added another home's total to its own.
134
+ printf '%s\n' 'bbbbbb' > "$b/data/captain.md"
135
+ {
136
+ local_record over-budget "$a"
137
+ local_record within-budget "$b"
138
+ } > "$primary/data/secondmates.md"
139
+
140
+ set +e
141
+ out=$(run_cascade "$primary")
142
+ set -e
143
+ sa=$(stanza "$out" over-budget)
144
+ sb=$(stanza "$out" within-budget)
145
+
146
+ [ "$(value_in "$sa" effective_budget_tokens)" = 10 ] \
147
+ || fail "over-budget home did not report its own allowance"
148
+ [ "$(value_in "$sb" effective_budget_tokens)" = 10 ] \
149
+ || fail "within-budget home did not report its own allowance"
150
+ [ "$(value_in "$sa" total_estimated_tokens)" = 13 ] \
151
+ || fail "over-budget home total was not its own files: $(value_in "$sa" total_estimated_tokens)"
152
+ [ "$(value_in "$sb" total_estimated_tokens)" = 3 ] \
153
+ || fail "within-budget home total was not its own files: $(value_in "$sb" total_estimated_tokens)"
154
+ [ "$(value_in "$sa" budget_status)" = over-budget ] \
155
+ || fail "the over-budget home was not classified against its own allowance"
156
+ [ "$(value_in "$sb" budget_status)" = within-budget ] \
157
+ || fail "a within-budget home was penalized for another home's memory"
158
+ [ "$(value_in "$sa" role)" = secondmate ] \
159
+ || fail "per-home accounting did not run in the secondmate home"
160
+ pass "each home is accounted against its own allowance instead of a fleet total"
161
+ }
162
+
163
+ test_every_registered_home_is_enumerated_exactly_once() {
164
+ local primary a b c out count dup rc
165
+ primary=$(new_primary enumerate)
166
+ a=$(new_home enum-a)
167
+ b=$(new_home enum-b)
168
+ c=$(new_home enum-c)
169
+ {
170
+ local_record enum-a "$a"
171
+ local_record enum-b "$b"
172
+ local_record enum-c "$c"
173
+ } > "$primary/data/secondmates.md"
174
+ # Two live endpoint records naming the same home must not multiply its stanza:
175
+ # the registry, not the endpoint inventory, is the enumeration source.
176
+ fm_write_secondmate_meta "$primary/state/enum-a.meta" "$a"
177
+ fm_write_secondmate_meta "$primary/state/enum-a-stale.meta" "$a"
178
+
179
+ set +e
180
+ out=$(run_cascade "$primary")
181
+ set -e
182
+ count=$(printf '%s\n' "$out" | grep -c '^secondmate=')
183
+ [ "$count" -eq 3 ] || fail "expected one stanza per registered home, got $count"
184
+ [ "$(printf '%s\n' "$out" | grep -c '^secondmate=enum-a$')" -eq 1 ] \
185
+ || fail "a home with two endpoint records was enumerated more than once"
186
+ [ "$(printf '%s\n' "$out" | sed -n 's/^secondmates=//p')" = 3 ] \
187
+ || fail "the cascade total did not match the registered homes"
188
+
189
+ dup=$(new_primary enumerate-dup)
190
+ {
191
+ local_record enum-a "$a"
192
+ local_record enum-other "$a"
193
+ } > "$dup/data/secondmates.md"
194
+ set +e
195
+ out=$(run_cascade "$dup" 2>&1)
196
+ rc=$?
197
+ set -e
198
+ expect_code 1 "$rc" "a registry that double-counts a home should refuse the cascade"
199
+ assert_contains "$out" 'duplicate secondmate home assignment' \
200
+ "the double-counted home was not named"
201
+ pass "the cascade emits one stanza per registered home and refuses a double-counted registry"
202
+ }
203
+
204
+ test_transport_routes_by_placement_and_liveness() {
205
+ local primary live idle remote out s
206
+ primary=$(new_primary transport)
207
+ live=$(new_home live-local)
208
+ idle=$(new_home idle-local)
209
+ remote="$TMP_ROOT/homes/remote-home"
210
+ {
211
+ local_record live-local "$live"
212
+ local_record idle-local "$idle"
213
+ remote_record remote-live remote-mac "$TMP_ROOT/remote-root" "$remote"
214
+ } > "$primary/data/secondmates.md"
215
+ fm_write_secondmate_meta "$primary/state/live-local.meta" "$live" 'firstmate:fm-live-local' alpha claude
216
+ fm_write_secondmate_meta "$primary/state/remote-live.meta" "$remote" 'fm-remote:fm-remote-live' alpha claude
217
+ printf 'role=secondmate\neffective_budget_tokens=7500\ntotal_estimated_tokens=100\nbudget_status=within-budget\n' \
218
+ > "$TMP_ROOT/remote-budget.txt"
219
+
220
+ set +e
221
+ out=$(run_cascade "$primary" \
222
+ FM_FAKE_TMUX_WINDOW='fm-live-local' \
223
+ FM_FAKE_REMOTE_BUDGET="$TMP_ROOT/remote-budget.txt" \
224
+ FM_FAKE_REMOTE_AGENT_STATE=alive)
225
+ set -e
226
+ [ "$(value_in "$(stanza "$out" live-local)" transport)" = agent ] \
227
+ || fail "a local home with a live agent was not routed to that agent"
228
+ [ "$(value_in "$(stanza "$out" idle-local)" placement)" = local ] \
229
+ || fail "a local home was not reported as local"
230
+ [ "$(value_in "$(stanza "$out" idle-local)" transport)" = direct ] \
231
+ || fail "a local home with no live agent was not routed to direct curation"
232
+ s=$(stanza "$out" remote-live)
233
+ [ "$(value_in "$s" placement)" = remote ] || fail "a remote home was not reported as remote"
234
+ [ "$(value_in "$s" host)" = remote-mac ] || fail "a remote home did not name its host"
235
+ [ "$(value_in "$s" transport)" = agent ] \
236
+ || fail "a remote home with a live agent was not routed to that agent"
237
+ [ "$(value_in "$s" total_estimated_tokens)" = 100 ] \
238
+ || fail "the remote home's own accounting was not reported"
239
+
240
+ set +e
241
+ out=$(run_cascade "$primary" \
242
+ FM_FAKE_TMUX_WINDOW='fm-live-local' \
243
+ FM_FAKE_REMOTE_BUDGET="$TMP_ROOT/remote-budget.txt" \
244
+ FM_FAKE_REMOTE_AGENT_STATE=dead)
245
+ set -e
246
+ s=$(stanza "$out" remote-live)
247
+ [ "$(value_in "$s" transport)" = deferred ] \
248
+ || fail "a remote home with no live agent was not deferred: $(value_in "$s" transport)"
249
+ assert_contains "$s" 'no remote memory write path' \
250
+ "the deferred remote home did not state why it cannot be curated in place"
251
+ pass "transport follows placement and live-agent state, and a remote home without an agent defers"
252
+ }
253
+
254
+ test_receipt_facts_are_complete_and_show_before_and_after() {
255
+ local primary home before after s
256
+ primary=$(new_primary receipt)
257
+ home=$(new_home receipt-home 20)
258
+ printf '%s\n' 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' > "$home/data/captain.md"
259
+ printf '%s\n' 'bbbbbb' > "$home/data/learnings.md"
260
+ local_record receipt-home "$home" > "$primary/data/secondmates.md"
261
+
262
+ set +e
263
+ before=$(run_cascade "$primary")
264
+ set -e
265
+ s=$(stanza "$before" receipt-home)
266
+ [ "$(value_in "$s" placement)" = local ] || fail "receipt stanza lacks placement"
267
+ [ "$(value_in "$s" home)" = "$home" ] || fail "receipt stanza lacks the home it accounted"
268
+ [ "$(value_in "$s" budget_report)" = ok ] || fail "receipt stanza lacks an accounting outcome"
269
+ [ -n "$(value_in "$s" transport)" ] || fail "receipt stanza lacks a transport"
270
+ local file
271
+ for file in captain.md captain-shared.md learnings.md; do
272
+ assert_contains "$s" "file=data/$file " "receipt stanza lacks a per-file action input for $file"
273
+ done
274
+ [ "$(value_in "$s" budget_status)" = over-budget ] \
275
+ || fail "the over-budget home was not surfaced before curation"
276
+
277
+ # Curation shrinks this home's editable memory; the same command is the
278
+ # after-pass, so before and after totals come from one accounting contract.
279
+ printf '%s\n' 'aaa' > "$home/data/captain.md"
280
+ set +e
281
+ after=$(run_cascade "$primary")
282
+ set -e
283
+ s=$(stanza "$after" receipt-home)
284
+ [ "$(value_in "$s" budget_status)" = within-budget ] \
285
+ || fail "the after pass did not reflect this home's curation"
286
+ [ "$(value_in "$(stanza "$before" receipt-home)" total_estimated_tokens)" \
287
+ -gt "$(value_in "$s" total_estimated_tokens)" ] \
288
+ || fail "the after total did not drop below the before total"
289
+ pass "each stanza carries the facts a per-home receipt needs, before and after curation"
290
+ }
291
+
292
+ test_a_slow_remote_is_bounded_and_the_rest_still_report() {
293
+ local primary home remote out rc started elapsed s
294
+ primary=$(new_primary bounded)
295
+ home=$(new_home bounded-local 10)
296
+ printf '%s\n' 'cccccc' > "$home/data/captain.md"
297
+ remote="$TMP_ROOT/homes/bounded-remote"
298
+ {
299
+ remote_record bounded-remote remote-mac "$TMP_ROOT/remote-root" "$remote"
300
+ local_record bounded-local "$home"
301
+ } > "$primary/data/secondmates.md"
302
+ fm_write_secondmate_meta "$primary/state/bounded-remote.meta" "$remote" 'fm-remote:fm-bounded-remote'
303
+ printf 'role=secondmate\n' > "$TMP_ROOT/remote-budget.txt"
304
+
305
+ started=$(date +%s)
306
+ set +e
307
+ out=$(run_cascade "$primary" \
308
+ FM_STOW_CASCADE_TIMEOUT=2 \
309
+ FM_FAKE_SSH_MODE=hang \
310
+ FM_FAKE_REMOTE_BUDGET="$TMP_ROOT/remote-budget.txt")
311
+ rc=$?
312
+ set -e
313
+ elapsed=$(( $(date +%s) - started ))
314
+ [ "$elapsed" -lt 30 ] || fail "the sweep waited $elapsed s on a host that never answered"
315
+ s=$(stanza "$out" bounded-remote)
316
+ [ "$(value_in "$s" budget_report)" = timeout ] \
317
+ || fail "an unanswered home did not report a bounded exception"
318
+ [ "$(value_in "$s" transport)" = unavailable ] \
319
+ || fail "an unanswered home claimed a transport conclusion"
320
+ s=$(stanza "$out" bounded-local)
321
+ [ "$(value_in "$s" budget_report)" = ok ] \
322
+ || fail "the healthy home was blocked by the unanswered one"
323
+ [ "$(value_in "$s" total_estimated_tokens)" = 3 ] \
324
+ || fail "the healthy home's own accounting was lost"
325
+ expect_code 3 "$rc" "a reported per-home exception should be distinguishable from a clean sweep"
326
+ assert_contains "$out" 'exceptions=1' "the sweep did not count the unreachable home"
327
+
328
+ set +e
329
+ out=$(run_cascade "$primary" \
330
+ FM_FAKE_SSH_MODE=unreachable \
331
+ FM_FAKE_REMOTE_BUDGET="$TMP_ROOT/remote-budget.txt")
332
+ rc=$?
333
+ set -e
334
+ [ "$(value_in "$(stanza "$out" bounded-remote)" budget_report)" = error ] \
335
+ || fail "an unreachable host was not reported as a per-home exception"
336
+ [ "$(value_in "$(stanza "$out" bounded-local)" budget_report)" = ok ] \
337
+ || fail "an unreachable host blocked the healthy home"
338
+ expect_code 3 "$rc" "an unreachable host should still finish the sweep"
339
+ pass "one slow or unreachable home is bounded and every other home still reports"
340
+ }
341
+
342
+ test_no_cascade_without_secondmates_or_from_a_secondmate_home() {
343
+ local primary secondmate out rc
344
+ primary=$(new_primary quiet)
345
+ set +e
346
+ out=$(run_cascade "$primary")
347
+ rc=$?
348
+ set -e
349
+ expect_code 0 "$rc" "a home with no registered secondmates should be a clean no-op"
350
+ assert_contains "$out" 'secondmates=0' "an empty cascade did not say so"
351
+ assert_not_contains "$out" 'secondmate=' "an empty cascade invented a home"
352
+
353
+ secondmate=$(new_home quiet-secondmate)
354
+ local_record quiet-secondmate "$secondmate" > "$secondmate/data/secondmates.md"
355
+ set +e
356
+ out=$(run_cascade "$secondmate")
357
+ rc=$?
358
+ set -e
359
+ expect_code 0 "$rc" "a secondmate home should not fail its own stow"
360
+ assert_contains "$out" 'role=secondmate' "a secondmate home was not recognized"
361
+ assert_contains "$out" 'secondmates=0' "a secondmate home cascaded to its own registry"
362
+ pass "the cascade stays silent with no secondmates and never runs from a secondmate home"
363
+ }
364
+
365
+ test_budget_is_enforced_per_home_and_never_summed
366
+ test_every_registered_home_is_enumerated_exactly_once
367
+ test_transport_routes_by_placement_and_liveness
368
+ test_receipt_facts_are_complete_and_show_before_and_after
369
+ test_a_slow_remote_is_bounded_and_the_rest_still_report
370
+ test_no_cascade_without_secondmates_or_from_a_secondmate_home
@@ -0,0 +1,291 @@
1
+ #!/usr/bin/env bash
2
+ # Behavior tests for the primary-session delegation-shape guard: the tracked
3
+ # hook registration, shared settings boundary, and PreToolUse classifier.
4
+ set -u
5
+
6
+ # shellcheck source=tests/lib.sh
7
+ . "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
8
+
9
+ CHECK="$ROOT/bin/fm-subagent-pretool-check.sh"
10
+ TMP_ROOT=$(fm_test_tmproot fm-subagent-pretool-tests)
11
+ PRIMARY="$TMP_ROOT/primary"
12
+ STATE="$PRIMARY/state"
13
+ OUT="$TMP_ROOT/out"
14
+ ERR="$TMP_ROOT/err"
15
+
16
+ mkdir -p "$PRIMARY/bin" "$STATE"
17
+ printf '# fixture\n' > "$PRIMARY/AGENTS.md"
18
+ git -C "$PRIMARY" init -q
19
+
20
+ BRIEF_ONLY_ROUTE='first classify the work under the AGENTS.md intake contract, then use bin/fm-brief.sh followed by bin/fm-spawn.sh for dispatched work'
21
+ SCOUT_ROUTE='first classify the work under the AGENTS.md intake contract: work already classified as a scout goes to bin/fm-scout.sh "<question>" [project], while authorized ship work and its bounded research go to bin/fm-brief.sh then bin/fm-spawn.sh'
22
+
23
+ # Every delegation, scheduling, worktree, and task-tracking tool Claude Code
24
+ # 2.1.217 offered a primary session in the observed baseline.
25
+ # This inventory is shape-classification coverage for the shipped guard and the
26
+ # recommended local Claude deny-list hardening list, but tracked settings must
27
+ # not ship that Claude-only permissions layer.
28
+ DELEGATION_TOOLS='Task Agent Workflow RemoteTrigger Monitor ScheduleWakeup SendMessage EnterWorktree ExitWorktree CronCreate CronDelete CronList TaskCreate TaskGet TaskList TaskUpdate TaskStop TaskOutput'
29
+
30
+ # Tools that must stay available: denying these would break ordinary work.
31
+ PRESERVED_TOOLS='Bash Edit Read Write Skill ToolSearch WebFetch WebSearch NotebookEdit ReportFindings DesignSync PushNotification'
32
+
33
+ # Session-local todo-list tools. They match a delegation stem but create no
34
+ # runnable work, so the guard's plan-only exclusion must allow them.
35
+ PLAN_ONLY_TOOLS='TaskCreate TaskUpdate'
36
+
37
+ # Names the plan-only exclusion must NOT release. Five of them contain a
38
+ # plan-only name as a substring and would be let through by a substring rather
39
+ # than exact-name match; bare Task is what a shortened entry of "task" would
40
+ # release. Together they make the exact-name contract testable instead of
41
+ # assumed.
42
+ PLAN_ONLY_NEAR_MISSES='TaskCreateAgent TaskCreateWorktree TaskUpdateAgent RemoteTaskCreate Task TaskCreator'
43
+
44
+ run_tool() {
45
+ local tool=$1 rc=0
46
+ shift
47
+ : > "$OUT"
48
+ : > "$ERR"
49
+ env FM_ROOT_OVERRIDE="$PRIMARY" FM_HOME="$PRIMARY" FM_STATE_OVERRIDE="$STATE" "$@" \
50
+ "$CHECK" --claude --tool "$tool" > "$OUT" 2> "$ERR" || rc=$?
51
+ return "$rc"
52
+ }
53
+
54
+ expect_allow() {
55
+ local label=$1 tool=$2 rc=0
56
+ shift 2
57
+ run_tool "$tool" "$@" || rc=$?
58
+ [ "$rc" -eq 0 ] || fail "$label ($tool) must allow, got exit $rc: $(cat "$ERR")"
59
+ [ ! -s "$OUT" ] || fail "$label ($tool) allow wrote stdout: $(cat "$OUT")"
60
+ [ ! -s "$ERR" ] || fail "$label ($tool) allow wrote stderr: $(cat "$ERR")"
61
+ }
62
+
63
+ expect_deny() {
64
+ local label=$1 tool=$2 rc=0
65
+ run_tool "$tool" || rc=$?
66
+ [ "$rc" -eq 2 ] || fail "$label ($tool) must deny with exit 2, got $rc"
67
+ [ ! -s "$OUT" ] || fail "$label ($tool) deny wrote stdout: $(cat "$OUT")"
68
+ jq -e '.hookSpecificOutput.hookEventName == "PreToolUse" and .hookSpecificOutput.permissionDecision == "deny"' "$ERR" >/dev/null 2>&1 \
69
+ || fail "$label ($tool) deny omitted Claude's permission decision: $(cat "$ERR")"
70
+ jq -e --arg tool "$tool" '.systemMessage | startswith("[subagent-dispatch]") and contains("blocked tool: " + $tool)' "$ERR" >/dev/null 2>&1 \
71
+ || fail "$label ($tool) deny message lost its code or tool name: $(jq -r '.systemMessage' "$ERR")"
72
+ }
73
+
74
+ # ---------------------------------------------------------------------------
75
+ # Delegation-shape PreToolUse guard.
76
+ # ---------------------------------------------------------------------------
77
+
78
+ test_guard_denies_every_currently_known_delegation_tool() {
79
+ local tool
80
+ for tool in $DELEGATION_TOOLS; do
81
+ case "$tool" in
82
+ TaskOutput|TaskStop|TaskGet|TaskList|CronList) continue ;;
83
+ TaskCreate|TaskUpdate) continue ;;
84
+ esac
85
+ expect_deny "known delegation tool" "$tool"
86
+ done
87
+ pass "the guard independently denies every work-creating delegation tool by shape"
88
+ }
89
+
90
+ test_guard_denies_hypothetical_future_tools() {
91
+ # A fixed deny list is fail-open against tools that do not exist yet.
92
+ # None of these names is on any list.
93
+ local tool
94
+ for tool in SubagentCreate SpawnWorker DelegateTask AgentPool WorkflowRun \
95
+ ScheduleJob CronSchedule CreateWorktree DispatchAgent TaskHandoff \
96
+ RemoteExec BackgroundAgent; do
97
+ expect_deny "future delegation tool" "$tool"
98
+ done
99
+ pass "the guard denies delegation-shaped tools that no deny list knows about yet"
100
+ }
101
+
102
+ test_guard_allows_ordinary_and_observe_only_tools() {
103
+ local tool
104
+ for tool in $PRESERVED_TOOLS; do
105
+ expect_allow "ordinary tool" "$tool"
106
+ done
107
+ # Observing or stopping work that already exists is not creating unaccounted
108
+ # work, and blocking it would strand a runaway task with no way to end it.
109
+ for tool in TaskOutput TaskStop TaskGet TaskList CronList BashOutput KillShell; do
110
+ expect_allow "observe-or-stop tool" "$tool"
111
+ done
112
+ pass "the guard leaves ordinary tools and observe-or-stop operations alone"
113
+ }
114
+
115
+ test_guard_allows_session_local_todo_tools() {
116
+ # These write, so they are not observe-or-stop, but what they write is the
117
+ # harness's session-local todo list: no executor, no agent, no worktree, no
118
+ # schedule, nothing that outlives the session. Denying them stops the primary
119
+ # tracking its own plan and grants no delegation power in exchange.
120
+ local tool
121
+ for tool in $PLAN_ONLY_TOOLS; do
122
+ expect_allow "session-local todo tool" "$tool"
123
+ done
124
+ pass "the guard leaves the session-local todo list alone"
125
+ }
126
+
127
+ test_plan_only_exclusion_is_exact_name() {
128
+ # The plan-only exclusion must never widen by substring or by a shorter stem.
129
+ # Every name here would be released by such a widening and must stay denied.
130
+ local tool
131
+ for tool in $PLAN_ONLY_NEAR_MISSES; do
132
+ expect_deny "plan-only near miss" "$tool"
133
+ done
134
+ pass "the plan-only exclusion releases exactly two names and nothing that merely contains them"
135
+ }
136
+
137
+ test_guard_never_classifies_mcp_tools() {
138
+ # An MCP server names its own tools; a task or agent noun there is common and
139
+ # has nothing to do with fleet dispatch.
140
+ local tool
141
+ for tool in mcp__linear__list_issues mcp__tracker__create_task \
142
+ mcp__acme__spawn_agent mcp__slack__slack_send_message; do
143
+ expect_allow "MCP tool" "$tool"
144
+ done
145
+ pass "MCP tool names are never classified as harness delegation"
146
+ }
147
+
148
+ test_deny_message_defers_to_intake_classification() {
149
+ local actual
150
+ printf '#!/usr/bin/env bash\n' > "$PRIMARY/bin/fm-scout.sh"
151
+ run_tool Agent && fail "scout-present case must still deny"
152
+ actual=$(jq -r '.systemMessage' "$ERR")
153
+ case "$actual" in
154
+ *"$SCOUT_ROUTE"*) ;;
155
+ *) fail "deny must reserve bin/fm-scout.sh for classified scout work: $actual" ;;
156
+ esac
157
+ case "$actual" in
158
+ *'investigation or diagnosis goes to bin/fm-scout.sh'*) fail "deny must not classify all investigation or diagnosis as scout work: $actual" ;;
159
+ esac
160
+ rm -f "$PRIMARY/bin/fm-scout.sh"
161
+ run_tool Agent && fail "scout-absent case must still deny"
162
+ actual=$(jq -r '.systemMessage' "$ERR")
163
+ case "$actual" in
164
+ *"$BRIEF_ONLY_ROUTE"*) ;;
165
+ *) fail "deny must degrade to brief-then-spawn when fm-scout.sh is absent: $actual" ;;
166
+ esac
167
+ pass "deny defers to intake classification and degrades gracefully without fm-scout.sh"
168
+ }
169
+
170
+ test_escape_hatch_allows_deliberate_use() {
171
+ local rc value
172
+ expect_allow "escape hatch set" Agent FM_ALLOW_SUBAGENT=1
173
+ expect_deny "escape hatch unset" Agent
174
+ for value in '' 0 yes true 11; do
175
+ rc=0
176
+ run_tool Agent "FM_ALLOW_SUBAGENT=$value" || rc=$?
177
+ [ "$rc" -eq 2 ] || fail "FM_ALLOW_SUBAGENT='$value' must not release the guard, got exit $rc"
178
+ done
179
+ pass "the single documented escape hatch releases the guard only on the exact opt-in value"
180
+ }
181
+
182
+ test_task_worktree_and_non_firstmate_repo_are_inert() {
183
+ local child="$TMP_ROOT/child" plain="$TMP_ROOT/plain" rc=0
184
+ git -C "$PRIMARY" config user.name fixture
185
+ git -C "$PRIMARY" config user.email fixture@example.test
186
+ git -C "$PRIMARY" add AGENTS.md
187
+ git -C "$PRIMARY" commit -qm fixture
188
+ git -C "$PRIMARY" worktree add -q -b fixture-child "$child"
189
+ mkdir -p "$child/bin" "$child/state"
190
+ printf '# fixture\n' > "$child/AGENTS.md"
191
+ : > "$OUT"
192
+ : > "$ERR"
193
+ FM_ROOT_OVERRIDE="$child" FM_HOME="$child" FM_STATE_OVERRIDE="$child/state" \
194
+ "$CHECK" --claude --tool Agent > "$OUT" 2> "$ERR" || rc=$?
195
+ [ "$rc" -eq 0 ] || fail "a crewmate task worktree must be out of scope, got exit $rc: $(cat "$ERR")"
196
+ [ ! -s "$OUT" ] || fail "task-worktree no-op wrote stdout: $(cat "$OUT")"
197
+ [ ! -s "$ERR" ] || fail "task-worktree no-op wrote stderr: $(cat "$ERR")"
198
+
199
+ mkdir -p "$plain/bin"
200
+ git -C "$plain" init -q
201
+ rc=0
202
+ FM_ROOT_OVERRIDE="$plain" FM_HOME="$plain" FM_STATE_OVERRIDE="$plain/state" \
203
+ "$CHECK" --claude --tool Agent > "$OUT" 2> "$ERR" || rc=$?
204
+ [ "$rc" -eq 0 ] || fail "a non-firstmate repo must be out of scope, got exit $rc"
205
+ pass "the guard is inert in a crewmate task worktree and in a non-firstmate repo"
206
+ }
207
+
208
+ test_secondmate_home_is_in_scope() {
209
+ local second="$TMP_ROOT/second" rc=0
210
+ git -C "$PRIMARY" worktree add -q -b fixture-second "$second"
211
+ mkdir -p "$second/bin" "$second/state"
212
+ printf '# fixture\n' > "$second/AGENTS.md"
213
+ printf 'sm-fixture\n' > "$second/.fm-secondmate-home"
214
+ FM_ROOT_OVERRIDE="$second" FM_HOME="$second" FM_STATE_OVERRIDE="$second/state" \
215
+ "$CHECK" --claude --tool Agent > "$OUT" 2> "$ERR" || rc=$?
216
+ [ "$rc" -eq 2 ] || fail "a marked secondmate home operates a fleet and must be guarded, got exit $rc"
217
+ pass "a marked secondmate home is guarded even though it is a linked worktree"
218
+ }
219
+
220
+ test_stdin_transports_and_output_shapes() {
221
+ local rc=0
222
+ : > "$OUT"; : > "$ERR"
223
+ printf '%s' '{"tool_name":"Agent","tool_input":{"prompt":"go"}}' \
224
+ | FM_ROOT_OVERRIDE="$PRIMARY" FM_HOME="$PRIMARY" FM_STATE_OVERRIDE="$STATE" \
225
+ "$CHECK" --claude > "$OUT" 2> "$ERR" || rc=$?
226
+ [ "$rc" -eq 2 ] || fail "Claude-shaped stdin must deny, got exit $rc"
227
+ [ ! -s "$OUT" ] || fail "Claude deny wrote stdout, which makes Claude ignore the deny: $(cat "$OUT")"
228
+
229
+ rc=0
230
+ : > "$OUT"; : > "$ERR"
231
+ printf '%s' '{"toolName":"Agent"}' \
232
+ | FM_ROOT_OVERRIDE="$PRIMARY" FM_HOME="$PRIMARY" FM_STATE_OVERRIDE="$STATE" \
233
+ "$CHECK" > "$OUT" 2> "$ERR" || rc=$?
234
+ [ "$rc" -eq 2 ] || fail "Grok-shaped stdin must deny, got exit $rc"
235
+ jq -e '.decision == "deny" and (.reason | startswith("[subagent-dispatch]"))' "$OUT" >/dev/null 2>&1 \
236
+ || fail "default deny mode must write a Grok decision object on stdout: $(cat "$OUT")"
237
+
238
+ rc=0
239
+ : > "$OUT"; : > "$ERR"
240
+ printf '%s' '{"tool_name":"Bash","tool_input":{"command":"ls"}}' \
241
+ | FM_ROOT_OVERRIDE="$PRIMARY" FM_HOME="$PRIMARY" FM_STATE_OVERRIDE="$STATE" \
242
+ "$CHECK" --claude > "$OUT" 2> "$ERR" || rc=$?
243
+ [ "$rc" -eq 0 ] || fail "Bash through stdin must allow, got exit $rc"
244
+ [ ! -s "$OUT" ] && [ ! -s "$ERR" ] || fail "stdin allow wrote output"
245
+ pass "both stdin transports classify correctly and Claude's deny keeps stdout empty"
246
+ }
247
+
248
+ test_malformed_transport_fails_open() {
249
+ local rc payload
250
+ for payload in '{not-json' '' '{}' '{"tool_name":null}'; do
251
+ rc=0
252
+ : > "$OUT"; : > "$ERR"
253
+ printf '%s' "$payload" \
254
+ | FM_ROOT_OVERRIDE="$PRIMARY" FM_HOME="$PRIMARY" FM_STATE_OVERRIDE="$STATE" \
255
+ "$CHECK" --claude > "$OUT" 2> "$ERR" || rc=$?
256
+ [ "$rc" -eq 0 ] || fail "malformed transport must fail open, payload '$payload' gave exit $rc"
257
+ [ ! -s "$OUT" ] || fail "fail-open path wrote stdout for payload '$payload'"
258
+ done
259
+ pass "malformed, empty, and tool-name-less payloads fail open rather than blocking every tool call"
260
+ }
261
+
262
+ test_missing_jq_stdin_transport_fails_open() {
263
+ local fakebin="$TMP_ROOT/no-jq-bin" bash_bin cat_bin rc=0
264
+ bash_bin=$(command -v bash) || fail "test needs bash to simulate the hook shebang"
265
+ cat_bin=$(command -v cat) || fail "test needs cat to feed stdin without jq"
266
+ mkdir -p "$fakebin"
267
+ ln -sf "$bash_bin" "$fakebin/bash"
268
+ ln -sf "$cat_bin" "$fakebin/cat"
269
+ : > "$OUT"; : > "$ERR"
270
+ printf '%s' '{"tool_name":"Agent"}' \
271
+ | env PATH="$fakebin" FM_ROOT_OVERRIDE="$PRIMARY" FM_HOME="$PRIMARY" FM_STATE_OVERRIDE="$STATE" \
272
+ "$CHECK" --claude > "$OUT" 2> "$ERR" || rc=$?
273
+ [ "$rc" -eq 0 ] || fail "missing jq transport must fail open, got exit $rc: $(cat "$ERR")"
274
+ [ ! -s "$OUT" ] || fail "missing jq fail-open path wrote stdout: $(cat "$OUT")"
275
+ [ ! -s "$ERR" ] || fail "missing jq fail-open path wrote stderr: $(cat "$ERR")"
276
+ pass "missing jq for stdin transport fails open rather than denying every tool call"
277
+ }
278
+
279
+ test_guard_denies_every_currently_known_delegation_tool
280
+ test_guard_denies_hypothetical_future_tools
281
+ test_guard_allows_ordinary_and_observe_only_tools
282
+ test_guard_allows_session_local_todo_tools
283
+ test_plan_only_exclusion_is_exact_name
284
+ test_guard_never_classifies_mcp_tools
285
+ test_deny_message_defers_to_intake_classification
286
+ test_escape_hatch_allows_deliberate_use
287
+ test_task_worktree_and_non_firstmate_repo_are_inert
288
+ test_secondmate_home_is_in_scope
289
+ test_stdin_transports_and_output_shapes
290
+ test_malformed_transport_fails_open
291
+ test_missing_jq_stdin_transport_fails_open