@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,796 @@
1
+ #!/usr/bin/env bash
2
+ # fm-send remote-secondmate delivery: the remote inbox leg.
3
+ #
4
+ # A remote steer is delivered by durable record, never by typing its payload:
5
+ # fm-send crosses fm-on.sh to the host-local leg (fm-remote-secondmate-control.sh
6
+ # cmd_send), which writes the message idempotently into the remote home's
7
+ # steering inbox and rings the remote doorbell best-effort. These tests drive
8
+ # the real fm-send + fm-on executables with a stubbed ssh transport that
9
+ # EXECUTES the real host-local leg against a seeded remote-home fixture (the
10
+ # FM_SSH_BIN seam tests/fm-on.test.sh proves preserves exit status), and pin:
11
+ # 1. A remote text steer lands as a durable record in the remote home's
12
+ # steering inbox, marker and corr token in the body, exits 0, and marks
13
+ # the pending-reply expectation delivered at enqueue; a failed doorbell
14
+ # never fails the send.
15
+ # 2. Re-running the identical leg is idempotent: an ambiguous transport
16
+ # (executed remotely, then ssh exit 255) makes fm-send retry the same
17
+ # leg once, and the remote inbox holds exactly ONE record afterwards.
18
+ # 3. A remote --resolve-key answer closes its decision at enqueue.
19
+ # 4. A remote harness-native "/..." steer also rides the inbox: the deleted
20
+ # remote typed-payload plane is gone for every remote text.
21
+ # 5. A real remote failure still fails loudly with the remote leg's own
22
+ # stderr and discards the undelivered expectation.
23
+ # 6. The deleted exit-3-as-delivered remap is GONE: a nonzero remote leg
24
+ # status is a failure, never reported as delivered.
25
+ # 7. Pure transport loss (ssh 255 twice, nothing executed) fails with
26
+ # resend-safe guidance - the deleted "do not resend" trap is gone - and
27
+ # preserves the marked expectation for the record that may have landed.
28
+ # 8. A LOCAL typed-plane send (an explicit backend target or harness-native
29
+ # slash) keeps its exit-3 delivered-unconfirmed contract, never closes a
30
+ # --resolve-key decision unconfirmed, and keeps a marked expectation
31
+ # armed.
32
+ set -u
33
+
34
+ # shellcheck source=tests/lib.sh
35
+ . "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
36
+ # shellcheck source=bin/fm-pending-reply-lib.sh
37
+ . "$ROOT/bin/fm-pending-reply-lib.sh"
38
+ # shellcheck source=bin/fm-marker-lib.sh
39
+ . "$ROOT/bin/fm-marker-lib.sh"
40
+ # shellcheck source=bin/fm-task-inbox-lib.sh
41
+ . "$ROOT/bin/fm-task-inbox-lib.sh"
42
+
43
+ SEND="$ROOT/bin/fm-send.sh"
44
+ DRAIN="$ROOT/bin/fm-wake-drain.sh"
45
+
46
+ TMP_ROOT=$(fm_test_tmproot fm-send-remote-delivery)
47
+ TMP_ROOT=$(cd "$TMP_ROOT" && pwd)
48
+
49
+ # Stub tmux for the local typed-plane legs: logs literal typed text to
50
+ # FM_SEND_LOG. The default composer reads empty (clean submit);
51
+ # FM_FAKE_TMUX_PENDING=1 keeps a proven pending composer with no busy footer,
52
+ # so the real submit core exhausts its Enter budget and reports the pending
53
+ # verdict. The ssh stub counts invocations, logs the wire line, and either
54
+ # fails with FM_FAKE_SSH_RC (emitting FM_FAKE_SSH_STDERR as the remote
55
+ # stderr), or decodes the entrypoint argv and executes the REAL host-local
56
+ # command against the decoded remote home - with FM_FAKE_SSH_AMBIGUOUS=1
57
+ # reporting ssh exit 255 after that execution (completion unknown, but the
58
+ # remote leg actually ran).
59
+ make_stubs() { # <dir> -> echoes fakebin dir
60
+ local dir=$1 fb="$1/fakebin"
61
+ mkdir -p "$fb"
62
+ cat > "$fb/tmux" <<'SH'
63
+ #!/usr/bin/env bash
64
+ set -u
65
+ case "${1:-}" in
66
+ send-keys)
67
+ shift
68
+ literal=0
69
+ while [ $# -gt 0 ]; do
70
+ case "$1" in
71
+ -t) shift 2 ;;
72
+ -l) literal=1; shift ;;
73
+ *) break ;;
74
+ esac
75
+ done
76
+ if [ "$literal" = 1 ]; then
77
+ printf '%s' "${1:-}" >> "$FM_SEND_LOG"
78
+ fi
79
+ exit 0 ;;
80
+ display-message)
81
+ for a in "$@"; do case "$a" in *cursor_y*) printf '1\n'; exit 0 ;; esac; done
82
+ printf 'fakepane\n'; exit 0 ;;
83
+ capture-pane)
84
+ if [ "${FM_FAKE_TMUX_PENDING:-0}" = 1 ]; then
85
+ printf '╭────────────╮\n│ > steer │\n╰────────────╯\n'
86
+ else
87
+ printf '╭────╮\n│ │\n╰────╯\n'
88
+ fi
89
+ exit 0 ;;
90
+ list-windows) exit 0 ;;
91
+ esac
92
+ exit 0
93
+ SH
94
+ chmod +x "$fb/tmux"
95
+ cat > "$fb/sleep" <<'SH'
96
+ #!/usr/bin/env bash
97
+ exit 0
98
+ SH
99
+ chmod +x "$fb/sleep"
100
+ cat > "$fb/fake-ssh" <<'SH'
101
+ #!/usr/bin/env bash
102
+ set -u
103
+ cat > /dev/null
104
+ count=$(cat "$FM_SSH_COUNT" 2>/dev/null || echo 0)
105
+ count=$((count + 1))
106
+ printf '%s\n' "$count" > "$FM_SSH_COUNT"
107
+ printf '%s\n' "$*" >> "$FM_SSH_LOG"
108
+ if [ -n "${FM_FAKE_SSH_HANG:-}" ]; then
109
+ # A busy remote lane: the transport attempt never returns on its own. The
110
+ # real sleep, because the stubbed one on PATH returns immediately.
111
+ /bin/sleep "$FM_FAKE_SSH_HANG"
112
+ exit 255
113
+ fi
114
+ if [ "${FM_FAKE_SSH_AFTER_AMBIGUOUS_RC:-0}" -ne 0 ] && [ "$count" -gt 1 ]; then
115
+ exit "$FM_FAKE_SSH_AFTER_AMBIGUOUS_RC"
116
+ fi
117
+ if [ "${FM_FAKE_SSH_RC:-0}" -ne 0 ]; then
118
+ [ -z "${FM_FAKE_SSH_STDERR:-}" ] || printf '%s\n' "$FM_FAKE_SSH_STDERR" >&2
119
+ exit "${FM_FAKE_SSH_RC}"
120
+ fi
121
+ while [ "$#" -gt 0 ]; do
122
+ case "$1" in -o) shift 2 ;; --) shift; break ;; *) exit 90 ;; esac
123
+ done
124
+ shift 2 # host, fm-remote-entrypoint.sh
125
+ home_b64=$3
126
+ argv_b64=$4
127
+ remote_home=$(perl -MMIME::Base64=decode_base64 -e 'print decode_base64($ARGV[0])' "$home_b64")
128
+ rargs=()
129
+ while IFS= read -r -d '' a; do rargs+=("$a"); done \
130
+ < <(perl -MMIME::Base64=decode_base64 -e 'print decode_base64($ARGV[0])' "$argv_b64")
131
+ cmd=${rargs[0]}
132
+ rc=0
133
+ env FM_HOME="$remote_home" FM_ROOT_OVERRIDE="$FM_REMOTE_CODE_ROOT" \
134
+ "$FM_REMOTE_CODE_ROOT/bin/$cmd" "${rargs[@]:1}" || rc=$?
135
+ if [ "${FM_FAKE_SSH_AMBIGUOUS:-0}" = 1 ] \
136
+ || { [ "${FM_FAKE_SSH_AFTER_AMBIGUOUS_RC:-0}" -ne 0 ] && [ "$count" -eq 1 ]; }; then
137
+ exit 255
138
+ fi
139
+ exit "$rc"
140
+ SH
141
+ chmod +x "$fb/fake-ssh"
142
+ printf '%s\n' "$fb"
143
+ }
144
+
145
+ setup_home() { # <name> -> echoes a fresh home dir with an empty state/
146
+ local home="$TMP_ROOT/$1-$RANDOM"
147
+ mkdir -p "$home/state"
148
+ printf '%s\n' "$home"
149
+ }
150
+
151
+ # A seeded remote secondmate home the executed host-local leg validates and
152
+ # writes into: identity marker, Firstmate-checkout shape, and a parent-route
153
+ # endpoint record on Herdr in the dedicated fm-remote session.
154
+ setup_remote_secondmate_home() { # <name> -> echoes remote home dir
155
+ local rh="$TMP_ROOT/$1-rhome"
156
+ mkdir -p "$rh/state/parent-route" "$rh/bin"
157
+ printf 'rsm\n' > "$rh/.fm-secondmate-home"
158
+ printf '# remote secondmate home fixture\n' > "$rh/AGENTS.md"
159
+ fm_write_meta "$rh/state/parent-route/rsm.meta" \
160
+ "window=fm-remote:p1" \
161
+ "worktree=-" \
162
+ "project=-" \
163
+ "backend=herdr" \
164
+ "endpoint_task_id=rsm" \
165
+ "harness=claude" \
166
+ "herdr_session=fm-remote" \
167
+ "herdr_workspace_id=w1" \
168
+ "herdr_tab_id=t1" \
169
+ "herdr_pane_id=p1"
170
+ printf '%s\n' "$rh"
171
+ }
172
+
173
+ # A parent home with a remote-secondmate task meta plus the registry row
174
+ # fm-on.sh resolves the ssh route from, pointing at <remote-home>.
175
+ setup_remote_parent_home() { # <name> <remote-home> -> echoes home dir
176
+ local home
177
+ home=$(setup_home "$1")
178
+ mkdir -p "$home/data"
179
+ fm_write_meta "$home/state/rsm.meta" \
180
+ "window=fm-remote:p1" \
181
+ "endpoint_task_id=rsm" \
182
+ "harness=claude" \
183
+ "kind=secondmate" \
184
+ "mode=secondmate" \
185
+ "yolo=off" \
186
+ "remote_host=remote-mac" \
187
+ "remote_root=/remote/root" \
188
+ "remote_backend=herdr" \
189
+ "remote_herdr_session=fm-remote" \
190
+ "remote_target=fm-remote:p1"
191
+ cat > "$home/data/secondmates.md" <<EOF
192
+ - rsm - remote test domain (host: remote-mac; root: /remote/root; home: $2; scope: remote testing; projects: alpha; added 2026-08-02)
193
+ EOF
194
+ printf '%s\n' "$home"
195
+ }
196
+
197
+ remote_inbox_records() { # <remote-home>
198
+ find "$1/state/parent-route/rsm.inbox" -maxdepth 1 -name '*.msg' 2>/dev/null
199
+ }
200
+
201
+ # The single non-dot pending-reply record in <home>, or empty.
202
+ pending_record() { # <home>
203
+ find "$1/state/pending-replies" -maxdepth 1 -type f ! -name '.*' 2>/dev/null | head -1
204
+ }
205
+
206
+ drain_out() { # <home>
207
+ FM_STATE_OVERRIDE="$1/state" "$DRAIN" 2>/dev/null
208
+ }
209
+
210
+ send_env() { # <fakebin> <parent-home> <ssh-log> [extra env...] -- <cmd...>
211
+ local fb=$1 home=$2 ssh_log=$3
212
+ shift 3
213
+ env PATH="$fb:$PATH" \
214
+ FM_ROOT_OVERRIDE="$ROOT" FM_HOME="$home" FM_SEND_SETTLE=0 \
215
+ FM_SSH_BIN="$fb/fake-ssh" FM_SSH_LOG="$ssh_log" \
216
+ FM_SSH_COUNT="$ssh_log.count" FM_REMOTE_CODE_ROOT="$ROOT" \
217
+ "$@"
218
+ }
219
+
220
+ test_remote_steer_lands_in_remote_inbox() {
221
+ local dir fb ssh_log home rhome rc err rec recs body pend
222
+ dir="$TMP_ROOT/remote-inbox"; mkdir -p "$dir"
223
+ fb=$(make_stubs "$dir"); ssh_log="$dir/ssh.log"; : > "$ssh_log"
224
+ rhome=$(setup_remote_secondmate_home remote-inbox)
225
+ home=$(setup_remote_parent_home remote-inbox "$rhome")
226
+
227
+ rc=0
228
+ send_env "$fb" "$home" "$ssh_log" \
229
+ "$SEND" rsm "please rename the metric" >"$dir/out" 2>"$dir/err" || rc=$?
230
+ err=$(cat "$dir/err")
231
+ expect_code 0 "$rc" "a durably recorded remote steer must exit 0: $err"
232
+ assert_grep 'fm-remote-entrypoint.sh' "$ssh_log" "the steer should cross the remote transport"
233
+ recs=$(remote_inbox_records "$rhome")
234
+ [ -n "$recs" ] || fail "the steer must land as a record in the remote steering inbox"
235
+ rec=$(printf '%s\n' "$recs" | head -1)
236
+ body=$(cat "$rec")
237
+ case "$body" in
238
+ *"please rename the metric"*) : ;;
239
+ *) fail "the remote record must carry the steer text: $body" ;;
240
+ esac
241
+ printf '%s' "$body" | grep -Eq 'corr=[a-f0-9]{16}' \
242
+ || fail "the remote record must carry the marked request's corr token: $body"
243
+ case "$body" in
244
+ *"$FM_FROMFIRST_MARK"*) : ;;
245
+ *) fail "the remote record must carry the from-firstmate marker: $body" ;;
246
+ esac
247
+ # The doorbell could not reach the fixture pane (no herdr CLI here); that
248
+ # never fails the send, and the notice still names the durable record.
249
+ assert_contains "$err" "durably recorded" \
250
+ "a failed doorbell must be reported as a notice on a durably sent steer"
251
+ assert_not_contains "$err" "error:" "a durably recorded steer must not carry an error report"
252
+ pend=$(pending_record "$home")
253
+ [ -n "$pend" ] || fail "the marked remote steer must keep its pending-reply expectation"
254
+ [ -n "$(grep '^delivered_epoch=' "$pend" | cut -d= -f2-)" ] \
255
+ || fail "a recorded remote steer must mark the expectation delivered at enqueue: $(cat "$pend")"
256
+ [ "$(grep '^phase=' "$pend" | tail -1 | cut -d= -f2-)" = awaiting_report ] \
257
+ || fail "the delivered expectation must await its report: $(cat "$pend")"
258
+ pass "fm-send remote: a text steer lands as a durable remote inbox record and exits 0 at enqueue"
259
+ }
260
+
261
+ test_remote_rerun_is_idempotent() {
262
+ local dir fb ssh_log home rhome rc err count pend corr expected_cmd arg quoted rec ssh_before resend_cmd
263
+ dir="$TMP_ROOT/remote-idem"; mkdir -p "$dir"
264
+ fb=$(make_stubs "$dir"); ssh_log="$dir/ssh.log"; : > "$ssh_log"
265
+ rhome=$(setup_remote_secondmate_home remote-idem)
266
+ home=$(setup_remote_parent_home remote-idem "$rhome")
267
+
268
+ # Ambiguous transport: the remote leg executes for real, then ssh reports
269
+ # 255 (completion unknown). fm-send retries the identical leg once; the
270
+ # idempotent remote write must land both executions on the same record.
271
+ rc=0
272
+ (
273
+ cd "$TMP_ROOT" || exit 1
274
+ send_env "$fb" "${home#"$TMP_ROOT/"}" "$ssh_log" FM_FAKE_SSH_AMBIGUOUS=1 \
275
+ "$SEND" rsm "please rename the metric"
276
+ ) >"$dir/out" 2>"$dir/err" || rc=$?
277
+ err=$(cat "$dir/err")
278
+ [ "$rc" -ne 0 ] || fail "a twice-lost transport must not claim confirmed delivery"
279
+ [ "$(cat "$ssh_log.count")" = 2 ] \
280
+ || fail "fm-send must retry the identical remote leg exactly once after ssh 255, got $(cat "$ssh_log.count") attempts"
281
+ count=$(remote_inbox_records "$rhome" | grep -c . || true)
282
+ [ "$count" = 1 ] \
283
+ || fail "re-running the remote leg must dedup onto one record, found $count:"$'\n'"$(remote_inbox_records "$rhome")"
284
+ assert_contains "$err" "Only the correlation-reusing resend below is idempotent" \
285
+ "an unconfirmed remote steer must limit resend safety to correlation reuse"
286
+ assert_not_contains "$err" "do not resend" \
287
+ "the deleted do-not-resend trap must be gone for an unconfirmed remote steer"
288
+ pend=$(pending_record "$home")
289
+ [ -n "$pend" ] || fail "an unconfirmed remote steer must preserve its expectation for the record that may have landed"
290
+ [ "$(grep '^phase=' "$pend" | tail -1 | cut -d= -f2-)" = delivery_unknown ] \
291
+ || fail "the preserved expectation must record unknown delivery: $(cat "$pend")"
292
+ corr=$(fm_pending_reply_get "$pend" corr_id)
293
+ printf -v quoted '%q' "$home"
294
+ expected_cmd="FM_HOME=$quoted FM_PENDING_REPLY_EXISTING_CORR=$corr"
295
+ for arg in "$SEND" rsm "please rename the metric"; do
296
+ printf -v quoted '%q' "$arg"
297
+ expected_cmd="$expected_cmd $quoted"
298
+ done
299
+ assert_contains "$err" "$expected_cmd" \
300
+ "double transport loss must print the exact correlation-reusing resend command"
301
+
302
+ fm_pending_reply_set "$pend" phase escalated \
303
+ || fail "could not advance the ambiguous expectation to the escalated fixture phase"
304
+ ssh_before=$(cat "$ssh_log.count")
305
+ rc=0
306
+ send_env "$fb" "$home" "$ssh_log" FM_PENDING_REPLY_EXISTING_CORR="$corr" \
307
+ "$SEND" rsm "please rename the metric" >"$dir/stale-resend.out" 2>"$dir/stale-resend.err" || rc=$?
308
+ [ "$rc" -ne 0 ] || fail "an explicitly requested non-reusable correlation must fail closed"
309
+ assert_contains "$(cat "$dir/stale-resend.err")" "refusing to mint a replacement correlation" \
310
+ "a stale explicit correlation must explain that no replacement was minted"
311
+ [ "$(cat "$ssh_log.count")" = "$ssh_before" ] \
312
+ || fail "a stale explicit correlation reached the remote transport"
313
+ [ "$(find "$home/state/pending-replies" -maxdepth 1 -type f ! -name '.*' | wc -l | tr -d ' ')" = 1 ] \
314
+ || fail "a stale explicit correlation minted a replacement expectation"
315
+ [ "$(find "$rhome/state/parent-route/rsm.inbox" -name '*.msg' | wc -l | tr -d ' ')" = 1 ] \
316
+ || fail "a stale explicit correlation created another remote record"
317
+ fm_pending_reply_set "$pend" phase delivery_unknown \
318
+ || fail "could not restore the ambiguous expectation for the supported resend"
319
+
320
+ resend_cmd=$(tail -1 "$dir/err")
321
+ rc=0
322
+ (
323
+ unset FM_HOME
324
+ env PATH="$fb:$PATH" FM_ROOT_OVERRIDE="$ROOT" FM_SEND_SETTLE=0 \
325
+ FM_SSH_BIN="$fb/fake-ssh" FM_SSH_LOG="$ssh_log" \
326
+ FM_SSH_COUNT="$ssh_log.count" FM_REMOTE_CODE_ROOT="$ROOT" \
327
+ bash -c "$resend_cmd"
328
+ ) >"$dir/resend.out" 2>"$dir/resend.err" || rc=$?
329
+ expect_code 0 "$rc" "the printed correlation-reusing resend must succeed without an inherited FM_HOME"
330
+ count=$(find "$rhome/state/parent-route/rsm.inbox" -name '*.msg' | wc -l | tr -d ' ')
331
+ [ "$count" = 1 ] || fail "the correlation-reusing resend must leave exactly one remote record, found $count"
332
+ rec=$(find "$rhome/state/parent-route/rsm.inbox" -name '*.msg' | head -1)
333
+ grep -F "corr=$corr" "$rec" >/dev/null \
334
+ || fail "the deduplicated remote record did not preserve correlation $corr"
335
+ [ -n "$(fm_pending_reply_get "$pend" delivered_epoch)" ] \
336
+ || fail "the successful resend did not confirm pending-reply delivery: $(cat "$pend")"
337
+ [ "$(fm_pending_reply_get "$pend" phase)" = awaiting_report ] \
338
+ || fail "the successful resend did not restore awaiting_report: $(cat "$pend")"
339
+ pass "fm-send remote: the printed correlation-reusing resend deduplicates onto the same record"
340
+ }
341
+
342
+ test_remote_retry_failure_preserves_ambiguous_expectation() {
343
+ local dir fb ssh_log home rhome rc err pend
344
+ dir="$TMP_ROOT/remote-ambiguous-then-fail"; mkdir -p "$dir"
345
+ fb=$(make_stubs "$dir"); ssh_log="$dir/ssh.log"; : > "$ssh_log"
346
+ rhome=$(setup_remote_secondmate_home remote-ambiguous-then-fail)
347
+ home=$(setup_remote_parent_home remote-ambiguous-then-fail "$rhome")
348
+
349
+ rc=0
350
+ send_env "$fb" "$home" "$ssh_log" FM_FAKE_SSH_AFTER_AMBIGUOUS_RC=1 \
351
+ "$SEND" rsm "please rename the metric" >"$dir/out" 2>"$dir/err" || rc=$?
352
+ err=$(cat "$dir/err")
353
+ [ "$rc" -ne 0 ] || fail "a failed retry after unknown completion must not claim delivery"
354
+ assert_contains "$err" "first transport attempt had unknown completion" \
355
+ "the final error must retain the first attempt's ambiguous completion"
356
+ pend=$(pending_record "$home")
357
+ [ -n "$pend" ] || fail "an ambiguous first attempt must preserve its expectation when the retry fails"
358
+ [ "$(grep '^phase=' "$pend" | tail -1 | cut -d= -f2-)" = delivery_unknown ] \
359
+ || fail "the ambiguous expectation must remain delivery_unknown after a failed retry: $(cat "$pend")"
360
+ [ "$(remote_inbox_records "$rhome" | grep -c . || true)" = 1 ] \
361
+ || fail "the first attempt's durable record must remain the sole remote record"
362
+ pass "fm-send remote: a failed retry cannot erase an earlier ambiguous delivery"
363
+ }
364
+
365
+ test_remote_fire_and_forget_never_arms_reply_recovery() {
366
+ local dir fb ssh_log home rhome rc count delivery action
367
+ dir="$TMP_ROOT/remote-fire-and-forget"; mkdir -p "$dir"
368
+ fb=$(make_stubs "$dir"); ssh_log="$dir/ssh.log"; : > "$ssh_log"
369
+ rhome=$(setup_remote_secondmate_home remote-fire-and-forget)
370
+ home=$(setup_remote_parent_home remote-fire-and-forget "$rhome")
371
+ delivery=0123456789abcdef
372
+
373
+ rc=0
374
+ send_env "$fb" "$home" "$ssh_log" FM_FAKE_SSH_AFTER_AMBIGUOUS_RC=1 \
375
+ "$SEND" rsm --fire-and-forget "$delivery" "reconcile your own books" \
376
+ >"$dir/out" 2>"$dir/err" || rc=$?
377
+ expect_code 3 "$rc" "an ambiguous fire-and-forget delivery must report unconfirmed"
378
+ [ "$(find "$home/state/pending-replies" -maxdepth 1 -type f 2>/dev/null | wc -l | tr -d ' ')" = 0 ] \
379
+ || fail "fire-and-forget delivery created a pending-reply expectation"
380
+ count=$(find "$rhome/state/parent-route/rsm.inbox" -name '*.msg' | wc -l | tr -d ' ')
381
+ [ "$count" = 1 ] || fail "the ambiguous fire-and-forget delivery did not land exactly once"
382
+ action=$(FM_TASK_INBOX_GRACE_SECS=0 FM_TASK_INBOX_RING_MAX=0 \
383
+ fm_task_inbox_due_action "$rhome/state/parent-route" rsm)
384
+ [ "$action" = quiet ] || fail "the remote fire-and-forget record armed inbox escalation: $action"
385
+
386
+ send_env "$fb" "$home" "$ssh_log" \
387
+ "$SEND" rsm --fire-and-forget "$delivery" "reconcile your own books" \
388
+ >"$dir/retry.out" 2>"$dir/retry.err" \
389
+ || fail "the fire-and-forget retry failed"
390
+ count=$(find "$rhome/state/parent-route/rsm.inbox" -name '*.msg' | wc -l | tr -d ' ')
391
+ [ "$count" = 1 ] || fail "the same fire-and-forget delivery id created a duplicate remote record"
392
+ grep -F "delivery=$delivery" "$(remote_inbox_records "$rhome" | head -1)" >/dev/null \
393
+ || fail "the remote record omitted its fire-and-forget delivery identity"
394
+ pass "fm-send remote: fire-and-forget delivery is idempotent without reply recovery"
395
+ }
396
+
397
+ test_remote_send_revalidates_after_retirement_lock() {
398
+ local dir rhome meta lock ready release rc sender_pid holder_pid
399
+ dir="$TMP_ROOT/remote-retire-race"; mkdir -p "$dir"
400
+ rhome=$(setup_remote_secondmate_home remote-retire-race)
401
+ meta="$rhome/state/parent-route/rsm.meta"
402
+ lock="$rhome/state/parent-route/.meta-rsm.lock"
403
+ ready="$dir/lock-ready"
404
+ release="$dir/release-lock"
405
+ FM_STATE_OVERRIDE="$rhome/state/parent-route" bash -c '
406
+ . "$1"
407
+ fm_task_inbox_lock_acquire "$2" || exit 91
408
+ : > "$3"
409
+ while [ ! -e "$4" ]; do sleep 0.05; done
410
+ rm -f "$5"
411
+ fm_lock_release "$2"
412
+ ' _ "$ROOT/bin/fm-task-inbox-lib.sh" "$lock" "$ready" "$release" "$meta" &
413
+ holder_pid=$!
414
+ while [ ! -e "$ready" ]; do kill -0 "$holder_pid" 2>/dev/null || fail "metadata-lock holder exited early"; sleep 0.05; done
415
+
416
+ rc=0
417
+ env FM_HOME="$rhome" FM_ROOT_OVERRIDE="$ROOT" \
418
+ "$ROOT/bin/fm-remote-secondmate-control.sh" send rsm "retirement-race steer" \
419
+ >"$dir/out" 2>"$dir/err" &
420
+ sender_pid=$!
421
+ sleep 0.2
422
+ : > "$release"
423
+ wait "$sender_pid" || rc=$?
424
+ wait "$holder_pid" || fail "metadata-lock holder failed"
425
+ [ "$rc" -ne 0 ] || fail "a send must not enqueue after endpoint retirement won the metadata lock"
426
+ [ -z "$(remote_inbox_records "$rhome")" ] \
427
+ || fail "a send re-created an orphan inbox after endpoint retirement"
428
+ pass "remote control: send revalidates endpoint ownership under the metadata lock"
429
+ }
430
+
431
+ test_remote_send_revalidates_parent_route_after_retirement_lock() {
432
+ local dir fb ssh_log home rhome meta lock ready release rc sender_pid holder_pid
433
+ dir="$TMP_ROOT/remote-parent-retire-race"; mkdir -p "$dir"
434
+ fb=$(make_stubs "$dir"); ssh_log="$dir/ssh.log"; : > "$ssh_log"
435
+ rhome=$(setup_remote_secondmate_home remote-parent-retire-race)
436
+ home=$(setup_remote_parent_home remote-parent-retire-race "$rhome")
437
+ meta="$home/state/rsm.meta"
438
+ lock="$home/state/.meta-rsm.lock"
439
+ ready="$dir/lock-ready"
440
+ release="$dir/release-lock"
441
+ FM_STATE_OVERRIDE="$home/state" bash -c '
442
+ . "$1"
443
+ fm_task_inbox_lock_acquire "$2" || exit 91
444
+ : > "$3"
445
+ while [ ! -e "$4" ]; do sleep 0.05; done
446
+ rm -f "$5"
447
+ fm_lock_release "$2"
448
+ ' _ "$ROOT/bin/fm-task-inbox-lib.sh" "$lock" "$ready" "$release" "$meta" &
449
+ holder_pid=$!
450
+ while [ ! -e "$ready" ]; do kill -0 "$holder_pid" 2>/dev/null || fail "parent metadata-lock holder exited early"; sleep 0.05; done
451
+
452
+ rc=0
453
+ send_env "$fb" "$home" "$ssh_log" \
454
+ "$SEND" rsm "parent-retirement-race steer" >"$dir/out" 2>"$dir/err" &
455
+ sender_pid=$!
456
+ sleep 0.2
457
+ : > "$release"
458
+ wait "$sender_pid" || rc=$?
459
+ wait "$holder_pid" || fail "parent metadata-lock holder failed"
460
+ [ "$rc" -ne 0 ] || fail "a remote send must not enqueue after parent retirement won the metadata lock"
461
+ [ -z "$(remote_inbox_records "$rhome")" ] \
462
+ || fail "a remote send enqueued after its parent route retired"
463
+ [ -z "$(pending_record "$home")" ] \
464
+ || fail "a parent-route retirement failure left a created pending expectation"
465
+ pass "fm-send remote: enqueue revalidates the parent route under its metadata lock"
466
+ }
467
+
468
+ test_remote_expected_host_revalidates_final_route() {
469
+ local dir fb ssh_log home rhome rc err count
470
+ dir="$TMP_ROOT/remote-expected-host"; mkdir -p "$dir"
471
+ fb=$(make_stubs "$dir"); ssh_log="$dir/ssh.log"; : > "$ssh_log"
472
+ rhome=$(setup_remote_secondmate_home remote-expected-host)
473
+ home=$(setup_remote_parent_home remote-expected-host "$rhome")
474
+
475
+ rc=0
476
+ send_env "$fb" "$home" "$ssh_log" \
477
+ FM_SEND_EXPECTED_SPAWN_GEN="" FM_SEND_EXPECTED_REMOTE_HOST=remote-mac \
478
+ "$SEND" rsm --fire-and-forget 1111111111111111 "matching expected host" \
479
+ >"$dir/match.out" 2>"$dir/match.err" || rc=$?
480
+ expect_code 0 "$rc" "a matching expected remote host must allow delivery"
481
+ count=$(remote_inbox_records "$rhome" | grep -c . || true)
482
+ [ "$count" = 1 ] || fail "a matching expected remote host did not deliver exactly once"
483
+
484
+ rc=0
485
+ send_env "$fb" "$home" "$ssh_log" \
486
+ FM_SEND_EXPECTED_SPAWN_GEN="" FM_SEND_EXPECTED_REMOTE_HOST=retired-mac \
487
+ "$SEND" rsm --fire-and-forget 2222222222222222 "stale expected host" \
488
+ >"$dir/mismatch.out" 2>"$dir/mismatch.err" || rc=$?
489
+ [ "$rc" -ne 0 ] || fail "a mismatched expected remote host reported delivery"
490
+ err=$(cat "$dir/mismatch.err")
491
+ assert_contains "$err" "retired or changed route" \
492
+ "a mismatched expected remote host did not report the route replacement: $err"
493
+ count=$(remote_inbox_records "$rhome" | grep -c . || true)
494
+ [ "$count" = 1 ] || fail "a mismatched expected remote host reached the remote inbox"
495
+ pass "fm-send remote: expected host is enforced by final route validation"
496
+ }
497
+
498
+ test_remote_resolve_key_closes_at_enqueue() {
499
+ local dir fb ssh_log home rhome rc out
500
+ dir="$TMP_ROOT/remote-key"; mkdir -p "$dir"
501
+ fb=$(make_stubs "$dir"); ssh_log="$dir/ssh.log"; : > "$ssh_log"
502
+ rhome=$(setup_remote_secondmate_home remote-key)
503
+ home=$(setup_remote_parent_home remote-key "$rhome")
504
+ printf 'needs-decision [key=upgrade-window]: tonight or the weekend\n' > "$home/state/rsm.status"
505
+
506
+ rc=0
507
+ send_env "$fb" "$home" "$ssh_log" \
508
+ "$SEND" rsm --resolve-key upgrade-window "the weekend, freeze Friday" >/dev/null 2>&1 || rc=$?
509
+ expect_code 0 "$rc" "a durably recorded remote answer must exit 0"
510
+ grep -F 'resolved [key=upgrade-window]: answered: the weekend, freeze Friday' "$home/state/rsm.status" >/dev/null \
511
+ || fail "a recorded remote answer must close the decision at enqueue: $(cat "$home/state/rsm.status")"
512
+ out=$(drain_out "$home")
513
+ if printf '%s' "$out" | grep -F 'OPEN DECISIONS' >/dev/null; then
514
+ fail "the answered decision still lists as open after a recorded remote answer: $out"
515
+ fi
516
+ grep -rqF 'the weekend, freeze Friday' "$rhome/state/parent-route/rsm.inbox" \
517
+ || fail "the remote answer must land in the remote steering inbox"
518
+ pass "fm-send remote: a --resolve-key answer closes its decision at enqueue"
519
+ }
520
+
521
+ test_remote_slash_rides_inbox() {
522
+ local dir fb ssh_log home rhome rc recs
523
+ dir="$TMP_ROOT/remote-slash"; mkdir -p "$dir"
524
+ fb=$(make_stubs "$dir"); ssh_log="$dir/ssh.log"; : > "$ssh_log"
525
+ rhome=$(setup_remote_secondmate_home remote-slash)
526
+ home=$(setup_remote_parent_home remote-slash "$rhome")
527
+
528
+ rc=0
529
+ send_env "$fb" "$home" "$ssh_log" \
530
+ "$SEND" rsm "/audit the ledger" >/dev/null 2>"$dir/err" || rc=$?
531
+ expect_code 0 "$rc" "a remote harness-native steer must ride the inbox and exit 0"
532
+ recs=$(remote_inbox_records "$rhome")
533
+ [ -n "$recs" ] || fail "a remote '/' steer must land in the remote steering inbox, not a typed plane"
534
+ grep -rqF '/audit the ledger' "$rhome/state/parent-route/rsm.inbox" \
535
+ || fail "the remote record must carry the harness-native text verbatim"
536
+ pass "fm-send remote: every remote text, harness-native included, rides the inbox"
537
+ }
538
+
539
+ test_remote_real_failure_still_fails() {
540
+ local dir fb ssh_log home rhome rc err
541
+ dir="$TMP_ROOT/remote-fail"; mkdir -p "$dir"
542
+ fb=$(make_stubs "$dir"); ssh_log="$dir/ssh.log"; : > "$ssh_log"
543
+ rhome=$(setup_remote_secondmate_home remote-fail)
544
+ home=$(setup_remote_parent_home remote-fail "$rhome")
545
+
546
+ rc=0
547
+ send_env "$fb" "$home" "$ssh_log" FM_FAKE_SSH_RC=1 \
548
+ FM_FAKE_SSH_STDERR='error: remote secondmate rsm endpoint metadata is invalid; refusing access until it is explicitly migrated' \
549
+ "$SEND" rsm "please rename the metric" >"$dir/out" 2>"$dir/err" || rc=$?
550
+ err=$(cat "$dir/err")
551
+ [ "$rc" -ne 0 ] || fail "a genuinely failed remote send must exit nonzero"
552
+ assert_contains "$err" "steer not sent to remote secondmate rsm" \
553
+ "a real remote failure must still report a real error"
554
+ assert_contains "$err" "endpoint metadata is invalid" \
555
+ "a real remote failure must surface the remote leg's own stderr"
556
+ [ -z "$(pending_record "$home")" ] \
557
+ || fail "a failed send must discard its undelivered expectation"
558
+ [ -z "$(remote_inbox_records "$rhome")" ] \
559
+ || fail "a refused remote leg must leave no inbox record"
560
+ pass "fm-send remote: a real remote failure still fails loudly with the remote diagnostics"
561
+ }
562
+
563
+ test_remote_exit3_no_longer_delivered() {
564
+ local dir fb ssh_log home rhome rc err
565
+ dir="$TMP_ROOT/remote-exit3"; mkdir -p "$dir"
566
+ fb=$(make_stubs "$dir"); ssh_log="$dir/ssh.log"; : > "$ssh_log"
567
+ rhome=$(setup_remote_secondmate_home remote-exit3)
568
+ home=$(setup_remote_parent_home remote-exit3 "$rhome")
569
+
570
+ # The typed-plane remote transport remapped a leg exit 3 to "delivered with
571
+ # confirmation pending". That transport is deleted: any nonzero remote leg
572
+ # status is a failure, never a delivery claim.
573
+ rc=0
574
+ send_env "$fb" "$home" "$ssh_log" FM_FAKE_SSH_RC=3 \
575
+ "$SEND" rsm "please rename the metric" >"$dir/out" 2>"$dir/err" || rc=$?
576
+ err=$(cat "$dir/err")
577
+ [ "$rc" -ne 0 ] || fail "a nonzero remote leg must no longer be remapped to delivered"
578
+ assert_not_contains "$err" "delivered to remote secondmate" \
579
+ "the deleted exit-3-as-delivered remap must be gone"
580
+ assert_contains "$err" "steer not sent to remote secondmate rsm" \
581
+ "a failed remote leg must report the steer as not sent"
582
+ [ -z "$(pending_record "$home")" ] \
583
+ || fail "a failed remote leg must discard its undelivered expectation"
584
+ pass "fm-send remote: exit 3 from the remote leg is a failure, never a delivery claim"
585
+ }
586
+
587
+ test_remote_transport_loss_preserves_expectation() {
588
+ local dir fb ssh_log home rhome rc err pend
589
+ dir="$TMP_ROOT/remote-255"; mkdir -p "$dir"
590
+ fb=$(make_stubs "$dir"); ssh_log="$dir/ssh.log"; : > "$ssh_log"
591
+ rhome=$(setup_remote_secondmate_home remote-255)
592
+ home=$(setup_remote_parent_home remote-255 "$rhome")
593
+
594
+ rc=0
595
+ send_env "$fb" "$home" "$ssh_log" FM_FAKE_SSH_RC=255 \
596
+ "$SEND" rsm "please rename the metric" >"$dir/out" 2>"$dir/err" || rc=$?
597
+ err=$(cat "$dir/err")
598
+ [ "$rc" -ne 0 ] || fail "an unknown-completion transport loss must exit nonzero"
599
+ [ "$(cat "$ssh_log.count")" = 2 ] \
600
+ || fail "fm-send must retry the identical remote leg once on ssh 255, got $(cat "$ssh_log.count") attempts"
601
+ assert_contains "$err" "Only the correlation-reusing resend below is idempotent" \
602
+ "transport loss must print the supported safe resend boundary"
603
+ assert_not_contains "$err" "do not resend" \
604
+ "the deleted do-not-resend trap must be gone for transport loss"
605
+ pend=$(pending_record "$home")
606
+ [ -n "$pend" ] || fail "transport loss must preserve the expectation for reconciliation"
607
+ [ "$(grep '^phase=' "$pend" | tail -1 | cut -d= -f2-)" = delivery_unknown ] \
608
+ || fail "transport loss must move the expectation to delivery_unknown: $(cat "$pend")"
609
+ pass "fm-send remote: ssh 255 fails with resend-safe guidance and preserves the expectation"
610
+ }
611
+
612
+ test_remote_send_budget_bounds_busy_lane() {
613
+ local dir fb ssh_log home rhome rc err began elapsed count pend delivery corr ssh_before
614
+ dir="$TMP_ROOT/remote-budget"; mkdir -p "$dir"
615
+ fb=$(make_stubs "$dir"); ssh_log="$dir/ssh.log"; : > "$ssh_log"
616
+ rhome=$(setup_remote_secondmate_home remote-budget)
617
+ home=$(setup_remote_parent_home remote-budget "$rhome")
618
+ delivery=aaaabbbbccccdddd
619
+
620
+ rc=0
621
+ send_env "$fb" "$home" "$ssh_log" FM_SEND_REMOTE_BUDGET=invalid \
622
+ "$SEND" rsm --key Enter >"$dir/key-invalid.out" 2>"$dir/key-invalid.err" || rc=$?
623
+ [ "$rc" -ne 0 ] || fail "an invalid remote key budget must fail"
624
+ assert_contains "$(cat "$dir/key-invalid.err")" "must be a positive integer" \
625
+ "an invalid remote key budget must explain its validation failure"
626
+ [ ! -f "$ssh_log.count" ] || fail "an invalid remote key budget reached the transport"
627
+
628
+ began=$(date +%s)
629
+ rc=0
630
+ send_env "$fb" "$home" "$ssh_log" FM_FAKE_SSH_HANG=60 FM_SEND_REMOTE_BUDGET=2 \
631
+ "$SEND" rsm --key Enter >"$dir/key.out" 2>"$dir/key.err" || rc=$?
632
+ elapsed=$(( $(date +%s) - began ))
633
+ expect_code 1 "$rc" "a bounded remote key must preserve the existing failure contract"
634
+ [ "$elapsed" -le 15 ] || fail "the bounded remote key waited ${elapsed}s behind the busy lane"
635
+ assert_contains "$(cat "$dir/key.err")" "completion may be unknown" \
636
+ "a bounded remote key failure must preserve its existing diagnostic"
637
+ [ "$(cat "$ssh_log.count")" = 1 ] \
638
+ || fail "a bounded remote key must make exactly one transport attempt"
639
+ printf '0\n' > "$ssh_log.count"
640
+
641
+ # T5: a fire-and-forget send to a mate behind a busy lane returns its
642
+ # unconfirmed result within its own budget instead of waiting the lane out.
643
+ began=$(date +%s)
644
+ rc=0
645
+ send_env "$fb" "$home" "$ssh_log" FM_FAKE_SSH_HANG=60 FM_SEND_REMOTE_BUDGET=2 \
646
+ "$SEND" rsm --fire-and-forget "$delivery" "reconcile your own books" \
647
+ >"$dir/out" 2>"$dir/err" || rc=$?
648
+ elapsed=$(( $(date +%s) - began ))
649
+ err=$(cat "$dir/err")
650
+ expect_code 3 "$rc" "a budget-bounded fire-and-forget send must report unconfirmed: $err"
651
+ [ "$elapsed" -le 15 ] || fail "the bounded send waited ${elapsed}s behind the busy lane"
652
+ assert_contains "$err" "delivery-id=$delivery" \
653
+ "the bounded unconfirmed result must name the reusable delivery id"
654
+ [ "$(cat "$ssh_log.count")" = 1 ] \
655
+ || fail "a budget hit must not retry into the same busy lane, got $(cat "$ssh_log.count") attempts"
656
+
657
+ # A retry with the same delivery id against the recovered lane dedups onto
658
+ # the same remote record.
659
+ send_env "$fb" "$home" "$ssh_log" \
660
+ "$SEND" rsm --fire-and-forget "$delivery" "reconcile your own books" \
661
+ >"$dir/retry.out" 2>"$dir/retry.err" \
662
+ || fail "the same-delivery-id retry after the budget hit failed"
663
+ count=$(remote_inbox_records "$rhome" | grep -c . || true)
664
+ [ "$count" = 1 ] || fail "the same-delivery-id retry did not dedup onto one record, found $count"
665
+
666
+ # A reply-bearing send names the budget and prints the correlation-reusing
667
+ # resend command, with the expectation preserved as delivery-unknown.
668
+ rc=0
669
+ send_env "$fb" "$home" "$ssh_log" FM_FAKE_SSH_HANG=60 FM_SEND_REMOTE_BUDGET=2 \
670
+ "$SEND" rsm "please rename the metric" >"$dir/reply.out" 2>"$dir/reply.err" || rc=$?
671
+ err=$(cat "$dir/reply.err")
672
+ [ "$rc" -ne 0 ] || fail "a budget-bounded reply-bearing send must not claim confirmed delivery"
673
+ assert_contains "$err" "within its 2s budget" \
674
+ "the budget-bounded failure must name the budget that bounded it"
675
+ assert_contains "$err" "Only the correlation-reusing resend below is idempotent" \
676
+ "the budget-bounded failure must print the supported safe resend boundary"
677
+ pend=$(pending_record "$home")
678
+ [ -n "$pend" ] || fail "a budget-bounded reply-bearing send must preserve its expectation"
679
+ [ "$(grep '^phase=' "$pend" | tail -1 | cut -d= -f2-)" = delivery_unknown ] \
680
+ || fail "the preserved expectation must record unknown delivery: $(cat "$pend")"
681
+
682
+ # Invalid transport configuration fails before a correlation-reusing resend
683
+ # mutates the preserved expectation or reaches the transport.
684
+ corr=$(fm_pending_reply_get "$pend" corr_id)
685
+ cp "$pend" "$dir/pending-before-invalid-budget"
686
+ ssh_before=$(cat "$ssh_log.count")
687
+ rc=0
688
+ send_env "$fb" "$home" "$ssh_log" FM_SEND_REMOTE_BUDGET=invalid \
689
+ FM_PENDING_REPLY_EXISTING_CORR="$corr" \
690
+ "$SEND" rsm "please rename the metric" >"$dir/invalid.out" 2>"$dir/invalid.err" || rc=$?
691
+ [ "$rc" -ne 0 ] || fail "an invalid remote budget must fail the resend"
692
+ assert_contains "$(cat "$dir/invalid.err")" "must be a positive integer" \
693
+ "an invalid remote budget must explain its validation failure"
694
+ [ "$(cat "$ssh_log.count")" = "$ssh_before" ] \
695
+ || fail "an invalid remote budget reached the remote transport"
696
+ cmp -s "$dir/pending-before-invalid-budget" "$pend" \
697
+ || fail "an invalid remote budget mutated the reusable pending expectation: $(cat "$pend")"
698
+ pass "fm-send remote: the remote leg is budget-bounded and stays idempotent across the bound"
699
+ }
700
+
701
+ test_local_secondmate_pending_keeps_expectation_armed() {
702
+ local dir fb log home rc rec corr
703
+ dir="$TMP_ROOT/local-pending-expectation"; mkdir -p "$dir"
704
+ fb=$(make_stubs "$dir"); log="$dir/send.log"
705
+ home=$(setup_home local-pending-expectation)
706
+ fm_write_meta "$home/state/lsm.meta" \
707
+ "window=sess:fm-lsm" "harness=claude" "kind=secondmate" "mode=secondmate" "home=$home/sm"
708
+
709
+ # A harness-native slash invocation keeps the typed plane for a LOCAL marked
710
+ # secondmate target, so this pins the kept armed-expectation semantics there.
711
+ : > "$log"
712
+ rc=0
713
+ env PATH="$fb:$PATH" FM_FAKE_TMUX_PENDING=1 \
714
+ FM_ROOT_OVERRIDE="$home" FM_HOME="$home" FM_SEND_LOG="$log" FM_SEND_SETTLE=0 \
715
+ "$SEND" lsm "/audit the ledger" >/dev/null 2>&1 || rc=$?
716
+ expect_code 3 "$rc" "an unconfirmed local secondmate submit must exit delivered-unconfirmed"
717
+ rec=$(pending_record "$home")
718
+ [ -n "$rec" ] \
719
+ || fail "the pending-reply expectation must survive an unconfirmed local secondmate send"
720
+ [ "$(fm_pending_reply_get "$rec" phase)" = awaiting_report ] \
721
+ || fail "the surviving expectation must stay armed, got $(fm_pending_reply_get "$rec" phase)"
722
+ # Armed means resolvable: the mate's correlated report still closes it.
723
+ corr=$(fm_pending_reply_get "$rec" corr_id)
724
+ printf 'done [corr=%s]: ledger clean\n' "$corr" > "$home/state/lsm.status"
725
+ fm_pending_reply_try_resolve "$home/state" "$corr" \
726
+ || fail "a correlated report must still resolve the preserved expectation"
727
+ pass "fm-send local: an unconfirmed secondmate send keeps its reply expectation armed"
728
+ }
729
+
730
+ test_local_pending_reports_delivered_unconfirmed() {
731
+ local dir fb log home rc err
732
+ dir="$TMP_ROOT/local-pending"; mkdir -p "$dir"
733
+ fb=$(make_stubs "$dir"); log="$dir/send.log"
734
+ home=$(setup_home local-pending)
735
+
736
+ # An explicit backend target is the typed plane, so the exit-3 ladder still
737
+ # governs it.
738
+ : > "$log"
739
+ rc=0
740
+ env PATH="$fb:$PATH" FM_FAKE_TMUX_PENDING=1 \
741
+ FM_ROOT_OVERRIDE="$home" FM_HOME="$home" FM_SEND_LOG="$log" FM_SEND_SETTLE=0 \
742
+ "$SEND" sess:win "steer text" >"$dir/out" 2>"$dir/err" || rc=$?
743
+ err=$(cat "$dir/err")
744
+ expect_code 3 "$rc" "an unconfirmed local submit must exit with the delivered-unconfirmed status"
745
+ assert_contains "$err" "submission is unconfirmed" \
746
+ "the unconfirmed local submit must be described honestly"
747
+ assert_not_contains "$err" "not submitted" \
748
+ "an unconfirmed local submit must not claim the text was not submitted"
749
+ assert_not_contains "$err" "error:" \
750
+ "an unconfirmed local submit must not carry an error-styled report"
751
+ pass "fm-send local: an unconfirmed submit exits 3 with an honest non-error report"
752
+ }
753
+
754
+ test_local_pending_does_not_close_resolve_key() {
755
+ local dir fb log home rc out
756
+ dir="$TMP_ROOT/local-pending-key"; mkdir -p "$dir"
757
+ fb=$(make_stubs "$dir"); log="$dir/send.log"
758
+ home=$(setup_home local-pending-key)
759
+ fm_write_meta "$home/state/t2.meta" "window=sess:fm-t2" "kind=ship"
760
+ printf 'blocked [key=creds]: need the deploy token\n' > "$home/state/t2.status"
761
+
762
+ # A harness-native slash answer keeps the typed plane, so the unconfirmed
763
+ # ladder still governs it; a plain-text answer would close at enqueue instead.
764
+ : > "$log"
765
+ rc=0
766
+ env PATH="$fb:$PATH" FM_FAKE_TMUX_PENDING=1 \
767
+ FM_ROOT_OVERRIDE="$home" FM_HOME="$home" FM_SEND_LOG="$log" FM_SEND_SETTLE=0 \
768
+ "$SEND" t2 --resolve-key creds "/vault fetch deploy-token" >/dev/null 2>&1 || rc=$?
769
+ expect_code 3 "$rc" "an unconfirmed local answer must exit with the delivered-unconfirmed status"
770
+ if grep -F 'resolved' "$home/state/t2.status" >/dev/null; then
771
+ fail "an unconfirmed local answer must not close the decision: $(cat "$home/state/t2.status")"
772
+ fi
773
+ out=$(drain_out "$home")
774
+ printf '%s' "$out" | grep -F '[key=creds]' >/dev/null \
775
+ || fail "the blocker must stay open after an unconfirmed local answer: $out"
776
+ pass "fm-send local: an unconfirmed submit still never closes a --resolve-key decision"
777
+ }
778
+
779
+ test_remote_steer_lands_in_remote_inbox
780
+ test_remote_rerun_is_idempotent
781
+ test_remote_retry_failure_preserves_ambiguous_expectation
782
+ test_remote_fire_and_forget_never_arms_reply_recovery
783
+ test_remote_send_revalidates_after_retirement_lock
784
+ test_remote_send_revalidates_parent_route_after_retirement_lock
785
+ test_remote_expected_host_revalidates_final_route
786
+ test_remote_resolve_key_closes_at_enqueue
787
+ test_remote_slash_rides_inbox
788
+ test_remote_real_failure_still_fails
789
+ test_remote_exit3_no_longer_delivered
790
+ test_remote_transport_loss_preserves_expectation
791
+ test_remote_send_budget_bounds_busy_lane
792
+ test_local_pending_reports_delivered_unconfirmed
793
+ test_local_pending_does_not_close_resolve_key
794
+ test_local_secondmate_pending_keeps_expectation_armed
795
+
796
+ echo "all fm-send-remote-delivery tests passed"