@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,821 @@
1
+ #!/usr/bin/env bash
2
+ # tests/fm-watch-arm.test.sh - the arm layer's cycle-close contract when the arm
3
+ # did not own the cycle.
4
+ #
5
+ # The watcher prints its one reason line to its OWN stdout, so only the arm that
6
+ # forked it ever reads that line. An arm that ATTACHED to an existing cycle holds
7
+ # no handle on it and can observe only a released lock, which is why a completely
8
+ # successful cycle used to be reported as
9
+ # "watcher: FAILED - cycle ended without an actionable reason" on every harness
10
+ # whose protocol reads that line. These are real-process tests: a real
11
+ # bin/fm-watch.sh holds the singleton, a real bin/fm-watch-arm.sh attaches to it,
12
+ # and a real status change drives a real wake through the watcher-bound delivery
13
+ # record and durable queue.
14
+ set -u
15
+
16
+ # shellcheck source=tests/wake-helpers.sh
17
+ . "$(dirname "${BASH_SOURCE[0]}")/wake-helpers.sh"
18
+
19
+ WATCH="$ROOT/bin/fm-watch.sh"
20
+ WATCH_ARM="$ROOT/bin/fm-watch-arm.sh"
21
+ DRAIN="$ROOT/bin/fm-wake-drain.sh"
22
+
23
+ TMP_ROOT=$(fm_test_tmproot fm-watch-arm-tests)
24
+
25
+ # Both starters background a real process the test later waits on, so they set a
26
+ # global instead of echoing: a command substitution would make the pid a child of
27
+ # a subshell this shell can no longer wait for.
28
+ SEED_PID=
29
+ ARM_PID=
30
+
31
+ # Start the real watcher as the singleton holder.
32
+ start_seed_watcher() { # <state> <fakebin> <watch-out>
33
+ local state=$1 fakebin=$2 out=$3 i
34
+ PATH="$fakebin:$PATH" FM_STATE_OVERRIDE="$state" FM_POLL=5 FM_SIGNAL_GRACE=1 \
35
+ FM_CHECK_INTERVAL=999999 FM_HEARTBEAT=999999 "$WATCH" > "$out" &
36
+ SEED_PID=$!
37
+ i=0
38
+ while [ "$i" -lt 60 ]; do
39
+ [ "$(cat "$state/.watch.lock/pid" 2>/dev/null || true)" = "$SEED_PID" ] \
40
+ && [ -e "$state/.last-watcher-beat" ] && break
41
+ sleep 0.1
42
+ i=$((i + 1))
43
+ done
44
+ [ "$(cat "$state/.watch.lock/pid" 2>/dev/null || true)" = "$SEED_PID" ] \
45
+ || fail "seed watcher did not take the lock"
46
+ }
47
+
48
+ # Attach a real arm to the live cycle.
49
+ start_attached_arm() { # <state> <fakebin> <arm-out> <confirm-timeout>
50
+ local state=$1 fakebin=$2 armout=$3 confirm=$4 i
51
+ PATH="$fakebin:$PATH" FM_STATE_OVERRIDE="$state" FM_ARM_ATTACH_POLL=0.1 \
52
+ FM_ARM_CONFIRM_TIMEOUT="$confirm" "$WATCH_ARM" > "$armout" &
53
+ ARM_PID=$!
54
+ i=0
55
+ while [ "$i" -lt 80 ]; do
56
+ grep -qF "watcher: attached pid=$SEED_PID" "$armout" 2>/dev/null && break
57
+ sleep 0.1
58
+ i=$((i + 1))
59
+ done
60
+ grep -qF "watcher: attached pid=$SEED_PID" "$armout" \
61
+ || fail "arm did not attach to the live watcher: $(cat "$armout")"
62
+ }
63
+
64
+ sha256_file() { # <path>
65
+ if command -v shasum >/dev/null 2>&1; then
66
+ shasum -a 256 "$1" | awk '{print $1}'
67
+ else
68
+ sha256sum "$1" | awk '{print $1}'
69
+ fi
70
+ }
71
+
72
+ write_remote_delta() { # <result-path> <status-line>
73
+ local result=$1 line=$2 payload empty payload_bytes payload_hash empty_hash
74
+ payload="$result.payload"
75
+ empty="$result.empty"
76
+ printf '%s\n' "$line" > "$payload"
77
+ : > "$empty"
78
+ payload_bytes=$(LC_ALL=C wc -c < "$payload" | tr -d '[:space:]')
79
+ payload_hash=$(sha256_file "$payload") || fail "could not hash remote delta payload"
80
+ empty_hash=$(sha256_file "$empty") || fail "could not hash empty remote delta prefix"
81
+ {
82
+ printf 'schema=fm-remote-delta.v1\n'
83
+ printf 'status=delta\n'
84
+ printf 'path=state/parent-replies.status\n'
85
+ printf 'from_offset=0\n'
86
+ printf 'to_offset=%s\n' "$payload_bytes"
87
+ printf 'from_prefix_sha256=%s\n' "$empty_hash"
88
+ printf 'to_prefix_sha256=%s\n' "$payload_hash"
89
+ printf 'payload_sha256=%s\n' "$payload_hash"
90
+ printf 'payload_bytes=%s\n' "$payload_bytes"
91
+ printf 'reason=fixture\n\n'
92
+ cat "$payload"
93
+ } > "$result"
94
+ rm -f "$payload" "$empty"
95
+ }
96
+
97
+ status_signature() { # <status-path>
98
+ bash -c '
99
+ . "$1"
100
+ reported=$(status_observed_signature "$2") || exit 1
101
+ size=$(_fm_status_file_size "$2") || exit 1
102
+ ident=$(_fm_open_decisions_file_ident "$2") || exit 1
103
+ printf "v2\t%s\t%s@%s" "$reported" "$size" "$ident"
104
+ ' _ "$ROOT/bin/fm-classify-lib.sh" "$1"
105
+ }
106
+
107
+ wait_for_file_text() { # <file> <fixed-text>
108
+ local file=$1 expected=$2 i=0
109
+ while [ "$i" -lt 100 ]; do
110
+ grep -F "$expected" "$file" >/dev/null 2>&1 && return 0
111
+ sleep 0.05
112
+ i=$((i + 1))
113
+ done
114
+ return 1
115
+ }
116
+
117
+ ack_wakes() { # <state>
118
+ local state=$1 sequence generation err
119
+ err="$state/.test-ack.err"
120
+ FM_STATE_OVERRIDE="$state" "$DRAIN" >/dev/null 2> "$err" || return 1
121
+ sequence=$(sed -n 's/^WAKE_ACK_REQUIRED:.*--ack-through \([0-9][0-9]*\) --recovery-generation [A-Za-z0-9._-][A-Za-z0-9._-]*$/\1/p' "$err")
122
+ generation=$(sed -n 's/^WAKE_ACK_REQUIRED:.*--ack-through [0-9][0-9]* --recovery-generation \([A-Za-z0-9._-][A-Za-z0-9._-]*\)$/\1/p' "$err")
123
+ rm -f "$err"
124
+ if [ -z "$sequence" ] || [ -z "$generation" ]; then
125
+ [ ! -s "$state/.wake-queue" ] || return 1
126
+ case "$(cat "$state/.watcher-down" 2>/dev/null || true)" in pending:*|announced:*) return 1 ;; esac
127
+ return 0
128
+ fi
129
+ FM_STATE_OVERRIDE="$state" "$DRAIN" --ack-through "$sequence" \
130
+ --recovery-generation "$generation"
131
+ }
132
+
133
+ # Print "<sequence>\t<generation>" from the acknowledgement command a drain
134
+ # printed, so a case can replay that exact pair later.
135
+ drain_ack_pair() { # <drain-stderr>
136
+ local err=$1 sequence generation
137
+ sequence=$(sed -n 's/^WAKE_ACK_REQUIRED:.*--ack-through \([0-9][0-9]*\) --recovery-generation [A-Za-z0-9._-][A-Za-z0-9._-]*$/\1/p' "$err")
138
+ generation=$(sed -n 's/^WAKE_ACK_REQUIRED:.*--ack-through [0-9][0-9]* --recovery-generation \([A-Za-z0-9._-][A-Za-z0-9._-]*\)$/\1/p' "$err")
139
+ [ -n "$sequence" ] && [ -n "$generation" ] || return 1
140
+ printf '%s\t%s\n' "$sequence" "$generation"
141
+ }
142
+
143
+ start_rearm_arm() { # <home> <state> <fakebin> <arm-out> [predecessor-arm-pid]
144
+ local home=$1 state=$2 fakebin=$3 armout=$4 predecessor=${5:-} i
145
+ PATH="$fakebin:$PATH" FM_HOME="$home" FM_STATE_OVERRIDE="$state" \
146
+ FM_POLL=1 FM_SIGNAL_GRACE=0 FM_CHECK_INTERVAL=999999 FM_HEARTBEAT=999999 \
147
+ FM_WATCH_PREDECESSOR_ARM_PID="$predecessor" \
148
+ "$WATCH_ARM" --restart > "$armout" &
149
+ ARM_PID=$!
150
+ i=0
151
+ while [ "$i" -lt 80 ]; do
152
+ grep -q '^watcher: started ' "$armout" 2>/dev/null && return 0
153
+ is_live_non_zombie "$ARM_PID" || return 0
154
+ sleep 0.05
155
+ i=$((i + 1))
156
+ done
157
+ return 0
158
+ }
159
+
160
+ test_attached_arm_reports_the_delivered_wake() {
161
+ local dir state fakebin out armout status
162
+ dir=$(make_case attached-delivered-wake)
163
+ state="$dir/state"
164
+ fakebin="$dir/fakebin"
165
+ out="$dir/watch.out"
166
+ armout="$dir/arm.out"
167
+ start_seed_watcher "$state" "$fakebin" "$out"
168
+ start_attached_arm "$state" "$fakebin" "$armout" 1
169
+
170
+ # A real captain-relevant status change: the watcher records it in the durable
171
+ # queue, prints its one reason line to its own stdout, and exits.
172
+ printf 'done: fixture finished\n' > "$state/demo.status"
173
+ wait_for_exit "$SEED_PID" 120
174
+ grep -q '^signal:' "$out" || fail "seed watcher did not surface the signal wake: $(cat "$out")"
175
+
176
+ wait_for_exit "$ARM_PID" 120
177
+ status=$?
178
+ grep -q 'demo.status' "$state/.wake-queue" \
179
+ || fail "the wake was not durably recorded, so this case proves nothing"
180
+ ! grep -qF 'watcher: FAILED' "$armout" \
181
+ || fail "attached arm reported a delivered wake as a failed cycle: $(cat "$armout")"
182
+ grep -q '^signal:' "$armout" \
183
+ || fail "attached arm did not report the durably recorded wake reason: $(cat "$armout")"
184
+ expect_code 0 "$status" "an attached arm whose cycle delivered a wake must close successfully"
185
+ grep -q 'reason=attached-delivered-wake' "$state/.watch-cycle-exits.log" \
186
+ || fail "the delivered-wake close was not classified in the lifecycle ledger"
187
+ pass "watch-arm: an attached arm reports the wake its cycle delivered instead of a false failure"
188
+ }
189
+
190
+ test_attached_arm_reports_the_delivered_wake_after_drain() {
191
+ local dir state fakebin out armout status
192
+ dir=$(make_case attached-drained-wake)
193
+ state="$dir/state"
194
+ fakebin="$dir/fakebin"
195
+ out="$dir/watch.out"
196
+ armout="$dir/arm.out"
197
+ start_seed_watcher "$state" "$fakebin" "$out"
198
+ # A wider confirmation budget keeps the arm in its successor wait while the
199
+ # handling turn drains, which is the ordering this case exists to cover.
200
+ start_attached_arm "$state" "$fakebin" "$armout" 5
201
+
202
+ printf 'done: fixture finished\n' > "$state/demo.status"
203
+ wait_for_exit "$SEED_PID" 120
204
+ # The handling turn consumes the records before the attached arm closes: the
205
+ # queue is empty again, while the watcher's identity-bound terminal record
206
+ # still proves which cycle delivered the reason.
207
+ FM_STATE_OVERRIDE="$state" "$DRAIN" >/dev/null 2>&1 || fail "drain failed"
208
+ ack_wakes "$state" || fail "handling acknowledgement failed"
209
+ [ ! -s "$state/.wake-queue" ] || fail "acknowledgement left records behind"
210
+
211
+ wait_for_exit "$ARM_PID" 200
212
+ status=$?
213
+ ! grep -qF 'watcher: FAILED' "$armout" \
214
+ || fail "attached arm reported an already-handled wake as a failed cycle: $(cat "$armout")"
215
+ grep -q '^signal:' "$armout" \
216
+ || fail "attached arm did not report the delivered reason after the queue drain: $(cat "$armout")"
217
+ expect_code 0 "$status" "an attached arm whose wake was already drained must close successfully"
218
+ pass "watch-arm: a delivered wake consumed by the handling turn still closes the attached arm cleanly"
219
+ }
220
+
221
+ test_attached_arm_still_fails_on_a_wake_it_did_not_deliver() {
222
+ local dir state fakebin out armout status
223
+ dir=$(make_case attached-no-delivery)
224
+ state="$dir/state"
225
+ fakebin="$dir/fakebin"
226
+ out="$dir/watch.out"
227
+ armout="$dir/arm.out"
228
+ start_seed_watcher "$state" "$fakebin" "$out"
229
+ start_attached_arm "$state" "$fakebin" "$armout" 1
230
+
231
+ # A process-event producer advances the same home-wide queue while the
232
+ # observed watcher remains uninvolved, so only watcher-bound evidence can
233
+ # distinguish this from a delivered watcher cycle.
234
+ append_wake "$state" check process-event "check: process-event result captured: fixture"
235
+ kill "$SEED_PID" 2>/dev/null || true
236
+ wait "$SEED_PID" 2>/dev/null || true
237
+ wait_for_exit "$ARM_PID" 120
238
+ status=$?
239
+ grep -qF 'watcher: FAILED - cycle ended without an actionable reason' "$armout" \
240
+ || fail "a cycle that delivered nothing must still fail loudly: $(cat "$armout")"
241
+ [ "$status" -ne 0 ] && [ "$status" -ne 124 ] \
242
+ || fail "arm did not exit nonzero for a cycle that delivered nothing (status $status)"
243
+ pass "watch-arm: a cycle that delivered no wake of its own still fails loudly"
244
+ }
245
+
246
+ test_rearm_resurfaces_durable_queue_and_remote_open_decision() {
247
+ local dir home state fakebin result armout drainout status watcher_pid sequence generation decision_recovery_arm decision_successor
248
+ dir=$(make_case rearm-resurface)
249
+ home="$dir/home"
250
+ state="$dir/state"
251
+ fakebin="$dir/fakebin"
252
+ result="$dir/remote.result"
253
+ armout="$dir/arm.out"
254
+ drainout="$dir/drain.out"
255
+ mkdir -p "$home/data"
256
+
257
+ # This is the real remote parent-reply ingest boundary. It writes the remote
258
+ # secondmate's decision onto the parent status surface the shared fold owns.
259
+ write_remote_delta "$result" \
260
+ 'needs-decision [key=remote-signoff]: remote secondmate is held for captain sign-off'
261
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" FM_DATA_OVERRIDE="$home/data" \
262
+ "$ROOT/bin/fm-procevent-remote-reply.sh" ingest ios "$result" >/dev/null \
263
+ || fail "remote parent-reply ingest failed"
264
+
265
+ # Drain once before the outage to establish the incremental cursor and the
266
+ # signal suppressor that a watcher had already observed. The decision remains
267
+ # intentionally open across the watcher-down interval.
268
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/baseline-drain.out" \
269
+ || fail "baseline drain failed"
270
+ ack_wakes "$state" || fail "baseline handling acknowledgement failed"
271
+ grep -F 'remote secondmate is held for captain sign-off' "$dir/baseline-drain.out" >/dev/null \
272
+ || fail "baseline fold did not expose the remote decision"
273
+ printf '%s' "$(status_signature "$state/ios.status")" > "$state/.seen-ios_status"
274
+
275
+ # A real watcher is then interrupted before the next two durable updates.
276
+ # This is the accepted blocking-tool shape: no watcher runs during the gap.
277
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/down-arm.out"
278
+ is_live_non_zombie "$ARM_PID" || fail "pre-outage watcher did not stay live"
279
+ watcher_pid=$(cat "$state/.watch.lock/pid" 2>/dev/null || true)
280
+ kill -KILL "$watcher_pid" 2>/dev/null || fail "could not abruptly stop pre-outage watcher"
281
+ wait "$ARM_PID" 2>/dev/null || true
282
+ [ ! -e "$state/.watcher-down" ] || fail "abrupt watcher exit unexpectedly ran cleanup"
283
+
284
+ # Two independent durable wakes arrive while no watcher exists. Neither gets
285
+ # a later status change to rescue it, which is the down-window loss shape.
286
+ append_wake "$state" check remote-reply-ios \
287
+ 'check: process-event result captured: remote-reply-ios:7'
288
+ append_wake "$state" check startup-network 'check: startup-network'
289
+
290
+ start_rearm_arm "$home" "$state" "$fakebin" "$armout"
291
+ sleep 0.25
292
+ if is_live_non_zombie "$ARM_PID"; then
293
+ # End the fixture through an ordinary actionable status transition so this
294
+ # failing pre-fix path leaves no child behind.
295
+ printf 'done: fixture cleanup\n' > "$state/cleanup.status"
296
+ wait_for_exit "$ARM_PID" 80 || true
297
+ fail "re-arm stayed live instead of surfacing durable wakes and the still-open remote decision"
298
+ fi
299
+ wait "$ARM_PID"
300
+ status=$?
301
+ expect_code 0 "$status" "re-arm re-surface wake must close successfully"
302
+ grep -F 'check: rearm-resurface' "$armout" >/dev/null \
303
+ || fail "re-arm did not report the durable recovery wake: $(cat "$armout")"
304
+
305
+ # The normal wake-handling drain is the one owner of both queue consumption
306
+ # and the cursor-backed fold. It must expose every queued record and the
307
+ # already-open remote decision without relying on another user message.
308
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$drainout" \
309
+ || fail "drain after re-arm recovery failed"
310
+ grep "$(printf '\tcheck\tremote-reply-ios\t')" "$drainout" >/dev/null \
311
+ || fail "remote-reply wake queued during downtime was not drained"
312
+ grep "$(printf '\tcheck\tstartup-network\t')" "$drainout" >/dev/null \
313
+ || fail "second durable wake queued during downtime was not drained"
314
+ grep -F 'ios [key=remote-signoff] needs-decision: remote secondmate is held for captain sign-off' "$drainout" >/dev/null \
315
+ || fail "remote parent-reply decision was not re-folded after watcher re-arm"
316
+ ack_wakes "$state" || fail "recovery handling acknowledgement failed"
317
+ [ ! -s "$state/.wake-queue" ] || fail "re-arm recovery acknowledgement left durable wakes behind"
318
+
319
+ # Persistent adapters establish a successor after the handling drain. Once
320
+ # the durable wake is acknowledged, that successor must remain live instead
321
+ # of replaying the completed recovery cycle.
322
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/recovery-successor-arm.out"
323
+ is_live_non_zombie "$ARM_PID" || fail "recovery successor did not stay live after the drain"
324
+
325
+ # A later down interval can have no new queue rows at all. The unchanged
326
+ # remote decision must still trigger a recovery wake and be folded again.
327
+ kill "$ARM_PID" 2>/dev/null || true
328
+ wait "$ARM_PID" 2>/dev/null || true
329
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/decision-only-arm.out"
330
+ wait_for_exit "$ARM_PID" 80 || fail "decision-only re-arm did not surface the open decision"
331
+ decision_recovery_arm=$ARM_PID
332
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/decision-handling-successor.out" "$decision_recovery_arm"
333
+ is_live_non_zombie "$ARM_PID" || fail "decision handling successor re-triggered before the drain"
334
+ decision_successor=$ARM_PID
335
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/decision-only-drain.out" \
336
+ 2> "$dir/decision-only-drain.err" || fail "decision-only drain after re-arm recovery failed"
337
+ grep -F 'ios [key=remote-signoff] needs-decision: remote secondmate is held for captain sign-off' \
338
+ "$dir/decision-only-drain.out" >/dev/null \
339
+ || fail "unchanged remote decision was not re-folded after a later down interval"
340
+ sequence=$(sed -n 's/^WAKE_ACK_REQUIRED:.*--ack-through \([0-9][0-9]*\) --recovery-generation [A-Za-z0-9._-][A-Za-z0-9._-]*$/\1/p' "$dir/decision-only-drain.err")
341
+ generation=$(sed -n 's/^WAKE_ACK_REQUIRED:.*--ack-through [0-9][0-9]* --recovery-generation \([A-Za-z0-9._-][A-Za-z0-9._-]*\)$/\1/p' "$dir/decision-only-drain.err")
342
+ [ "$sequence" = 0 ] && [ -n "$generation" ] \
343
+ || fail "decision-only recovery did not require generation-bound post-handling acknowledgement"
344
+ is_live_non_zombie "$decision_successor" \
345
+ || fail "decision-only drain spuriously re-triggered its live handling successor"
346
+ ! grep -F 'check: rearm-resurface' "$dir/decision-handling-successor.out" >/dev/null \
347
+ || fail "decision-only handling successor emitted recursive recovery"
348
+
349
+ kill -TERM "$decision_successor" 2>/dev/null || fail "could not interrupt decision handling successor"
350
+ wait "$decision_successor" 2>/dev/null || true
351
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/interrupted-decision-arm.out"
352
+ wait_for_exit "$ARM_PID" 80 || fail "interrupted decision handling was not recovered on successor re-arm"
353
+ grep -F 'check: rearm-resurface' "$dir/interrupted-decision-arm.out" >/dev/null \
354
+ || fail "successor did not re-surface the unacknowledged decision recovery"
355
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/replayed-decision-drain.out" \
356
+ 2> "$dir/replayed-decision-drain.err" || fail "replayed decision recovery drain failed"
357
+ grep -F 'ios [key=remote-signoff] needs-decision: remote secondmate is held for captain sign-off' \
358
+ "$dir/replayed-decision-drain.out" >/dev/null \
359
+ || fail "interrupted decision recovery did not re-fold the open decision"
360
+ sequence=$(sed -n 's/^WAKE_ACK_REQUIRED:.*--ack-through \([0-9][0-9]*\) --recovery-generation [A-Za-z0-9._-][A-Za-z0-9._-]*$/\1/p' "$dir/replayed-decision-drain.err")
361
+ generation=$(sed -n 's/^WAKE_ACK_REQUIRED:.*--ack-through [0-9][0-9]* --recovery-generation \([A-Za-z0-9._-][A-Za-z0-9._-]*\)$/\1/p' "$dir/replayed-decision-drain.err")
362
+ [ "$sequence" = 0 ] && [ -n "$generation" ] \
363
+ || fail "replayed decision recovery omitted its current acknowledgement generation"
364
+ FM_STATE_OVERRIDE="$state" "$DRAIN" --ack-through "$sequence" --recovery-generation "$generation" \
365
+ || fail "completed decision handling could not acknowledge current recovery"
366
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/decision-successor-arm.out"
367
+ is_live_non_zombie "$ARM_PID" || fail "acknowledged decision recovery did not leave a live successor"
368
+ kill "$ARM_PID" 2>/dev/null || true
369
+ wait "$ARM_PID" 2>/dev/null || true
370
+ pass "watch-arm: re-arm surfaces every queued wake and an open remote decision after downtime"
371
+ }
372
+
373
+ test_marker_publish_failure_retains_recovery_evidence() {
374
+ local dir home state fakebin first_arm watcher_pid armout
375
+ dir=$(make_case downtime-marker-publish-failure)
376
+ home="$dir/home"
377
+ state="$dir/state"
378
+ fakebin="$dir/fakebin"
379
+ mkdir -p "$home/data"
380
+
381
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/first-arm.out"
382
+ first_arm=$ARM_PID
383
+ is_live_non_zombie "$first_arm" || fail "marker-failure fixture watcher did not stay live"
384
+ watcher_pid=$(cat "$state/.watch.lock/pid" 2>/dev/null || true)
385
+ mkdir "$state/.watcher-down"
386
+ kill -TERM "$watcher_pid" 2>/dev/null || fail "could not stop marker-failure fixture watcher"
387
+ wait "$first_arm" 2>/dev/null || true
388
+
389
+ [ "$(cat "$state/.watch.lock/pid" 2>/dev/null || true)" = "$watcher_pid" ] \
390
+ || fail "marker publication failure discarded stale-lock recovery evidence"
391
+ ! is_live_non_zombie "$watcher_pid" \
392
+ || fail "marker-failure fixture watcher remained live"
393
+
394
+ rmdir "$state/.watcher-down"
395
+ armout="$dir/recovery-arm.out"
396
+ start_rearm_arm "$home" "$state" "$fakebin" "$armout"
397
+ wait_for_exit "$ARM_PID" 80 || fail "stale-lock recovery did not surface downtime"
398
+ grep -F 'check: rearm-resurface' "$armout" >/dev/null \
399
+ || fail "stale-lock recovery did not emit the recovery wake: $(cat "$armout")"
400
+ pass "watch-arm: marker publication failure retains stale-lock recovery evidence"
401
+ }
402
+
403
+ test_delivery_gap_wake_is_recovered_once() {
404
+ local dir home state fakebin first_arm
405
+ dir=$(make_case delivery-gap-recovery)
406
+ home="$dir/home"
407
+ state="$dir/state"
408
+ fakebin="$dir/fakebin"
409
+ mkdir -p "$home/data"
410
+
411
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/first-arm.out"
412
+ first_arm=$ARM_PID
413
+ is_live_non_zombie "$first_arm" || fail "delivery-gap fixture watcher did not stay live"
414
+ printf 'done: first delivered wake\n' > "$state/first.status"
415
+ wait_for_exit "$first_arm" 120 || fail "first watcher did not deliver its status wake"
416
+ grep -q '^signal:' "$dir/first-arm.out" \
417
+ || fail "first watcher did not report its delivered wake"
418
+
419
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/first-drain.out" \
420
+ || fail "first handling drain failed"
421
+ ack_wakes "$state" || fail "first handling acknowledgement failed"
422
+ append_wake "$state" check startup-network 'check: startup-network during handling gap'
423
+
424
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/gap-arm.out"
425
+ wait_for_exit "$ARM_PID" 80 || fail "successor missed the wake queued in the delivery gap"
426
+ grep -F 'check: rearm-resurface' "$dir/gap-arm.out" >/dev/null \
427
+ || fail "delivery-gap successor did not emit one recovery wake: $(cat "$dir/gap-arm.out")"
428
+
429
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/gap-drain.out" \
430
+ || fail "delivery-gap recovery drain failed"
431
+ grep "$(printf '\tcheck\tstartup-network\t')" "$dir/gap-drain.out" >/dev/null \
432
+ || fail "wake queued in the delivery gap was not drained"
433
+ ack_wakes "$state" || fail "delivery-gap handling acknowledgement failed"
434
+
435
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/stable-successor.out"
436
+ is_live_non_zombie "$ARM_PID" || fail "successor looped after the delivery gap was drained"
437
+ kill "$ARM_PID" 2>/dev/null || true
438
+ wait "$ARM_PID" 2>/dev/null || true
439
+ pass "watch-arm: a wake queued after handling drain is recovered once at successor arm"
440
+ }
441
+
442
+ test_interrupted_handling_is_redrained_on_rearm() {
443
+ local dir home state fakebin first_arm recovery_arm sequence generation handling_watcher_pid handling_generation generation_replay
444
+ dir=$(make_case interrupted-handling-redrain)
445
+ home="$dir/home"
446
+ state="$dir/state"
447
+ fakebin="$dir/fakebin"
448
+ mkdir -p "$home/data"
449
+
450
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/first-arm.out"
451
+ first_arm=$ARM_PID
452
+ is_live_non_zombie "$first_arm" || fail "interrupted-handling fixture watcher did not stay live"
453
+ printf 'done: wake whose handling is interrupted\n' > "$state/interrupted.status"
454
+ wait_for_exit "$first_arm" 120 || fail "fixture watcher did not deliver its wake"
455
+ grep "$(printf '\tsignal\tinterrupted.status\t')" "$state/.wake-queue" >/dev/null \
456
+ || fail "delivered wake was not durable before handling"
457
+
458
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/crash-gap-recovery-arm.out"
459
+ wait_for_exit "$ARM_PID" 80 || fail "re-arm after a pre-successor crash stranded the durable wake"
460
+ recovery_arm=$ARM_PID
461
+ grep -F 'check: rearm-resurface' "$dir/crash-gap-recovery-arm.out" >/dev/null \
462
+ || fail "re-arm after a pre-successor crash did not re-surface the durable wake"
463
+ grep "$(printf '\tsignal\tinterrupted.status\t')" "$state/.wake-queue" >/dev/null \
464
+ || fail "pre-successor crash recovery removed the unacknowledged durable wake"
465
+ case "$(cat "$state/.watcher-down" 2>/dev/null || true)" in
466
+ pending:downtime:*|announced:downtime:*) ;;
467
+ *) fail "reason emission marked recovery handled before a successor was established" ;;
468
+ esac
469
+ generation_before=$(recovery_marker_generation "$state/.watcher-down")
470
+ [ -n "$generation_before" ] || fail "crash-gap recovery left no recovery generation"
471
+
472
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/reason-emit-crash-replay.out"
473
+ wait_for_exit "$ARM_PID" 80 || fail "a crash after reason emission stranded the durable wake"
474
+ recovery_arm=$ARM_PID
475
+ grep -F 'check: rearm-resurface' "$dir/reason-emit-crash-replay.out" >/dev/null \
476
+ || fail "a crash after reason emission did not re-drain recovery"
477
+ generation_replay=$(recovery_marker_generation "$state/.watcher-down")
478
+ [ -n "$generation_replay" ] \
479
+ || fail "reason-emission replay left no recovery generation"
480
+ grep "$(printf '\tsignal\tinterrupted.status\t')" "$state/.wake-queue" >/dev/null \
481
+ || fail "reason-emission replay removed the unacknowledged durable wake"
482
+
483
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/handling-successor-arm.out" "$recovery_arm"
484
+ is_live_non_zombie "$ARM_PID" \
485
+ || fail "expected handling successor looped on the pending durable wake"
486
+ case "$(cat "$state/.watcher-down" 2>/dev/null || true)" in
487
+ announced:downtime:*|pending:downtime:*) ;;
488
+ *) fail "successor launch marked recovery handled before prompt delivery" ;;
489
+ esac
490
+ handling_generation=$(recovery_marker_generation "$state/.watcher-down")
491
+ handling_watcher_pid=$(sed -n 's/^watcher: started pid=\([0-9][0-9]*\).* recovery-generation=.*$/\1/p' "$dir/handling-successor-arm.out")
492
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$WATCH_ARM" --handling-delivered "$handling_generation" \
493
+ --watcher-pid "$handling_watcher_pid" \
494
+ || fail "confirmed prompt delivery did not begin handling"
495
+ case "$(cat "$state/.watcher-down" 2>/dev/null || true)" in
496
+ pending:handling:"$handling_generation"|announced:handling:"$handling_generation") ;;
497
+ *) fail "confirmed prompt delivery did not transition its recovery generation" ;;
498
+ esac
499
+ ! grep -F 'check: rearm-resurface' "$dir/handling-successor-arm.out" >/dev/null \
500
+ || fail "expected handling successor emitted a recursive recovery wake"
501
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/interrupted-drain.out" \
502
+ 2> "$dir/interrupted-drain.err" || fail "handling drain did not expose the durable wake"
503
+ grep "$(printf '\tsignal\tinterrupted.status\t')" "$dir/interrupted-drain.out" >/dev/null \
504
+ || fail "handling drain did not present the durable wake"
505
+ grep "$(printf '\tsignal\tinterrupted.status\t')" "$state/.wake-queue" >/dev/null \
506
+ || fail "interrupted handling removed the unacknowledged durable wake"
507
+ is_live_non_zombie "$ARM_PID" || fail "handling drain stopped its live successor"
508
+
509
+ kill -TERM "$ARM_PID" 2>/dev/null || fail "could not interrupt the handling successor"
510
+ wait "$ARM_PID" 2>/dev/null || true
511
+ case "$(cat "$state/.watcher-down" 2>/dev/null || true)" in
512
+ pending:downtime:*|announced:downtime:*) ;;
513
+ *) fail "interrupted pre-handling successor did not persist downtime recovery" ;;
514
+ esac
515
+
516
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/recovery-arm.out"
517
+ wait_for_exit "$ARM_PID" 80 || fail "successor after interruption did not re-surface the pending wake"
518
+ grep -F 'check: rearm-resurface' "$dir/recovery-arm.out" >/dev/null \
519
+ || fail "successor after interruption did not emit durable recovery"
520
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/replay-drain.out" \
521
+ 2> "$dir/replay-drain.err" || fail "successor could not re-drain the interrupted wake"
522
+ grep "$(printf '\tsignal\tinterrupted.status\t')" "$dir/replay-drain.out" >/dev/null \
523
+ || fail "successor did not re-drain the still-durable wake"
524
+ sequence=$(sed -n 's/^WAKE_ACK_REQUIRED:.*--ack-through \([0-9][0-9]*\) --recovery-generation [A-Za-z0-9._-][A-Za-z0-9._-]*$/\1/p' "$dir/replay-drain.err")
525
+ generation=$(sed -n 's/^WAKE_ACK_REQUIRED:.*--ack-through [0-9][0-9]* --recovery-generation \([A-Za-z0-9._-][A-Za-z0-9._-]*\)$/\1/p' "$dir/replay-drain.err")
526
+ [ -n "$sequence" ] && [ -n "$generation" ] \
527
+ || fail "re-drain did not emit a generation-bound post-handling acknowledgement command"
528
+ FM_STATE_OVERRIDE="$state" "$DRAIN" --ack-through "$sequence" \
529
+ --recovery-generation "$generation" \
530
+ || fail "completed replay could not acknowledge the handled wake"
531
+ [ ! -s "$state/.wake-queue" ] || fail "acknowledged replay remained in the durable queue"
532
+ pass "watch-arm: interrupted handling leaves its wake durable for successor re-drain"
533
+ }
534
+
535
+ test_malformed_marker_is_quarantined_once() {
536
+ local dir home state fakebin invalid_count
537
+ dir=$(make_case malformed-downtime-marker)
538
+ home="$dir/home"
539
+ state="$dir/state"
540
+ fakebin="$dir/fakebin"
541
+ mkdir -p "$home/data" "$state/.watcher-down"
542
+ printf 'foreign state\n' > "$state/.watcher-down/payload"
543
+
544
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/recovery-arm.out"
545
+ wait_for_exit "$ARM_PID" 80 || fail "malformed marker did not produce a bounded recovery wake"
546
+ grep -F 'check: rearm-resurface' "$dir/recovery-arm.out" >/dev/null \
547
+ || fail "malformed marker did not emit the recovery wake"
548
+ invalid_count=$(find "$state" -maxdepth 1 -type d -name '.watcher-down.invalid.*' | wc -l | tr -d '[:space:]')
549
+ [ "$invalid_count" -eq 1 ] || fail "malformed marker was not quarantined exactly once"
550
+
551
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/recovery-drain.out" \
552
+ || fail "malformed-marker recovery drain failed"
553
+ ack_wakes "$state" || fail "malformed-marker handling acknowledgement failed"
554
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/stable-successor.out"
555
+ is_live_non_zombie "$ARM_PID" || fail "malformed marker caused a persistent recovery loop"
556
+ kill "$ARM_PID" 2>/dev/null || true
557
+ wait "$ARM_PID" 2>/dev/null || true
558
+ pass "watch-arm: malformed recovery state is quarantined without a successor loop"
559
+ }
560
+
561
+ test_recovery_consumption_serializes_queue_publication() {
562
+ local dir home state fakebin
563
+ dir=$(make_case recovery-consumption-race)
564
+ home="$dir/home"
565
+ state="$dir/state"
566
+ fakebin="$dir/fakebin"
567
+ mkdir -p "$home/data"
568
+ printf 'acked:handling:fixture\n' > "$state/.watcher-down"
569
+
570
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/arm.out"
571
+ is_live_non_zombie "$ARM_PID" || fail "acknowledged recovery fixture did not remain live"
572
+ append_wake "$state" check startup-network 'check: concurrent startup-network' \
573
+ || fail "concurrent queue publication failed"
574
+ wait_for_exit "$ARM_PID" 80 \
575
+ || fail "watcher missed publication after an acknowledged recovery handoff"
576
+ grep -F 'check: rearm-resurface' "$dir/arm.out" >/dev/null \
577
+ || fail "publisher did not restore recovery evidence"
578
+ grep "$(printf '\tcheck\tstartup-network\t')" "$state/.wake-queue" >/dev/null \
579
+ || fail "publisher did not durably append its wake"
580
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/drain.out" \
581
+ || fail "publisher recovery drain failed"
582
+ grep "$(printf '\tcheck\tstartup-network\t')" "$dir/drain.out" >/dev/null \
583
+ || fail "publisher wake was not surfaced and drained"
584
+ ack_wakes "$state" || fail "publisher handling acknowledgement failed"
585
+ pass "watch-arm: publication after recovery handoff is surfaced"
586
+ }
587
+
588
+ test_restart_preserves_recovery_across_reused_pid_lock() {
589
+ local dir home state fakebin armout unrelated owner
590
+ dir=$(make_case restart-reused-pid-recovery)
591
+ home="$dir/home"
592
+ state="$dir/state"
593
+ fakebin="$dir/fakebin"
594
+ armout="$dir/arm.out"
595
+ owner="$state/.watch.lock.owner.fixture"
596
+ mkdir -p "$home/data" "$owner"
597
+
598
+ sleep 300 &
599
+ unrelated=$!
600
+ printf '%s\n' "$unrelated" > "$owner/pid"
601
+ printf '%s\n' "$home" > "$owner/fm-home"
602
+ printf '%s\n' "$WATCH" > "$owner/watcher-path"
603
+ printf '%s\n' 'reused-pid-does-not-match' > "$owner/pid-identity"
604
+ ln -s "$owner" "$state/.watch.lock"
605
+
606
+ start_rearm_arm "$home" "$state" "$fakebin" "$armout"
607
+ wait_for_exit "$ARM_PID" 80 || fail "restart did not surface recovery after clearing a reused-pid lock"
608
+ grep -F 'check: rearm-resurface' "$armout" >/dev/null \
609
+ || fail "restart cleared reused-pid lock evidence without a recovery wake: $(cat "$armout")"
610
+ is_live_non_zombie "$unrelated" || fail "restart signaled the unrelated process whose pid was reused"
611
+ kill "$unrelated" 2>/dev/null || true
612
+ wait "$unrelated" 2>/dev/null || true
613
+ pass "watch-arm: restart publishes recovery before clearing a reused-pid watcher lock"
614
+ }
615
+
616
+ test_markerless_legacy_queue_is_recovered_on_arm() {
617
+ local dir home state fakebin row
618
+ dir=$(make_case markerless-legacy-arm)
619
+ home="$dir/home"
620
+ state="$dir/state"
621
+ fakebin="$dir/fakebin"
622
+ mkdir -p "$home/data"
623
+ row=$(printf '1700000000\t7\tcheck\tlegacy-process-event\tcheck: legacy process-event')
624
+ printf '%s\n' "$row" > "$state/.wake-queue"
625
+
626
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/arm.out"
627
+ wait_for_exit "$ARM_PID" 80 || fail "markerless legacy queue was stranded at re-arm"
628
+ grep -F 'check: rearm-resurface' "$dir/arm.out" >/dev/null \
629
+ || fail "markerless legacy queue did not trigger recovery"
630
+ case "$(cat "$state/.watcher-down" 2>/dev/null || true)" in
631
+ pending:downtime:*|announced:downtime:*) ;;
632
+ *) fail "markerless legacy queue was not adopted into downtime recovery" ;;
633
+ esac
634
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/drain.out" \
635
+ || fail "adopted legacy queue could not be drained"
636
+ grep -F "$row" "$dir/drain.out" >/dev/null \
637
+ || fail "adopted legacy wake was not presented"
638
+ ack_wakes "$state" || fail "adopted legacy wake could not be acknowledged"
639
+ pass "watch-arm: markerless legacy queues are adopted and recovered"
640
+ }
641
+
642
+ # Exercise the handling-window recovery invariant owned by
643
+ # docs/watcher-continuity.md through real watcher processes.
644
+ test_handling_window_close_keeps_the_acknowledgement_valid() {
645
+ local dir home state fakebin pair sequence generation
646
+ dir=$(make_case handling-window-close-acknowledgement)
647
+ home="$dir/home"
648
+ state="$dir/state"
649
+ fakebin="$dir/fakebin"
650
+ mkdir -p "$home/data"
651
+
652
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/first-arm.out"
653
+ is_live_non_zombie "$ARM_PID" || fail "handling-window fixture watcher did not stay live"
654
+ printf 'done: wake handled while a watcher cycle closes\n' > "$state/handled.status"
655
+ wait_for_exit "$ARM_PID" 120 || fail "fixture watcher did not deliver its wake"
656
+ grep "$(printf '\tsignal\thandled.status\t')" "$state/.wake-queue" >/dev/null \
657
+ || fail "delivered wake was not durable before handling"
658
+
659
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/drain.out" 2> "$dir/drain.err" \
660
+ || fail "handling drain did not present the durable wake"
661
+ pair=$(drain_ack_pair "$dir/drain.err") \
662
+ || fail "drain did not print a generation-bound acknowledgement command"
663
+ sequence=${pair%%$'\t'*}
664
+ generation=${pair##*$'\t'}
665
+
666
+ # One full watcher cycle appends a wake and then closes inside the handling window.
667
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/handling-window-arm.out"
668
+ is_live_non_zombie "$ARM_PID" || fail "handling-window watcher did not stay live"
669
+ printf 'done: wake published during handling\n' > "$state/during-handling.status"
670
+ wait_for_exit "$ARM_PID" 120 || fail "handling-window watcher did not deliver its wake"
671
+ grep "$(printf '\tsignal\tduring-handling.status\t')" "$state/.wake-queue" >/dev/null \
672
+ || fail "handling-window watcher did not durably append its wake"
673
+
674
+ [ "$(cat "$state/.watcher-down" 2>/dev/null || true)" = "pending:downtime:$generation" ] \
675
+ || fail "repeated publications during handling replaced the outstanding recovery generation"
676
+ FM_STATE_OVERRIDE="$state" "$DRAIN" --ack-through "$sequence" \
677
+ --recovery-generation "$generation" 2> "$dir/ack.err" \
678
+ || fail "the printed acknowledgement was rejected after repeated publications: $(cat "$dir/ack.err")"
679
+ ! grep "$(printf '\tsignal\thandled.status\t')" "$state/.wake-queue" >/dev/null \
680
+ || fail "the acknowledged wake was not consumed"
681
+ grep "$(printf '\tsignal\tduring-handling.status\t')" "$state/.wake-queue" >/dev/null \
682
+ || fail "the newer handling-window wake was over-consumed"
683
+
684
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/remaining-drain.out" \
685
+ 2> "$dir/remaining-drain.err" || fail "remaining wake could not be re-drained"
686
+ pair=$(drain_ack_pair "$dir/remaining-drain.err") \
687
+ || fail "remaining drain did not print an acknowledgement command"
688
+ FM_STATE_OVERRIDE="$state" "$DRAIN" --ack-through "${pair%%$'\t'*}" \
689
+ --recovery-generation "${pair##*$'\t'}" \
690
+ || fail "remaining handling-window wake could not be acknowledged"
691
+ [ ! -s "$state/.wake-queue" ] || fail "remaining wake was not consumed"
692
+ case "$(cat "$state/.watcher-down" 2>/dev/null || true)" in
693
+ acked:*) ;;
694
+ *) fail "the handled recovery episode was not retired" ;;
695
+ esac
696
+
697
+ # The next arm must supervise rather than spend its whole cycle on recovery.
698
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/next-arm.out"
699
+ is_live_non_zombie "$ARM_PID" \
700
+ || fail "the watcher armed after acknowledgement died inside its first cycle"
701
+ ! grep -F 'check: rearm-resurface' "$dir/next-arm.out" >/dev/null \
702
+ || fail "the watcher armed after acknowledgement re-announced a retired recovery"
703
+ printf 'blocked: a later wake the live watcher must still surface\n' > "$state/later.status"
704
+ wait_for_exit "$ARM_PID" 120 || fail "the live watcher did not surface a later wake"
705
+ grep -q '^signal:' "$dir/next-arm.out" \
706
+ || fail "the watcher armed after acknowledgement never reached real supervision work: $(cat "$dir/next-arm.out")"
707
+ pass "watch-arm: a watcher close during handling keeps the printed acknowledgement valid"
708
+ }
709
+
710
+ # Exercise the moved-generation recovery invariant owned by
711
+ # docs/watcher-continuity.md through real watcher processes.
712
+ test_moved_generation_acknowledgement_is_self_healing() {
713
+ local dir home state fakebin pair first_sequence first_generation second_generation
714
+ dir=$(make_case moved-generation-acknowledgement)
715
+ home="$dir/home"
716
+ state="$dir/state"
717
+ fakebin="$dir/fakebin"
718
+ mkdir -p "$home/data"
719
+
720
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/first-arm.out"
721
+ is_live_non_zombie "$ARM_PID" || fail "moved-generation fixture watcher did not stay live"
722
+ printf 'done: first handled wake\n' > "$state/first.status"
723
+ wait_for_exit "$ARM_PID" 120 || fail "fixture watcher did not deliver its first wake"
724
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/first-drain.out" \
725
+ 2> "$dir/first-drain.err" || fail "first drain did not present the durable wake"
726
+ pair=$(drain_ack_pair "$dir/first-drain.err") \
727
+ || fail "first drain did not print a generation-bound acknowledgement command"
728
+ first_sequence=${pair%%$'\t'*}
729
+ first_generation=${pair##*$'\t'}
730
+ FM_STATE_OVERRIDE="$state" "$DRAIN" --ack-through "$first_sequence" \
731
+ --recovery-generation "$first_generation" \
732
+ || fail "the first handled wake could not be acknowledged"
733
+
734
+ # A retired episode does not freeze the generation: the next one is its own.
735
+ start_rearm_arm "$home" "$state" "$fakebin" "$dir/second-arm.out"
736
+ is_live_non_zombie "$ARM_PID" || fail "second fixture watcher did not stay live"
737
+ printf 'done: second wake in a newer recovery episode\n' > "$state/second.status"
738
+ wait_for_exit "$ARM_PID" 120 || fail "second fixture watcher did not deliver its wake"
739
+ second_generation=$(sed -n 's/^pending:downtime:\(.*\)$/\1/p' "$state/.watcher-down")
740
+ [ -n "$second_generation" ] || fail "a wake after acknowledgement did not open a recovery episode"
741
+ [ "$second_generation" != "$first_generation" ] \
742
+ || fail "an acknowledged episode kept its generation instead of opening a new one"
743
+
744
+ # Replaying the stale pair must not fail, must not over-consume, and must not
745
+ # retire the newer episode.
746
+ FM_STATE_OVERRIDE="$state" "$DRAIN" --ack-through "$first_sequence" \
747
+ --recovery-generation "$first_generation" 2> "$dir/stale-ack.err" \
748
+ || fail "a replayed stale acknowledgement was rejected instead of degrading safely"
749
+ if ! grep -F 'WAKE_ACK_REQUIRED' "$dir/stale-ack.err" >/dev/null \
750
+ || ! grep -F 're-run' "$dir/stale-ack.err" >/dev/null; then
751
+ fail "a moved recovery generation did not name its own remedy: $(cat "$dir/stale-ack.err")"
752
+ fi
753
+ grep "$(printf '\tsignal\tsecond.status\t')" "$state/.wake-queue" >/dev/null \
754
+ || fail "a stale acknowledgement consumed a wake above its sequence"
755
+ [ "$(cat "$state/.watcher-down" 2>/dev/null || true)" = "pending:downtime:$second_generation" ] \
756
+ || fail "a stale acknowledgement retired the newer recovery episode"
757
+
758
+ # The sequence alone owns consumption, so the handled rows go even while the
759
+ # generation is stale, and only the episode stays pending.
760
+ FM_STATE_OVERRIDE="$state" "$DRAIN" --ack-through 999 \
761
+ --recovery-generation "$first_generation" 2> "$dir/stale-consume.err" \
762
+ || fail "a stale acknowledgement refused to consume the rows it was given"
763
+ [ ! -s "$state/.wake-queue" ] \
764
+ || fail "a stale acknowledgement left its handled rows on the durable queue"
765
+ [ "$(cat "$state/.watcher-down" 2>/dev/null || true)" = "pending:downtime:$second_generation" ] \
766
+ || fail "row consumption under a stale generation retired the pending episode"
767
+
768
+ # Following the printed remedy closes the episode, so the loop is self-healing.
769
+ FM_HOME="$home" FM_STATE_OVERRIDE="$state" "$DRAIN" > "$dir/redrain.out" \
770
+ 2> "$dir/redrain.err" || fail "the remedy re-drain did not run"
771
+ pair=$(drain_ack_pair "$dir/redrain.err") \
772
+ || fail "the remedy re-drain did not print the newer acknowledgement command"
773
+ FM_STATE_OVERRIDE="$state" "$DRAIN" --ack-through "${pair%%$'\t'*}" \
774
+ --recovery-generation "${pair##*$'\t'}" \
775
+ || fail "the newer recovery episode could not be acknowledged"
776
+ case "$(cat "$state/.watcher-down" 2>/dev/null || true)" in
777
+ acked:*) ;;
778
+ *) fail "following the printed remedy did not retire the newer recovery episode" ;;
779
+ esac
780
+ pass "watch-arm: a moved recovery generation consumes handled rows and names its remedy"
781
+ }
782
+
783
+ test_downtime_marker_does_not_follow_symlink() {
784
+ local dir home state fakebin armout watcher_pid sentinel
785
+ dir=$(make_case downtime-marker-symlink)
786
+ home="$dir/home"
787
+ state="$dir/state"
788
+ fakebin="$dir/fakebin"
789
+ armout="$dir/arm.out"
790
+ sentinel="$dir/sentinel"
791
+ mkdir -p "$home/data"
792
+
793
+ start_rearm_arm "$home" "$state" "$fakebin" "$armout"
794
+ is_live_non_zombie "$ARM_PID" || fail "symlink fixture watcher did not stay live"
795
+ watcher_pid=$(cat "$state/.watch.lock/pid" 2>/dev/null || true)
796
+ printf 'must remain intact\n' > "$sentinel"
797
+ ln -s "$sentinel" "$state/.watcher-down"
798
+ kill -TERM "$watcher_pid" 2>/dev/null || fail "could not stop symlink fixture watcher"
799
+ wait "$ARM_PID" 2>/dev/null || true
800
+
801
+ [ "$(cat "$sentinel")" = "must remain intact" ] \
802
+ || fail "downtime marker publication followed and truncated a symlink"
803
+ [ -f "$state/.watcher-down" ] && [ ! -L "$state/.watcher-down" ] \
804
+ || fail "downtime marker was not safely published as a regular file"
805
+ pass "watch-arm: downtime marker publication does not follow symlinks"
806
+ }
807
+
808
+ test_attached_arm_reports_the_delivered_wake
809
+ test_attached_arm_reports_the_delivered_wake_after_drain
810
+ test_attached_arm_still_fails_on_a_wake_it_did_not_deliver
811
+ test_rearm_resurfaces_durable_queue_and_remote_open_decision
812
+ test_marker_publish_failure_retains_recovery_evidence
813
+ test_delivery_gap_wake_is_recovered_once
814
+ test_interrupted_handling_is_redrained_on_rearm
815
+ test_malformed_marker_is_quarantined_once
816
+ test_recovery_consumption_serializes_queue_publication
817
+ test_restart_preserves_recovery_across_reused_pid_lock
818
+ test_markerless_legacy_queue_is_recovered_on_arm
819
+ test_handling_window_close_keeps_the_acknowledgement_valid
820
+ test_moved_generation_acknowledgement_is_self_healing
821
+ test_downtime_marker_does_not_follow_symlink