@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,712 @@
1
+ #!/usr/bin/env bash
2
+ # tests/fm-startup-network.test.sh - behavior tests for bin/fm-startup-network.sh,
3
+ # the deferred network stage a session start launches instead of running its
4
+ # network work on the blocking path.
5
+ #
6
+ # The session-start suite proves the digest no longer waits and that the deferred
7
+ # sweeps still land. This suite pins the stage's own contract, whose whole job is
8
+ # to make deferral safe:
9
+ # - `start` returns immediately and does not hold the caller's stdout open,
10
+ # which is what would strand a session-open hook behind the worker
11
+ # - a durable acknowledgement after harvest prints a finished result suppresses
12
+ # the wake, while an unacknowledged result always produces one
13
+ # - mutating sweeps are refused when the fleet lock no longer names the session
14
+ # that requested them, and the refusal is reported rather than silent
15
+ # - the aggregate bound turns a wedged sweep into an actionable line
16
+ # - an abandoned `running` record is reported as needing a rerun rather than
17
+ # staying "in progress" forever
18
+ # - single-flight: a second `start` never launches a competing worker
19
+ set -u
20
+
21
+ # shellcheck source=tests/lib.sh
22
+ . "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
23
+
24
+ TMP_ROOT=$(fm_test_tmproot fm-startup-network-tests)
25
+ FM_TEST_CLEANUP_DIRS+=("$TMP_ROOT")
26
+ trap fm_test_cleanup EXIT
27
+
28
+ # new_world <name>: an FM_HOME plus a fake code root whose bin/ is a real
29
+ # firstmate bin/ except for fm-bootstrap.sh, which is replaced by a scriptable
30
+ # stand-in. The stage's contract is about WHEN and WHETHER the network half runs
31
+ # and how its result is published; bin/fm-bootstrap.sh's own behavior is owned by
32
+ # tests/fm-bootstrap.test.sh, so pinning it here would duplicate that owner and
33
+ # make these assertions depend on unrelated tool detection.
34
+ new_world() {
35
+ local name=$1 w home root
36
+ w="$TMP_ROOT/$name"
37
+ home="$w/home"
38
+ root="$w/root"
39
+ mkdir -p "$home/state" "$root/bin"
40
+ for f in "$ROOT"/bin/*.sh; do
41
+ ln -s "$f" "$root/bin/$(basename "$f")"
42
+ done
43
+ rm -f "$root/bin/fm-bootstrap.sh"
44
+ cat > "$root/bin/fm-bootstrap.sh" <<'SH'
45
+ #!/usr/bin/env bash
46
+ # Scriptable stand-in: records how it was invoked, then behaves as the test asks.
47
+ set -u
48
+ printf 'network=%s detect_only=%s\n' \
49
+ "${FM_BOOTSTRAP_NETWORK:-all}" "${FM_BOOTSTRAP_DETECT_ONLY:-0}" \
50
+ >> "${FM_FAKE_BOOTSTRAP_LOG:?}"
51
+ # The real sweeps record their elapsed times through fm-timing-lib.sh, which
52
+ # reaches them as an exported FM_TIMING_LOG. Recording the same way here proves
53
+ # the stage actually hands that channel to its child and publishes what the child
54
+ # wrote - the part of the contract this suite owns. What the real sweeps measure
55
+ # is owned by tests/fm-bootstrap.test.sh.
56
+ if [ -n "${FM_TIMING_LOG:-}" ]; then
57
+ . "$(dirname "$0")/fm-timing-lib.sh"
58
+ fm_timing_record phase "${FM_FAKE_TIMING_PHASE:-gh-auth}" \
59
+ "$(( $(fm_timing_now_ms) - 1500 ))" "${FM_FAKE_TIMING_DETAIL:-}"
60
+ fi
61
+ [ -z "${FM_FAKE_BOOTSTRAP_SLEEP:-}" ] || sleep "$FM_FAKE_BOOTSTRAP_SLEEP"
62
+ [ -z "${FM_FAKE_BOOTSTRAP_OUT:-}" ] || printf '%s\n' "$FM_FAKE_BOOTSTRAP_OUT"
63
+ exit "${FM_FAKE_BOOTSTRAP_RC:-0}"
64
+ SH
65
+ chmod +x "$root/bin/fm-bootstrap.sh"
66
+ cat > "$root/bin/ps" <<'SH'
67
+ #!/usr/bin/env bash
68
+ pid=
69
+ previous=
70
+ for argument in "$@"; do
71
+ [ "$previous" = -p ] && pid=$argument
72
+ previous=$argument
73
+ done
74
+ if [ "$pid" = "${FM_FAKE_HARNESS_PID:-}" ]; then
75
+ case "$*" in
76
+ *comm=*) printf '/usr/local/bin/claude\n' ;;
77
+ *args=*) printf 'claude\n' ;;
78
+ *ppid=*) /bin/ps -o ppid= -p "$pid" ;;
79
+ esac
80
+ else
81
+ /bin/ps "$@"
82
+ fi
83
+ SH
84
+ chmod +x "$root/bin/ps"
85
+ printf '%s|%s|%s\n' "$home" "$root" "$w/bootstrap.log"
86
+ }
87
+
88
+ # The detached worker records itself a moment after `start` returns - that gap is
89
+ # the whole point of not blocking - so a test that wants to observe the worker
90
+ # waits for its record rather than assuming instant publication.
91
+ await_worker_record() { # <home>
92
+ local home=$1 waited=0
93
+ while [ ! -s "$home/state/.startup-network.status" ] && [ "$waited" -lt 100 ]; do
94
+ sleep 0.1
95
+ waited=$((waited + 1))
96
+ done
97
+ [ -s "$home/state/.startup-network.status" ] || fail "the detached worker never recorded itself"
98
+ }
99
+
100
+ test_wait_fails_without_a_published_stage() {
101
+ local rec home root log
102
+ rec=$(new_world wait-without-stage)
103
+ IFS='|' read -r home root log <<EOF
104
+ $rec
105
+ EOF
106
+
107
+ if run_stage "$home" "$root" wait 1 >/dev/null; then
108
+ fail "wait reported success even though no deferred stage had published"
109
+ fi
110
+
111
+ pass "fm-startup-network: wait fails when no deferred stage publishes before its deadline"
112
+ }
113
+
114
+ run_stage() { # <home> <root> <args...>
115
+ local home=$1 root=$2
116
+ shift 2
117
+ PATH="$root/bin:$PATH" FM_FAKE_HARNESS_PID="${FM_FAKE_HARNESS_PID_OVERRIDE:-$$}" \
118
+ FM_HOME="$home" FM_ROOT_OVERRIDE="$root" "$root/bin/fm-startup-network.sh" "$@"
119
+ }
120
+
121
+ wait_for_startup_network_wake() { # <home> [tenths]
122
+ local home=$1 limit=${2:-50} waited=0
123
+ while ! grep -Fq $'check\tstartup-network' "$home/state/.wake-queue" 2>/dev/null \
124
+ && [ "$waited" -lt "$limit" ]; do
125
+ sleep 0.1
126
+ waited=$((waited + 1))
127
+ done
128
+ grep -Fq $'check\tstartup-network' "$home/state/.wake-queue" 2>/dev/null
129
+ }
130
+
131
+ # --- tests -------------------------------------------------------------------
132
+
133
+ # `start` is called from inside a session-open hook whose stdout the harness
134
+ # reads to EOF. A worker that inherited that pipe would hold the session open for
135
+ # exactly as long as the network work it was supposed to get off the critical
136
+ # path, so this asserts both halves: start returns fast, AND the pipe closes
137
+ # while the worker is still running.
138
+ test_start_returns_without_holding_the_callers_stdout() {
139
+ local rec home root log started elapsed pending
140
+ rec=$(new_world start-nonblocking)
141
+ IFS='|' read -r home root log <<EOF
142
+ $rec
143
+ EOF
144
+ printf '%s\n' $$ > "$home/state/.lock"
145
+
146
+ started=$(date +%s)
147
+ # Command substitution reads to EOF, exactly like a hook harvesting hook output.
148
+ FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_SLEEP=10 \
149
+ run_stage "$home" "$root" start --locked 1 --harvest-pid $$ >/dev/null
150
+ elapsed=$(( $(date +%s) - started ))
151
+
152
+ [ "$elapsed" -lt 4 ] || fail "start blocked for ${elapsed}s behind a 10s worker"
153
+ await_worker_record "$home"
154
+ pending=$(run_stage "$home" "$root" report)
155
+ [ "$(printf '%s\n' "$pending" | head -1)" = "IN PROGRESS - the deferred network checks have not finished yet." ] \
156
+ || fail "the worker was not actually still running: $pending"
157
+ assert_contains "$pending" "Only a FAILED or otherwise actionable result arrives as a \`check: startup-network\` wake; a clean success stays silent." \
158
+ "the pending guidance still promised a wake for clean success"
159
+ assert_contains "$pending" "$root/bin/fm-startup-network.sh report" \
160
+ "the pending guidance omitted the durable on-demand report path"
161
+ run_stage "$home" "$root" wait 30 >/dev/null || fail "the worker never published"
162
+ assert_grep 'network=only' "$log" "the worker did not run bootstrap's network-only phase"
163
+ pass "fm-startup-network: start returns immediately and never holds the caller's stdout open"
164
+ }
165
+
166
+ test_harvest_acknowledgement_suppresses_the_wake_and_no_claim_produces_it() {
167
+ local rec home root log claimant output waited=0 worker_pid
168
+ rec=$(new_world claim-handshake)
169
+ IFS='|' read -r home root log <<EOF
170
+ $rec
171
+ EOF
172
+ printf '%s\n' $$ > "$home/state/.lock"
173
+
174
+ sleep 30 &
175
+ claimant=$!
176
+ FM_SESSION_START_TIMEOUT=15 FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_OUT='acknowledged result' \
177
+ run_stage "$home" "$root" start --locked 0 --harvest-pid "$claimant"
178
+ run_stage "$home" "$root" wait 30 >/dev/null || fail "the claimed worker never published"
179
+ worker_pid=$(sed -n 's/^pid=//p' "$home/state/.startup-network.status")
180
+ output=$(run_stage "$home" "$root" harvest --pid "$claimant")
181
+ assert_contains "$output" "acknowledged result" \
182
+ "harvest did not print the finished result it acknowledged"
183
+ [ -s "$home/state/.startup-network.delivered" ] \
184
+ || fail "harvest did not durably acknowledge the result it printed"
185
+ kill "$claimant" 2>/dev/null || true
186
+ wait "$claimant" 2>/dev/null || true
187
+ while kill -0 "$worker_pid" 2>/dev/null && [ "$waited" -lt 50 ]; do
188
+ sleep 0.1
189
+ waited=$((waited + 1))
190
+ done
191
+ ! kill -0 "$worker_pid" 2>/dev/null \
192
+ || fail "the worker did not settle after harvest acknowledged its result"
193
+ [ ! -s "$home/state/.wake-queue" ] \
194
+ || fail "a result harvest acknowledged also queued a wake: $(cat "$home/state/.wake-queue")"
195
+
196
+ # Harvest releases that claim, so the NEXT publication has nobody to print it.
197
+ # An actionable result (not a clean success) is used here so the assertion
198
+ # stays about the claim mechanism; test_a_successful_result_never_queues_a_wake
199
+ # below owns the separate "a clean success never wakes" contract.
200
+ assert_absent "$home/state/.startup-network.claim" "harvest did not release its own claim"
201
+ FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_OUT='MISSING: some-tool (install: brew install some-tool)' \
202
+ run_stage "$home" "$root" run --locked 0
203
+ assert_grep 'check startup-network' "$home/state/.wake-queue" \
204
+ "an unclaimed actionable result never reached the wake queue"
205
+
206
+ : > "$home/state/.wake-queue"
207
+ FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_OUT='MISSING: some-tool (install: brew install some-tool)' \
208
+ run_stage "$home" "$root" start --locked 0 --harvest-pid 999999999
209
+ run_stage "$home" "$root" wait 30 >/dev/null || fail "the dead-claim worker never published"
210
+ wait_for_startup_network_wake "$home" || fail "the dead-claim worker never settled delivery"
211
+ assert_grep 'check startup-network' "$home/state/.wake-queue" \
212
+ "a dead session's stale claim swallowed the result"
213
+ assert_absent "$home/state/.startup-network.claim" "a dead claim was not reaped"
214
+ pass "fm-startup-network: exactly one of the digest and the wake reports each actionable result"
215
+ }
216
+
217
+ test_a_claimant_crash_after_publish_still_queues_the_wake() {
218
+ local rec home root log claimant
219
+ rec=$(new_world claimant-crash)
220
+ IFS='|' read -r home root log <<EOF
221
+ $rec
222
+ EOF
223
+ sleep 10 &
224
+ claimant=$!
225
+ # Actionable output: a clean success in this same crash window must stay
226
+ # silent (test_a_successful_result_never_queues_a_wake owns that case).
227
+ FM_SESSION_START_TIMEOUT=4 FM_FAKE_BOOTSTRAP_LOG="$log" \
228
+ FM_FAKE_BOOTSTRAP_OUT='MISSING: some-tool (install: brew install some-tool)' \
229
+ run_stage "$home" "$root" start --locked 0 --harvest-pid "$claimant"
230
+ run_stage "$home" "$root" wait 30 >/dev/null || fail "the crash-window worker never published"
231
+ kill -0 "$claimant" 2>/dev/null \
232
+ || fail "the claimant died before the worker published"
233
+ kill "$claimant" 2>/dev/null || true
234
+ wait "$claimant" 2>/dev/null || true
235
+ wait_for_startup_network_wake "$home" || fail "the crash-window worker never settled delivery"
236
+ assert_grep 'check startup-network' "$home/state/.wake-queue" \
237
+ "a claimant crash after publication silently lost the result"
238
+ assert_absent "$home/state/.startup-network.delivered" \
239
+ "an unharvested result was recorded as delivered"
240
+ pass "fm-startup-network: a claimant crash after publication still surfaces the result"
241
+ }
242
+
243
+ test_a_report_publication_failure_is_failed_and_still_wakes() {
244
+ local rec home root log claimant output state
245
+ rec=$(new_world report-publication-failure)
246
+ IFS='|' read -r home root log <<EOF
247
+ $rec
248
+ EOF
249
+ mkdir "$home/state/.startup-network.report"
250
+ chmod 500 "$home/state/.startup-network.report"
251
+ sleep 10 &
252
+ claimant=$!
253
+
254
+ FM_SESSION_START_TIMEOUT=4 FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_OUT='unpublishable result' \
255
+ run_stage "$home" "$root" start --locked 0 --harvest-pid "$claimant"
256
+ run_stage "$home" "$root" wait 30 >/dev/null || fail "the report-publication failure never settled"
257
+ state=$(sed -n 's/^state=//p' "$home/state/.startup-network.status")
258
+ [ "$state" = failed ] || fail "a report-publication failure was published as $state"
259
+
260
+ output=$(run_stage "$home" "$root" report)
261
+ assert_contains "$output" "NETWORK_CHECKS: could not publish the deferred check report" \
262
+ "report did not surface the report-publication failure: $output"
263
+ output=$(run_stage "$home" "$root" harvest --pid "$claimant")
264
+ assert_contains "$output" "NETWORK_CHECKS: could not publish the deferred check report" \
265
+ "harvest did not surface the report-publication failure: $output"
266
+ assert_absent "$home/state/.startup-network.delivered" \
267
+ "harvest acknowledged a result whose report was not published"
268
+ wait_for_startup_network_wake "$home" || fail "the report-publication failure suppressed the wake"
269
+ assert_grep 'check startup-network' "$home/state/.wake-queue" \
270
+ "the report-publication failure did not reach the wake queue"
271
+
272
+ kill "$claimant" 2>/dev/null || true
273
+ wait "$claimant" 2>/dev/null || true
274
+ chmod 700 "$home/state/.startup-network.report"
275
+ pass "fm-startup-network: a report-publication failure is failed, diagnosed, and still wakes"
276
+ }
277
+
278
+ # A clean success is not captain-facing progress (AGENTS.md section 8): it must
279
+ # never become a main-blocking wake row, whether or not a session was there to
280
+ # claim and harvest it inline. The result stays durable and readable through
281
+ # `report` either way.
282
+ test_a_successful_result_never_queues_a_wake() {
283
+ local rec home root log claimant report
284
+ rec=$(new_world successful-result-silent)
285
+ IFS='|' read -r home root log <<EOF
286
+ $rec
287
+ EOF
288
+ sleep 10 &
289
+ claimant=$!
290
+ FM_SESSION_START_TIMEOUT=4 FM_FAKE_BOOTSTRAP_LOG="$log" \
291
+ run_stage "$home" "$root" start --locked 0 --harvest-pid "$claimant"
292
+ run_stage "$home" "$root" wait 30 >/dev/null || fail "the unclaimed successful worker never published"
293
+ kill "$claimant" 2>/dev/null || true
294
+ wait "$claimant" 2>/dev/null || true
295
+
296
+ # Give the same settling window the crash-window test uses, then confirm no
297
+ # wake ever lands - not a race that just hasn't finished yet.
298
+ sleep 1
299
+ [ ! -s "$home/state/.wake-queue" ] \
300
+ || fail "a clean successful network-checks result queued a main-blocking wake: $(cat "$home/state/.wake-queue")"
301
+ report=$(run_stage "$home" "$root" report)
302
+ assert_contains "$report" "(silent - no problems found)" \
303
+ "a successful result was not durably readable through report: $report"
304
+
305
+ # Bootstrap itself explicitly types completed benign work as BOOTSTRAP_INFO.
306
+ # That producer-owned no-action record is durable but must remain just as
307
+ # quiet as a fully silent success.
308
+ FM_FAKE_BOOTSTRAP_LOG="$log" \
309
+ FM_FAKE_BOOTSTRAP_OUT='BOOTSTRAP_INFO: fixture completed benign work' \
310
+ run_stage "$home" "$root" run --locked 0
311
+ [ ! -s "$home/state/.wake-queue" ] \
312
+ || fail "a BOOTSTRAP_INFO-only success queued a main-blocking wake: $(cat "$home/state/.wake-queue")"
313
+ report=$(run_stage "$home" "$root" report)
314
+ assert_contains "$report" "BOOTSTRAP_INFO: fixture completed benign work" \
315
+ "the completed no-action fact was not retained in the durable report"
316
+
317
+ pass "fm-startup-network: silent and explicitly informational successes never queue a main-blocking wake"
318
+ }
319
+
320
+ # The FAILED/actionable half of the same contract, paired with the success
321
+ # test above: an actionable report (here, a MISSING: line bootstrap-diagnostics
322
+ # would load a skill for) still reaches the wake queue even when unclaimed.
323
+ test_an_actionable_successful_result_still_queues_a_wake() {
324
+ local rec home root log claimant
325
+ rec=$(new_world actionable-result-wakes)
326
+ IFS='|' read -r home root log <<EOF
327
+ $rec
328
+ EOF
329
+ sleep 10 &
330
+ claimant=$!
331
+ FM_SESSION_START_TIMEOUT=4 FM_FAKE_BOOTSTRAP_LOG="$log" \
332
+ FM_FAKE_BOOTSTRAP_OUT='MISSING: some-tool (install: brew install some-tool)' \
333
+ run_stage "$home" "$root" start --locked 0 --harvest-pid "$claimant"
334
+ run_stage "$home" "$root" wait 30 >/dev/null || fail "the unclaimed actionable worker never published"
335
+ kill "$claimant" 2>/dev/null || true
336
+ wait "$claimant" 2>/dev/null || true
337
+
338
+ wait_for_startup_network_wake "$home" \
339
+ || fail "an actionable successful (state=done) result never queued a wake"
340
+ assert_grep 'check startup-network' "$home/state/.wake-queue" \
341
+ "an actionable result did not reach the wake queue"
342
+
343
+ pass "fm-startup-network: an actionable state=done report still queues a wake"
344
+ }
345
+
346
+ # The worker outlives the command that launched it. If another session took the
347
+ # lock meanwhile, running the mutating sweeps would sweep underneath that
348
+ # session, so they are refused - and the refusal is reported, not silent.
349
+ test_mutating_sweeps_are_refused_when_the_lock_changed_hands() {
350
+ local rec home root log report
351
+ rec=$(new_world lock-changed)
352
+ IFS='|' read -r home root log <<EOF
353
+ $rec
354
+ EOF
355
+ printf '222222\n' > "$home/state/.lock"
356
+
357
+ FM_FAKE_BOOTSTRAP_LOG="$log" run_stage "$home" "$root" run --locked 1 --lock-pid 111111
358
+ assert_grep 'network=only detect_only=1' "$log" \
359
+ "the worker ran mutating sweeps for a lock it no longer held"
360
+ report=$(run_stage "$home" "$root" report)
361
+ assert_contains "$report" "NETWORK_CHECKS: the fleet lock was no longer held" \
362
+ "the downgrade to a read-only probe was not reported"
363
+
364
+ # A detached start captures the lock itself and may run the mutating phase.
365
+ : > "$log"
366
+ printf '%s\n' $$ > "$home/state/.lock"
367
+ FM_FAKE_BOOTSTRAP_LOG="$log" run_stage "$home" "$root" start --locked 1 --harvest-pid $$
368
+ run_stage "$home" "$root" wait 30 >/dev/null || fail "the lock-authorized worker never published"
369
+ assert_grep 'network=only detect_only=0' "$log" \
370
+ "the worker refused sweeps for the very session that still holds the lock"
371
+ pass "fm-startup-network: manual callers cannot forge mutation authority"
372
+ }
373
+
374
+ # The unbounded per-call network work is exactly what could wedge a startup. The
375
+ # stage carries one aggregate bound, and hitting it is an actionable line.
376
+ test_the_stage_bound_is_reported_not_swallowed() {
377
+ local rec home root log report
378
+ rec=$(new_world stage-bound)
379
+ IFS='|' read -r home root log <<EOF
380
+ $rec
381
+ EOF
382
+ printf '%s\n' $$ > "$home/state/.lock"
383
+
384
+ FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_SLEEP=20 FM_STARTUP_NETWORK_TIMEOUT=2 \
385
+ run_stage "$home" "$root" start --locked 1 --harvest-pid $$
386
+ run_stage "$home" "$root" harvest --pid $$ >/dev/null
387
+ FM_STARTUP_NETWORK_TIMEOUT=2 run_stage "$home" "$root" wait 10 >/dev/null \
388
+ || fail "the bounded worker never settled"
389
+ report=$(run_stage "$home" "$root" report)
390
+ assert_contains "$report" "NETWORK_CHECKS: hit the 2s bound before finishing" \
391
+ "a wedged deferred stage was not reported: $report"
392
+ assert_contains "$report" "fm-startup-network.sh run --locked 1" \
393
+ "the timeout line did not say how to rerun the stage"
394
+ wait_for_startup_network_wake "$home" || fail "the timed-out worker never settled delivery"
395
+ assert_grep 'check startup-network' "$home/state/.wake-queue" \
396
+ "a timed-out stage did not surface to the agent"
397
+ pass "fm-startup-network: an aggregate bound turns a wedged sweep into an actionable line"
398
+ }
399
+
400
+ # A worker killed before publication leaves a `running` record behind.
401
+ # That record must read as work to redo, not as work still in flight.
402
+ test_an_abandoned_run_reads_as_needing_a_rerun() {
403
+ local rec home root log report
404
+ rec=$(new_world abandoned)
405
+ IFS='|' read -r home root log <<EOF
406
+ $rec
407
+ EOF
408
+ cat > "$home/state/.startup-network.status" <<EOF
409
+ state=running
410
+ pid=999999999
411
+ started=$(date +%s)
412
+ locked=1
413
+ phases=probe,sweeps
414
+ EOF
415
+
416
+ report=$(run_stage "$home" "$root" report)
417
+ assert_contains "$report" "NETWORK_CHECKS: the deferred check worker stopped before publishing" \
418
+ "an abandoned run still read as in progress: $report"
419
+ assert_contains "$report" "dead-secondmate relaunch" \
420
+ "the abandoned run did not name the checks that never completed"
421
+
422
+ # A record older than the whole aggregate bound is abandoned even when its pid
423
+ # happens to be alive again, so "in progress" can never become permanent.
424
+ cat > "$home/state/.startup-network.status" <<EOF
425
+ state=running
426
+ pid=$$
427
+ started=$(( $(date +%s) - 400 ))
428
+ locked=1
429
+ phases=probe,sweeps
430
+ EOF
431
+ assert_contains "$(FM_STARTUP_NETWORK_TIMEOUT=10 run_stage "$home" "$root" report)" \
432
+ "NETWORK_CHECKS: the deferred check worker stopped before publishing" \
433
+ "a record that outlived the stage bound still read as in progress"
434
+ pass "fm-startup-network: an abandoned run reports as needing a rerun, never as in progress forever"
435
+ }
436
+
437
+ # Two session opens in quick succession must not run the same mutating sweeps
438
+ # concurrently against each other.
439
+ test_start_is_single_flight() {
440
+ local rec home root log runs
441
+ rec=$(new_world single-flight)
442
+ IFS='|' read -r home root log <<EOF
443
+ $rec
444
+ EOF
445
+ printf '%s\n' $$ > "$home/state/.lock"
446
+
447
+ FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_SLEEP=6 \
448
+ run_stage "$home" "$root" start --locked 1 --harvest-pid $$
449
+ await_worker_record "$home"
450
+ FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_SLEEP=6 \
451
+ run_stage "$home" "$root" start --locked 1 --harvest-pid $$
452
+ run_stage "$home" "$root" wait 40 >/dev/null || fail "the worker never published"
453
+
454
+ runs=$(grep -c 'network=only' "$log" || true)
455
+ [ "$runs" -eq 1 ] || fail "a second start launched a competing worker ($runs runs): $(cat "$log")"
456
+ pass "fm-startup-network: a second start never launches a competing worker"
457
+ }
458
+
459
+ test_start_reserves_its_generation_before_returning() {
460
+ local rec home root log report
461
+ rec=$(new_world generation-reservation)
462
+ IFS='|' read -r home root log <<EOF
463
+ $rec
464
+ EOF
465
+ cat > "$home/state/.startup-network.status" <<EOF
466
+ state=done
467
+ pid=999999999
468
+ started=1
469
+ finished=2
470
+ rc=0
471
+ locked=0
472
+ phases=probe
473
+ generation=old
474
+ lock_pid=
475
+ EOF
476
+ printf 'old result\n' > "$home/state/.startup-network.report"
477
+
478
+ FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_SLEEP=5 \
479
+ run_stage "$home" "$root" start --locked 0 --harvest-pid $$
480
+ report=$(run_stage "$home" "$root" harvest --pid $$)
481
+ assert_contains "$report" "IN PROGRESS" \
482
+ "harvest exposed the previous generation after a new start returned: $report"
483
+ assert_not_contains "$report" "old result" \
484
+ "harvest printed a stale generation's report"
485
+ run_stage "$home" "$root" wait 30 >/dev/null || fail "the reserved generation never published"
486
+ pass "fm-startup-network: start atomically reserves the generation harvest observes"
487
+ }
488
+
489
+ test_new_lock_owner_does_not_reuse_the_previous_owners_worker() {
490
+ local rec home root log generation_one generation_two next_owner
491
+ rec=$(new_world owner-handoff)
492
+ IFS='|' read -r home root log <<EOF
493
+ $rec
494
+ EOF
495
+ printf '%s\n' $$ > "$home/state/.lock"
496
+ FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_SLEEP=6 \
497
+ run_stage "$home" "$root" start --locked 1 --harvest-pid $$
498
+ generation_one=$(sed -n 's/^generation=//p' "$home/state/.startup-network.status")
499
+
500
+ next_owner=$(/bin/ps -o ppid= -p $$ | tr -d ' ')
501
+ printf '%s\n' "$next_owner" > "$home/state/.lock"
502
+ FM_FAKE_HARNESS_PID_OVERRIDE="$next_owner" FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_SLEEP=1 \
503
+ run_stage "$home" "$root" start --locked 1 --harvest-pid $$
504
+ generation_two=$(sed -n 's/^generation=//p' "$home/state/.startup-network.status")
505
+ [ "$generation_one" != "$generation_two" ] \
506
+ || fail "the new lock owner reused the previous owner's generation"
507
+ run_stage "$home" "$root" wait 30 >/dev/null || fail "the new owner's generation never published"
508
+ pass "fm-startup-network: a new lock owner gets a distinct worker generation"
509
+ }
510
+
511
+ test_lock_takeover_stays_read_only_while_a_sweep_holds_the_lease() {
512
+ local rec home root log next_owner new_owner out rc started elapsed waited=0
513
+ rec=$(new_world sweep-lease)
514
+ IFS='|' read -r home root log <<EOF
515
+ $rec
516
+ EOF
517
+ printf '%s\n' $$ > "$home/state/.lock"
518
+ FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_SLEEP=6 \
519
+ run_stage "$home" "$root" start --locked 1 --harvest-pid $$
520
+ while [ ! -s "$log" ] && [ "$waited" -lt 50 ]; do
521
+ sleep 0.1
522
+ waited=$((waited + 1))
523
+ done
524
+ [ -s "$log" ] || fail "the mutating sweep never started"
525
+
526
+ next_owner=$(/bin/ps -o ppid= -p $$ | tr -d ' ')
527
+ started=$(date +%s)
528
+ rc=0
529
+ out=$(PATH="$root/bin:$PATH" FM_FAKE_HARNESS_PID="$next_owner" \
530
+ FM_HOME="$home" FM_ROOT_OVERRIDE="$root" "$root/bin/fm-lock.sh" 2>&1) || rc=$?
531
+ elapsed=$(( $(date +%s) - started ))
532
+ [ "$rc" -ne 0 ] || fail "lock takeover succeeded while the prior sweep was mutating"
533
+ [ "$elapsed" -lt 4 ] || fail "lock takeover blocked ${elapsed}s behind deferred network work"
534
+ assert_contains "$out" "operate read-only" \
535
+ "a lease-blocked takeover did not fail closed to read-only: $out"
536
+ [ "$(cat "$home/state/.lock")" = "$$" ] \
537
+ || fail "the lease-blocked takeover replaced the prior owner"
538
+
539
+ run_stage "$home" "$root" wait 30 >/dev/null || fail "the leased sweep never settled"
540
+ out=$(PATH="$root/bin:$PATH" FM_FAKE_HARNESS_PID="$next_owner" \
541
+ FM_HOME="$home" FM_ROOT_OVERRIDE="$root" "$root/bin/fm-lock.sh" 2>&1) \
542
+ || fail "lock takeover still failed after the sweep released its lease"
543
+ new_owner=$(cat "$home/state/.lock")
544
+ assert_contains "$out" "lock acquired: harness pid $new_owner" \
545
+ "the fleet lock did not record the harness owner reported by acquisition"
546
+ [ "$new_owner" != "$$" ] || fail "the prior harness still owned the lock after takeover"
547
+ pass "fm-startup-network: fleet-lock takeover cannot overlap a mutating sweep"
548
+ }
549
+
550
+ # Every record carries a start offset from ONE origin, so the artifact reads as a
551
+ # timeline and not just a bag of durations. The origin is normally exported by the
552
+ # stage, but a process that starts recording without one has to adopt an origin
553
+ # and KEEP it: recomputing it per record would silently flatten every offset to
554
+ # zero and lose the ordering the artifact exists to show. Driven with explicit
555
+ # start stamps so the assertion does not depend on the host clock's resolution.
556
+ test_records_share_one_origin_so_offsets_form_a_timeline() {
557
+ local dir log offsets count second third
558
+ dir="$TMP_ROOT/timing-origin"
559
+ mkdir -p "$dir"
560
+ log="$dir/timings.tsv"
561
+
562
+ (
563
+ # shellcheck source=bin/fm-timing-lib.sh
564
+ . "$ROOT/bin/fm-timing-lib.sh"
565
+ unset FM_TIMING_EPOCH_MS
566
+ FM_TIMING_LOG=$log
567
+ export FM_TIMING_LOG
568
+ base=$(fm_timing_now_ms)
569
+ fm_timing_record phase first "$base"
570
+ fm_timing_record phase second "$(( base + 5000 ))"
571
+ fm_timing_record phase third "$(( base + 9000 ))"
572
+ )
573
+
574
+ # The origin lands within the first record, so that record's own offset rounds
575
+ # to zero; what proves the origin was KEPT is that the later records are spaced
576
+ # by exactly the interval they were given. Recomputing the origin per record
577
+ # would report every one of them as zero.
578
+ offsets=$(awk -F'\t' '$1 == "v1" { print $4 }' "$log")
579
+ count=$(printf '%s\n' "$offsets" | grep -c .)
580
+ second=$(printf '%s\n' "$offsets" | sed -n 2p)
581
+ third=$(printf '%s\n' "$offsets" | sed -n 3p)
582
+ [ "$count" -eq 3 ] || fail "expected three records, got: $offsets"
583
+ [ "$second" -gt 0 ] && [ "$third" -gt "$second" ] \
584
+ || fail "records did not share one origin - offsets were: $offsets"
585
+ [ "$(( third - second ))" -eq 4000 ] \
586
+ || fail "offsets did not preserve the interval between records: $offsets"
587
+ pass "fm-startup-network: timing records share one origin so their offsets form a timeline"
588
+ }
589
+
590
+ # The whole point of the artifact is that it is FREE until someone asks for it.
591
+ # `harvest` is what composes a session start's NETWORK CHECKS section, so a
592
+ # timing line leaking into it would be a change to every startup's output; only
593
+ # the on-demand `report` may print them.
594
+ test_timings_are_published_and_only_the_on_demand_report_prints_them() {
595
+ local rec home root log report_out harvest_out
596
+ rec=$(new_world timings-published)
597
+ IFS='|' read -r home root log <<EOF
598
+ $rec
599
+ EOF
600
+ printf '%s\n' $$ > "$home/state/.lock"
601
+
602
+ FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_OUT='sweep finding' \
603
+ FM_FAKE_TIMING_PHASE=fleet-sync FM_FAKE_TIMING_DETAIL=dotfiles-private \
604
+ run_stage "$home" "$root" run --locked 1
605
+
606
+ assert_present "$home/state/.startup-network.timings" \
607
+ "a finished run published no timing record"
608
+ assert_grep 'fleet-sync' "$home/state/.startup-network.timings" \
609
+ "the stage did not publish what the sweep recorded"
610
+ assert_grep 'stage network-checks' "$home/state/.startup-network.timings" \
611
+ "the stage did not record its own bounded total"
612
+
613
+ report_out=$(run_stage "$home" "$root" report)
614
+ assert_contains "$report_out" "sweep finding" "report stopped printing the sweep result"
615
+ assert_contains "$report_out" "TIMINGS" "report did not print the per-step timings"
616
+ assert_contains "$report_out" "fleet-sync dotfiles-private" \
617
+ "report did not attribute the elapsed time to the clone that spent it"
618
+ assert_contains "$report_out" "slowest:" "report did not surface the slowest steps"
619
+
620
+ harvest_out=$(run_stage "$home" "$root" harvest --pid $$)
621
+ assert_contains "$harvest_out" "sweep finding" "harvest stopped printing the sweep result"
622
+ assert_not_contains "$harvest_out" "TIMINGS" \
623
+ "the timings leaked into the session-start digest section"
624
+ assert_not_contains "$harvest_out" "slowest:" \
625
+ "the timings leaked into the session-start digest section"
626
+ pass "fm-startup-network: timings are durable and printed only on demand"
627
+ }
628
+
629
+ # A run that hit the bound is exactly the run worth attributing, so whatever the
630
+ # killed sweeps managed to record must survive rather than being discarded with
631
+ # them.
632
+ test_a_bounded_run_still_publishes_the_timings_it_managed_to_record() {
633
+ local rec home root log report_out
634
+ rec=$(new_world timings-partial)
635
+ IFS='|' read -r home root log <<EOF
636
+ $rec
637
+ EOF
638
+ printf '%s\n' $$ > "$home/state/.lock"
639
+
640
+ FM_STARTUP_NETWORK_TIMEOUT=1 FM_SESSION_START_TIMEOUT=2 \
641
+ FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_BOOTSTRAP_SLEEP=20 \
642
+ FM_FAKE_TIMING_PHASE=secondmate-liveness FM_FAKE_TIMING_DETAIL='mate-a@host-one' \
643
+ run_stage "$home" "$root" run --locked 1
644
+
645
+ [ "$(sed -n 's/^state=//p' "$home/state/.startup-network.status")" = timeout ] \
646
+ || fail "the bounded run did not record itself as timed out"
647
+ report_out=$(run_stage "$home" "$root" report)
648
+ assert_contains "$report_out" "hit the 1s bound" "the bound stopped being reported"
649
+ assert_contains "$report_out" "secondmate-liveness mate-a@host-one" \
650
+ "a timed-out run discarded the partial timings its sweeps had already recorded"
651
+ pass "fm-startup-network: a timed-out run still publishes the partial timings it recorded"
652
+ }
653
+
654
+ # The artifact is read by a human looking at a slow startup, so it must be
655
+ # incapable of carrying an argv or a credential out of a sweep, and incapable of
656
+ # being broken by one either: a detail with tabs or newlines would otherwise
657
+ # forge extra records.
658
+ test_the_timing_artifact_cannot_carry_a_command_line_or_forge_records() {
659
+ local rec home root log lines report_out
660
+ rec=$(new_world timings-sanitized)
661
+ IFS='|' read -r home root log <<EOF
662
+ $rec
663
+ EOF
664
+ printf '%s\n' $$ > "$home/state/.lock"
665
+
666
+ FM_FAKE_BOOTSTRAP_LOG="$log" FM_FAKE_TIMING_PHASE=secondmate-sync \
667
+ FM_FAKE_TIMING_DETAIL="ssh -i /key host v1 forged 0 9999
668
+ GITHUB_TOKEN=ghp_supersecretvalue" \
669
+ run_stage "$home" "$root" run --locked 1
670
+
671
+ assert_no_grep 'ghp_supersecretvalue' "$home/state/.startup-network.timings" \
672
+ "the timing artifact carried a credential-shaped value through"
673
+ assert_no_grep 'forged' "$home/state/.startup-network.timings" \
674
+ "a detail containing tabs forged an extra timing record"
675
+ assert_no_grep 'ssh' "$home/state/.startup-network.timings" \
676
+ "the timing artifact carried a command line through"
677
+ assert_grep 'unrecordable' "$home/state/.startup-network.timings" \
678
+ "free text was silently dropped instead of being marked unrecordable"
679
+ lines=$(grep -c . "$home/state/.startup-network.timings")
680
+ [ "$lines" -eq 2 ] \
681
+ || fail "one sweep record plus the stage total should be 2 lines, got $lines"
682
+
683
+ # The step itself is still measured - only its untrustworthy label is refused,
684
+ # so a sweep that mislabels itself still shows up as time spent.
685
+ assert_grep 'secondmate-sync' "$home/state/.startup-network.timings" \
686
+ "refusing the label also discarded the measurement"
687
+
688
+ report_out=$(run_stage "$home" "$root" report)
689
+ assert_not_contains "$report_out" "ghp_supersecretvalue" \
690
+ "the rendered report printed a credential-shaped value"
691
+ pass "fm-startup-network: the timing artifact cannot carry a command line or forge records"
692
+ }
693
+
694
+ test_wait_fails_without_a_published_stage
695
+ test_start_returns_without_holding_the_callers_stdout
696
+ test_harvest_acknowledgement_suppresses_the_wake_and_no_claim_produces_it
697
+ test_a_claimant_crash_after_publish_still_queues_the_wake
698
+ test_a_report_publication_failure_is_failed_and_still_wakes
699
+ test_a_successful_result_never_queues_a_wake
700
+ test_an_actionable_successful_result_still_queues_a_wake
701
+ test_mutating_sweeps_are_refused_when_the_lock_changed_hands
702
+ test_the_stage_bound_is_reported_not_swallowed
703
+ test_an_abandoned_run_reads_as_needing_a_rerun
704
+ test_start_is_single_flight
705
+ test_start_reserves_its_generation_before_returning
706
+ test_new_lock_owner_does_not_reuse_the_previous_owners_worker
707
+ test_lock_takeover_stays_read_only_while_a_sweep_holds_the_lease
708
+ test_records_share_one_origin_so_offsets_form_a_timeline
709
+ test_timings_are_published_and_only_the_on_demand_report_prints_them
710
+ test_a_bounded_run_still_publishes_the_timings_it_managed_to_record
711
+ test_the_timing_artifact_cannot_carry_a_command_line_or_forge_records
712
+ echo "# fm-startup-network.test.sh: all assertions passed"