@zerwiz/ymir 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (850) hide show
  1. package/.agents/README.md +291 -0
  2. package/.agents/agents/bragi-marketer.md +111 -0
  3. package/.agents/agents/brokk.md +352 -0
  4. package/.agents/agents/forseti-reviewer.md +93 -0
  5. package/.agents/agents/frigg-consent.md +77 -0
  6. package/.agents/agents/groa-update.md +77 -0
  7. package/.agents/agents/hnoss-designer.md +69 -0
  8. package/.agents/agents/huginn-researcher.md +99 -0
  9. package/.agents/agents/jord-projects.md +78 -0
  10. package/.agents/agents/kvasir-scout.md +91 -0
  11. package/.agents/agents/mimir-planner.md +92 -0
  12. package/.agents/agents/muninn-stow.md +77 -0
  13. package/.agents/agents/saga-bearings.md +78 -0
  14. package/.agents/agents/sindri-developer.md +114 -0
  15. package/.agents/agents/snotra-documenter.md +105 -0
  16. package/.agents/agents/syn-recovery.md +78 -0
  17. package/.agents/agents/tyr-check.md +78 -0
  18. package/.agents/agents/urdh-hold.md +77 -0
  19. package/.agents/agents/volundr-smith.md +80 -0
  20. package/.agents/agents/vor-diagnostics.md +77 -0
  21. package/.agents/assets/agents/naming.md +50 -0
  22. package/.agents/assets/agents/registry.md +93 -0
  23. package/.agents/assets/agents/runtime.md +36 -0
  24. package/.agents/assets/agents/toon-tasks-cli.md +42 -0
  25. package/.agents/assets/templates/company_entity.template.md +37 -0
  26. package/.agents/backend/backends/cmux.sh +649 -0
  27. package/.agents/backend/backends/herdr-eventwait.py +157 -0
  28. package/.agents/backend/backends/herdr-workspace-move.py +114 -0
  29. package/.agents/backend/backends/herdr.sh +3386 -0
  30. package/.agents/backend/backends/orca.sh +290 -0
  31. package/.agents/backend/backends/tmux.sh +357 -0
  32. package/.agents/backend/backends/zellij.sh +700 -0
  33. package/.agents/backend/fm-afk-launch.sh +654 -0
  34. package/.agents/backend/fm-afk-return.sh +241 -0
  35. package/.agents/backend/fm-afk-start.sh +172 -0
  36. package/.agents/backend/fm-arm-command-policy.mjs +960 -0
  37. package/.agents/backend/fm-arm-pretool-check.sh +200 -0
  38. package/.agents/backend/fm-backend-hometag-lib.sh +52 -0
  39. package/.agents/backend/fm-backend.sh +981 -0
  40. package/.agents/backend/fm-backlog-handoff.sh +909 -0
  41. package/.agents/backend/fm-backlog-receive.sh +187 -0
  42. package/.agents/backend/fm-backlog-transition-lib.sh +779 -0
  43. package/.agents/backend/fm-bearings-board.sh +204 -0
  44. package/.agents/backend/fm-bearings-snapshot.sh +546 -0
  45. package/.agents/backend/fm-bootstrap.sh +1568 -0
  46. package/.agents/backend/fm-branch-outcome.sh +203 -0
  47. package/.agents/backend/fm-branch-prompt.sh +101 -0
  48. package/.agents/backend/fm-brief.sh +452 -0
  49. package/.agents/backend/fm-busy-event.sh +234 -0
  50. package/.agents/backend/fm-busy-lib.sh +986 -0
  51. package/.agents/backend/fm-captain-hold.sh +1001 -0
  52. package/.agents/backend/fm-cd-command-policy.mjs +153 -0
  53. package/.agents/backend/fm-cd-pretool-check.sh +190 -0
  54. package/.agents/backend/fm-check-lib.sh +74 -0
  55. package/.agents/backend/fm-check-register.sh +42 -0
  56. package/.agents/backend/fm-check-unregister.sh +52 -0
  57. package/.agents/backend/fm-classify-lib.sh +1770 -0
  58. package/.agents/backend/fm-claude-stop-autoarm.sh +334 -0
  59. package/.agents/backend/fm-composer-lib.sh +1416 -0
  60. package/.agents/backend/fm-config-inherit-lib.sh +1202 -0
  61. package/.agents/backend/fm-config-push.sh +254 -0
  62. package/.agents/backend/fm-control-lib.sh +251 -0
  63. package/.agents/backend/fm-control.sh +878 -0
  64. package/.agents/backend/fm-crew-state.sh +625 -0
  65. package/.agents/backend/fm-cursor-lib.sh +243 -0
  66. package/.agents/backend/fm-decision-hold.sh +232 -0
  67. package/.agents/backend/fm-doc-audience-check.sh +269 -0
  68. package/.agents/backend/fm-dod-lib.sh +67 -0
  69. package/.agents/backend/fm-ensure-agents-md.sh +253 -0
  70. package/.agents/backend/fm-extension-launch-barrier.mjs +129 -0
  71. package/.agents/backend/fm-extension.mjs +2577 -0
  72. package/.agents/backend/fm-extension.sh +16 -0
  73. package/.agents/backend/fm-ff-lib.sh +420 -0
  74. package/.agents/backend/fm-fleet-snapshot.sh +1463 -0
  75. package/.agents/backend/fm-fleet-sync.sh +458 -0
  76. package/.agents/backend/fm-fleet-view.sh +96 -0
  77. package/.agents/backend/fm-gate-refuse-lib.sh +102 -0
  78. package/.agents/backend/fm-guard.sh +242 -0
  79. package/.agents/backend/fm-harness.sh +195 -0
  80. package/.agents/backend/fm-herdr-ci-cleanup.sh +115 -0
  81. package/.agents/backend/fm-herdr-lab.sh +346 -0
  82. package/.agents/backend/fm-herdr-session-cleanup.sh +337 -0
  83. package/.agents/backend/fm-home-seed.sh +984 -0
  84. package/.agents/backend/fm-home-summary-refresh.sh +255 -0
  85. package/.agents/backend/fm-hook-host-lib.sh +36 -0
  86. package/.agents/backend/fm-inactive-reconcile.sh +531 -0
  87. package/.agents/backend/fm-inbox.sh +399 -0
  88. package/.agents/backend/fm-install-actionlint.sh +84 -0
  89. package/.agents/backend/fm-install-herdr.sh +96 -0
  90. package/.agents/backend/fm-install-shellcheck.sh +84 -0
  91. package/.agents/backend/fm-install-treehouse.sh +95 -0
  92. package/.agents/backend/fm-kimi-turnend-hook.sh +276 -0
  93. package/.agents/backend/fm-lease-lib.sh +218 -0
  94. package/.agents/backend/fm-lease.sh +189 -0
  95. package/.agents/backend/fm-line-cap-lib.sh +51 -0
  96. package/.agents/backend/fm-lint-workflows.sh +137 -0
  97. package/.agents/backend/fm-lint.sh +591 -0
  98. package/.agents/backend/fm-lock-lib.sh +104 -0
  99. package/.agents/backend/fm-lock.sh +107 -0
  100. package/.agents/backend/fm-marker-lib.sh +12 -0
  101. package/.agents/backend/fm-merge-local.sh +74 -0
  102. package/.agents/backend/fm-merge-outcome-lib.sh +138 -0
  103. package/.agents/backend/fm-nm-run-lib.sh +124 -0
  104. package/.agents/backend/fm-on.sh +125 -0
  105. package/.agents/backend/fm-operational-input.sh +253 -0
  106. package/.agents/backend/fm-peek.sh +46 -0
  107. package/.agents/backend/fm-pending-reply-lib.sh +1409 -0
  108. package/.agents/backend/fm-pr-check.sh +135 -0
  109. package/.agents/backend/fm-pr-lib.sh +1016 -0
  110. package/.agents/backend/fm-pr-merge.sh +707 -0
  111. package/.agents/backend/fm-pr-poll.sh +110 -0
  112. package/.agents/backend/fm-primary-scope-lib.sh +33 -0
  113. package/.agents/backend/fm-procevent-extension-capture.pl +259 -0
  114. package/.agents/backend/fm-procevent-lavish.sh +628 -0
  115. package/.agents/backend/fm-procevent-lib.sh +892 -0
  116. package/.agents/backend/fm-procevent-quota.sh +290 -0
  117. package/.agents/backend/fm-procevent-remote-reply.sh +577 -0
  118. package/.agents/backend/fm-procevent-when.sh +504 -0
  119. package/.agents/backend/fm-procevent.sh +1466 -0
  120. package/.agents/backend/fm-project-mode.sh +92 -0
  121. package/.agents/backend/fm-project-origin-lib.sh +180 -0
  122. package/.agents/backend/fm-promote.sh +282 -0
  123. package/.agents/backend/fm-public-followup-emit.sh +260 -0
  124. package/.agents/backend/fm-public-followup-lib.sh +398 -0
  125. package/.agents/backend/fm-public-followup.sh +1330 -0
  126. package/.agents/backend/fm-push-transition-lib.sh +168 -0
  127. package/.agents/backend/fm-quota-axi-lib.sh +93 -0
  128. package/.agents/backend/fm-quota-choose.sh +384 -0
  129. package/.agents/backend/fm-remote-delta-read.sh +189 -0
  130. package/.agents/backend/fm-remote-doctor.sh +800 -0
  131. package/.agents/backend/fm-remote-entrypoint.sh +193 -0
  132. package/.agents/backend/fm-remote-file.sh +234 -0
  133. package/.agents/backend/fm-remote-home-provision.sh +259 -0
  134. package/.agents/backend/fm-remote-home-seed.sh +259 -0
  135. package/.agents/backend/fm-remote-inherit-push.sh +90 -0
  136. package/.agents/backend/fm-remote-inherit.sh +179 -0
  137. package/.agents/backend/fm-remote-job-lib.sh +1241 -0
  138. package/.agents/backend/fm-remote-job-reap-orphans.sh +137 -0
  139. package/.agents/backend/fm-remote-job-worker.sh +1124 -0
  140. package/.agents/backend/fm-remote-readiness-lib.sh +44 -0
  141. package/.agents/backend/fm-remote-secondmate-control.sh +340 -0
  142. package/.agents/backend/fm-review-diff.sh +158 -0
  143. package/.agents/backend/fm-secondmate-charter-lib.sh +44 -0
  144. package/.agents/backend/fm-secondmate-nudge-lib.sh +68 -0
  145. package/.agents/backend/fm-secondmate-parent-lib.sh +70 -0
  146. package/.agents/backend/fm-secondmate-reconcile.sh +371 -0
  147. package/.agents/backend/fm-secondmate-registry-lib.sh +311 -0
  148. package/.agents/backend/fm-secondmate-report.sh +88 -0
  149. package/.agents/backend/fm-send.sh +1059 -0
  150. package/.agents/backend/fm-session-lock-lib.sh +176 -0
  151. package/.agents/backend/fm-session-start.sh +970 -0
  152. package/.agents/backend/fm-sessionstart-cursor.sh +40 -0
  153. package/.agents/backend/fm-sessionstart-nudge.sh +45 -0
  154. package/.agents/backend/fm-sessionstart-run.sh +145 -0
  155. package/.agents/backend/fm-spawn.sh +3153 -0
  156. package/.agents/backend/fm-startup-memory-budget-lib.sh +224 -0
  157. package/.agents/backend/fm-startup-memory-budget.sh +94 -0
  158. package/.agents/backend/fm-startup-network.sh +642 -0
  159. package/.agents/backend/fm-stow-cascade.sh +251 -0
  160. package/.agents/backend/fm-subagent-pretool-check.sh +207 -0
  161. package/.agents/backend/fm-supervise-daemon.sh +1732 -0
  162. package/.agents/backend/fm-supervision-instructions.sh +215 -0
  163. package/.agents/backend/fm-supervision-lib.sh +90 -0
  164. package/.agents/backend/fm-supervisor-target-lib.sh +78 -0
  165. package/.agents/backend/fm-tangle-lib.sh +53 -0
  166. package/.agents/backend/fm-task-inbox-lib.sh +402 -0
  167. package/.agents/backend/fm-tasks-axi-lib.sh +123 -0
  168. package/.agents/backend/fm-teardown.sh +2922 -0
  169. package/.agents/backend/fm-test-isolation-proof.sh +588 -0
  170. package/.agents/backend/fm-test-run.sh +2191 -0
  171. package/.agents/backend/fm-timeout-lib.sh +141 -0
  172. package/.agents/backend/fm-timing-lib.sh +179 -0
  173. package/.agents/backend/fm-tmux-lib.sh +291 -0
  174. package/.agents/backend/fm-tool-update-check.sh +898 -0
  175. package/.agents/backend/fm-trace-context-lib.sh +227 -0
  176. package/.agents/backend/fm-transition-lib.sh +103 -0
  177. package/.agents/backend/fm-turnend-guard-cursor.sh +391 -0
  178. package/.agents/backend/fm-turnend-guard-grok.sh +90 -0
  179. package/.agents/backend/fm-turnend-guard.sh +432 -0
  180. package/.agents/backend/fm-update.sh +110 -0
  181. package/.agents/backend/fm-vendor-auth-probe.sh +190 -0
  182. package/.agents/backend/fm-voice-client.py +1373 -0
  183. package/.agents/backend/fm-voice-relay.py +1256 -0
  184. package/.agents/backend/fm-wake-drain.sh +592 -0
  185. package/.agents/backend/fm-wake-grant.sh +127 -0
  186. package/.agents/backend/fm-wake-lib.sh +1849 -0
  187. package/.agents/backend/fm-watch-arm.sh +602 -0
  188. package/.agents/backend/fm-watch-checkpoint.sh +109 -0
  189. package/.agents/backend/fm-watch.sh +1962 -0
  190. package/.agents/backend/fm-x-dismiss.sh +116 -0
  191. package/.agents/backend/fm-x-followup.sh +286 -0
  192. package/.agents/backend/fm-x-lib.sh +1000 -0
  193. package/.agents/backend/fm-x-link.sh +234 -0
  194. package/.agents/backend/fm-x-poll.sh +185 -0
  195. package/.agents/backend/fm-x-reply.sh +385 -0
  196. package/.agents/backend/fm_voice_frame.py +166 -0
  197. package/.agents/backend/fm_voice_records.py +574 -0
  198. package/.agents/backend/model-bridge.py +290 -0
  199. package/.agents/backend/opencode-go-bridge.py +30 -0
  200. package/.agents/bus/README.md +12 -0
  201. package/.agents/bus/messages.json +1 -0
  202. package/.agents/config/agents.machine.example.yaml +16 -0
  203. package/.agents/config/agents.yaml +50 -0
  204. package/.agents/config/agents.yaml.example +72 -0
  205. package/.agents/config/crew-dispatch.json +22 -0
  206. package/.agents/config/cron.yaml +9 -0
  207. package/.agents/config/eindri-dispatch.json +67 -0
  208. package/.agents/config/eindri-harness +1 -0
  209. package/.agents/config/startup-memory-budget +1 -0
  210. package/.agents/config/tailscale-sync.yaml +27 -0
  211. package/.agents/filebrowser/.filebrowser.json +6 -0
  212. package/.agents/filebrowser/README.md +11 -0
  213. package/.agents/gateway/README.md +12 -0
  214. package/.agents/github/README.md +15 -0
  215. package/.agents/github/webhooks/.gitkeep +0 -0
  216. package/.agents/github/workflows/.gitkeep +0 -0
  217. package/.agents/harness/opencode/plugins/lib/rodd-operational-input.js +37 -0
  218. package/.agents/harness/opencode/plugins/package.json +4 -0
  219. package/.agents/harness/opencode/plugins/saga-sessionstart.js +77 -0
  220. package/.agents/harness/opencode/plugins/syn-cd-check.js +61 -0
  221. package/.agents/harness/opencode/plugins/syn-pretool-check.js +68 -0
  222. package/.agents/harness/opencode/plugins/syn-turnend-guard.js +105 -0
  223. package/.agents/harness/opencode/plugins/syn-watch-arm.js +521 -0
  224. package/.agents/memory/README.md +46 -0
  225. package/.agents/memory/kaia.engram +0 -0
  226. package/.agents/memory/kaia.engram-shm +0 -0
  227. package/.agents/memory/kaia.engram-wal +0 -0
  228. package/.agents/memory/runes_audit.md +5 -0
  229. package/.agents/memory/well/episodes.jsonl +367 -0
  230. package/.agents/memory/well/workspace.jsonl +27 -0
  231. package/.agents/migrations/0001-hodd-layout.sh +38 -0
  232. package/.agents/migrations/0002-a2a-mcp.sh +27 -0
  233. package/.agents/migrations/0003-private-data-separation.sh +111 -0
  234. package/.agents/migrations/0004-hoard-and-realms.sh +104 -0
  235. package/.agents/sandbox/Dockerfile.utgard +19 -0
  236. package/.agents/sandbox/README.md +14 -0
  237. package/.agents/sandbox/utgard.config.json +14 -0
  238. package/.agents/skills/README.md +59 -0
  239. package/.agents/skills/bragi-marketing/README.md +3 -0
  240. package/.agents/skills/bragi-marketing/SKILL.md +82 -0
  241. package/.agents/skills/eindri-homes/README.md +3 -0
  242. package/.agents/skills/eindri-homes/SKILL.md +291 -0
  243. package/.agents/skills/eindri-homes/assets/control-plane.md +122 -0
  244. package/.agents/skills/eindri-homes/assets/dispatch.md +80 -0
  245. package/.agents/skills/frigg-consent/README.md +3 -0
  246. package/.agents/skills/frigg-consent/SKILL.md +56 -0
  247. package/.agents/skills/galdr-ymirsystem/README.md +3 -0
  248. package/.agents/skills/galdr-ymirsystem/SKILL.md +158 -0
  249. package/.agents/skills/galdr-ymirsystem/assets/README.md +74 -0
  250. package/.agents/skills/galdr-ymirsystem/assets/brokk-distro-runtime.md +390 -0
  251. package/.agents/skills/galdr-ymirsystem/assets/build-method.md +113 -0
  252. package/.agents/skills/galdr-ymirsystem/assets/build-tool-categories.md +178 -0
  253. package/.agents/skills/galdr-ymirsystem/assets/eindri-orchestration.md +518 -0
  254. package/.agents/skills/galdr-ymirsystem/assets/eindri-profiles.md +136 -0
  255. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +673 -0
  256. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/claude-code.md +197 -0
  257. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/codex.md +191 -0
  258. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/cursor.md +193 -0
  259. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/opencode.md +262 -0
  260. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/pi.md +295 -0
  261. package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +496 -0
  262. package/.agents/skills/galdr-ymirsystem/assets/installation.md +571 -0
  263. package/.agents/skills/galdr-ymirsystem/assets/local-models/HOST-DATA.example.md +72 -0
  264. package/.agents/skills/galdr-ymirsystem/assets/local-models.md +274 -0
  265. package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +161 -0
  266. package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +377 -0
  267. package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
  268. package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +129 -0
  269. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/einherjar-spawn.schema.json +81 -0
  270. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/herdr-profile.toml +75 -0
  271. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +96 -0
  272. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/supervision-tree.yml +163 -0
  273. package/.agents/skills/galdr-ymirsystem/assets/pi-boot-guide.md +58 -0
  274. package/.agents/skills/galdr-ymirsystem/assets/porting-upstream-to-norse.md +301 -0
  275. package/.agents/skills/galdr-ymirsystem/assets/principles.md +385 -0
  276. package/.agents/skills/galdr-ymirsystem/assets/reference-adoption.md +99 -0
  277. package/.agents/skills/galdr-ymirsystem/assets/registry.md +379 -0
  278. package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +463 -0
  279. package/.agents/skills/galdr-ymirsystem/assets/runtime-components.md +553 -0
  280. package/.agents/skills/galdr-ymirsystem/assets/smidja.md +175 -0
  281. package/.agents/skills/galdr-ymirsystem/schemas/toon-schemas.md +190 -0
  282. package/.agents/skills/galdr-ymirsystem/scripts/README.md +45 -0
  283. package/.agents/skills/galdr-ymirsystem/scripts/bench-one.sh +248 -0
  284. package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +360 -0
  285. package/.agents/skills/galdr-ymirsystem/scripts/gpu-sample.sh +43 -0
  286. package/.agents/skills/galdr-ymirsystem/scripts/stop-all.sh +34 -0
  287. package/.agents/skills/galdr-ymirsystem/scripts/toon-check.py +188 -0
  288. package/.agents/skills/gjallarhorn-relay/README.md +3 -0
  289. package/.agents/skills/gjallarhorn-relay/SKILL.md +283 -0
  290. package/.agents/skills/groa-update/SKILL.md +70 -0
  291. package/.agents/skills/hamr-adapters/README.md +3 -0
  292. package/.agents/skills/hamr-adapters/SKILL.md +123 -0
  293. package/.agents/skills/hamr-adapters/references/common/control-and-recovery.md +37 -0
  294. package/.agents/skills/hamr-adapters/references/common/dispatch.md +32 -0
  295. package/.agents/skills/hamr-adapters/references/common/model-and-effort.md +42 -0
  296. package/.agents/skills/hamr-adapters/references/common/primary-hooks.md +40 -0
  297. package/.agents/skills/hamr-adapters/references/harness/claude.md +55 -0
  298. package/.agents/skills/hamr-adapters/references/harness/codex.md +43 -0
  299. package/.agents/skills/hamr-adapters/references/harness/cursor.md +75 -0
  300. package/.agents/skills/hamr-adapters/references/harness/grok.md +69 -0
  301. package/.agents/skills/hamr-adapters/references/harness/kimi.md +51 -0
  302. package/.agents/skills/hamr-adapters/references/harness/muse.md +70 -0
  303. package/.agents/skills/hamr-adapters/references/harness/opencode.md +42 -0
  304. package/.agents/skills/hamr-adapters/references/harness/pi.md +56 -0
  305. package/.agents/skills/herdr-panes/README.md +3 -0
  306. package/.agents/skills/herdr-panes/SKILL.md +92 -0
  307. package/.agents/skills/herdr-panes/assets/herdr-backend.md +346 -0
  308. package/.agents/skills/herdr-panes/assets/tmux-backend.md +115 -0
  309. package/.agents/skills/hnoss-design/DESIGN.md +19 -0
  310. package/.agents/skills/hnoss-design/README.md +3 -0
  311. package/.agents/skills/hnoss-design/SKILL.md +83 -0
  312. package/.agents/skills/hvild-afk/README.md +3 -0
  313. package/.agents/skills/hvild-afk/SKILL.md +241 -0
  314. package/.agents/skills/jord-projects/README.md +3 -0
  315. package/.agents/skills/jord-projects/SKILL.md +92 -0
  316. package/.agents/skills/lifecycle/SKILL.md +56 -0
  317. package/.agents/skills/lifecycle/smoke_test.sh +52 -0
  318. package/.agents/skills/lifecycle/start.sh +21 -0
  319. package/.agents/skills/lifecycle/status.sh +35 -0
  320. package/.agents/skills/lifecycle/stop.sh +16 -0
  321. package/.agents/skills/muninn-stow/README.md +3 -0
  322. package/.agents/skills/muninn-stow/SKILL.md +309 -0
  323. package/.agents/skills/no-mistakes/SKILL.md +379 -0
  324. package/.agents/skills/nornir-schedule/README.md +3 -0
  325. package/.agents/skills/nornir-schedule/SKILL.md +25 -0
  326. package/.agents/skills/nornir-schedule/assets/events.md +137 -0
  327. package/.agents/skills/nornir-schedule/assets/quota.md +131 -0
  328. package/.agents/skills/nsr-compliance/README.md +3 -0
  329. package/.agents/skills/nsr-compliance/SKILL.md +29 -0
  330. package/.agents/skills/nsr-compliance/assets/nsr/README.md +40 -0
  331. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/README.md +15 -0
  332. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/git-workflow.md +18 -0
  333. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/security.md +22 -0
  334. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/tech-stack.md +8 -0
  335. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/SKILL.md +71 -0
  336. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/config/core_four.yaml +16 -0
  337. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_danger.sh +102 -0
  338. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_wiring.sh +69 -0
  339. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/validate_code.sh +29 -0
  340. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/verify_docs.py +66 -0
  341. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/generate.sh +109 -0
  342. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/result_envelope.json +14 -0
  343. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/task_envelope.json +20 -0
  344. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/runner.py +303 -0
  345. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/telemetry/logger.py +112 -0
  346. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/README.md +18 -0
  347. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/config/README.md +10 -0
  348. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/README.md +12 -0
  349. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_env.sh +23 -0
  350. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_paths.sh +20 -0
  351. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_platform.sh +23 -0
  352. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/README.md +11 -0
  353. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/envelopes/README.md +10 -0
  354. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/README.md +10 -0
  355. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/install.sh +125 -0
  356. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/telemetry/README.md +11 -0
  357. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/README.md +20 -0
  358. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/README.md +36 -0
  359. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/SKILL-template.md +14 -0
  360. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/README.md +25 -0
  361. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/SKILL.md +13 -0
  362. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/create_branch.sh +6 -0
  363. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/safe_commit.sh +9 -0
  364. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/sync_upstream.sh +6 -0
  365. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/README.md +27 -0
  366. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/SKILL.md +15 -0
  367. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/smoke_test.sh +12 -0
  368. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/start.sh +19 -0
  369. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/status.sh +12 -0
  370. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/stop.sh +15 -0
  371. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/README.md +18 -0
  372. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/README.md +10 -0
  373. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/core_four.yaml +13 -0
  374. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/README.md +12 -0
  375. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_danger.sh +102 -0
  376. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_env.sh +23 -0
  377. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_paths.sh +20 -0
  378. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_platform.sh +23 -0
  379. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_wiring.sh +69 -0
  380. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/README.md +11 -0
  381. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/README.md +10 -0
  382. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/result_envelope.json +14 -0
  383. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/task_envelope.json +20 -0
  384. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/runner.py +303 -0
  385. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/README.md +10 -0
  386. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/install.sh +125 -0
  387. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/README.md +11 -0
  388. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/logger.py +112 -0
  389. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/README.md +14 -0
  390. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/api.md +17 -0
  391. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/compliance-wiring.md +155 -0
  392. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/frontend.md +15 -0
  393. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/README.md +11 -0
  394. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/README.md +23 -0
  395. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/README.md +16 -0
  396. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/template-README.md +11 -0
  397. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/deploy.sh +49 -0
  398. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/README.md +12 -0
  399. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/development.env.example +17 -0
  400. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/production.env.example +17 -0
  401. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/staging.env.example +17 -0
  402. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/README.md +11 -0
  403. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/template.env.example +8 -0
  404. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/README.md +26 -0
  405. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/agent.md +7 -0
  406. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/developers/dev-1.md +20 -0
  407. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/linux.md +9 -0
  408. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/macos.md +10 -0
  409. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/shared.md +19 -0
  410. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/windows.md +14 -0
  411. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/README.md +25 -0
  412. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/_template.md +41 -0
  413. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/hosting-1.md +41 -0
  414. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/README.md +21 -0
  415. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/README.md +15 -0
  416. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/deployment.md +19 -0
  417. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/disaster-recovery.md +15 -0
  418. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/ops-tasks.md +14 -0
  419. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/features/README.md +16 -0
  420. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/project-skills.md +146 -0
  421. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/README.md +11 -0
  422. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/software-compliance-research.md +269 -0
  423. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/working-with-agents.md +260 -0
  424. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/AGENTS.md +38 -0
  425. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/ARCHITECTURE.md +36 -0
  426. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/BEST_PRACTICES.md +27 -0
  427. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/CI_CD.md +23 -0
  428. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/FEATURES.md +15 -0
  429. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/README.md +22 -0
  430. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/RULES.md +37 -0
  431. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/STRUCTURE.md +45 -0
  432. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/TECH_STACK.md +28 -0
  433. package/.agents/skills/nsr-compliance/assets/nsr/scaffold-spec.md +63 -0
  434. package/.agents/skills/nsr-compliance/assets/nsr/scripts/scaffold.sh +103 -0
  435. package/.agents/skills/nsr-compliance/assets/nsr/scripts/validate.sh +52 -0
  436. package/.agents/skills/nsr-compliance/assets/nsrcompliance/SKILL.md +71 -0
  437. package/.agents/skills/nsr-compliance/assets/nsrcompliance/config/core_four.yaml +16 -0
  438. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_danger.sh +102 -0
  439. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_wiring.sh +69 -0
  440. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/validate_code.sh +29 -0
  441. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/verify_docs.py +66 -0
  442. package/.agents/skills/nsr-compliance/assets/nsrcompliance/generate.sh +109 -0
  443. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/result_envelope.json +14 -0
  444. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/task_envelope.json +20 -0
  445. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/runner.py +303 -0
  446. package/.agents/skills/nsr-compliance/assets/nsrcompliance/telemetry/logger.py +112 -0
  447. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/README.md +18 -0
  448. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/config/README.md +10 -0
  449. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/README.md +12 -0
  450. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_env.sh +23 -0
  451. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_paths.sh +20 -0
  452. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_platform.sh +23 -0
  453. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/README.md +11 -0
  454. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/envelopes/README.md +10 -0
  455. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/README.md +10 -0
  456. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/install.sh +125 -0
  457. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/telemetry/README.md +11 -0
  458. package/.agents/skills/pr-ops/README.md +3 -0
  459. package/.agents/skills/pr-ops/SKILL.md +109 -0
  460. package/.agents/skills/ratatoskr-a2a/README.md +3 -0
  461. package/.agents/skills/ratatoskr-a2a/SKILL.md +148 -0
  462. package/.agents/skills/rules-check-drift/SKILL.md +81 -0
  463. package/.agents/skills/saga-bearings/README.md +3 -0
  464. package/.agents/skills/saga-bearings/SKILL.md +26 -0
  465. package/.agents/skills/saga-bearings/assets/bearings.md +161 -0
  466. package/.agents/skills/saga-bearings/assets/board-template.html +731 -0
  467. package/.agents/skills/saga-bearings/assets/recap.md +56 -0
  468. package/.agents/skills/syn-recovery/README.md +3 -0
  469. package/.agents/skills/syn-recovery/SKILL.md +54 -0
  470. package/.agents/skills/tyr-check/README.md +3 -0
  471. package/.agents/skills/tyr-check/SKILL.md +218 -0
  472. package/.agents/skills/urdh-hold/README.md +3 -0
  473. package/.agents/skills/urdh-hold/SKILL.md +25 -0
  474. package/.agents/skills/urdh-hold/assets/decisions.md +15 -0
  475. package/.agents/skills/urdh-hold/assets/hold.md +54 -0
  476. package/.agents/skills/vor-diagnostics/README.md +3 -0
  477. package/.agents/skills/vor-diagnostics/SKILL.md +68 -0
  478. package/.agents/skills/ymir-host/README.md +3 -0
  479. package/.agents/skills/ymir-host/SKILL.md +34 -0
  480. package/.agents/skills/ymir-host/assets/desktop.md +36 -0
  481. package/.agents/skills/ymir-host/assets/omarchy.md +297 -0
  482. package/.agents/skills/ymir-host/assets/runbooks.md +15 -0
  483. package/.agents/skills/ymir-host/assets/thjazi.md +220 -0
  484. package/.agents/skills/ymir-host/assets/update.md +64 -0
  485. package/.agents/tests/assets/board-render-harness.mjs +117 -0
  486. package/.agents/tests/cmux-test-safety.sh +51 -0
  487. package/.agents/tests/fixtures.sh +291 -0
  488. package/.agents/tests/fm-afk-inject-e2e.test.sh +428 -0
  489. package/.agents/tests/fm-afk-inject-herdr-e2e.test.sh +536 -0
  490. package/.agents/tests/fm-afk-launch.test.sh +961 -0
  491. package/.agents/tests/fm-afk-pi-herdr-return-e2e.test.sh +297 -0
  492. package/.agents/tests/fm-afk-return.test.sh +283 -0
  493. package/.agents/tests/fm-arm-pretool-check.test.sh +482 -0
  494. package/.agents/tests/fm-ask-user-authority.test.sh +42 -0
  495. package/.agents/tests/fm-backend-autodetect-smoke.test.sh +176 -0
  496. package/.agents/tests/fm-backend-cmux-smoke.test.sh +188 -0
  497. package/.agents/tests/fm-backend-cmux.test.sh +1166 -0
  498. package/.agents/tests/fm-backend-herdr-eventwait-smoke.test.sh +136 -0
  499. package/.agents/tests/fm-backend-herdr-eventwait.test.py +106 -0
  500. package/.agents/tests/fm-backend-herdr-focus-flash-e2e.test.sh +409 -0
  501. package/.agents/tests/fm-backend-herdr-launcher-workspace-e2e.test.sh +437 -0
  502. package/.agents/tests/fm-backend-herdr-presentation-e2e.test.sh +1424 -0
  503. package/.agents/tests/fm-backend-herdr-prune-safety-e2e.test.sh +180 -0
  504. package/.agents/tests/fm-backend-herdr-respawn-idem-e2e.test.sh +182 -0
  505. package/.agents/tests/fm-backend-herdr-smoke.test.sh +355 -0
  506. package/.agents/tests/fm-backend-herdr-workspace-per-home-e2e.test.sh +251 -0
  507. package/.agents/tests/fm-backend-herdr.test.sh +4606 -0
  508. package/.agents/tests/fm-backend-orca.test.sh +1357 -0
  509. package/.agents/tests/fm-backend-tmux-smoke.test.sh +173 -0
  510. package/.agents/tests/fm-backend-zellij-smoke.test.sh +205 -0
  511. package/.agents/tests/fm-backend-zellij.test.sh +1360 -0
  512. package/.agents/tests/fm-backend.test.sh +1142 -0
  513. package/.agents/tests/fm-backlog-atomicity.test.sh +2302 -0
  514. package/.agents/tests/fm-backlog-handoff.test.sh +1360 -0
  515. package/.agents/tests/fm-bearings-board-render.test.sh +135 -0
  516. package/.agents/tests/fm-bearings-board.test.sh +414 -0
  517. package/.agents/tests/fm-bearings-snapshot.test.sh +1990 -0
  518. package/.agents/tests/fm-bootstrap-network-parallel.test.sh +316 -0
  519. package/.agents/tests/fm-bootstrap.test.sh +1178 -0
  520. package/.agents/tests/fm-branch-supervision.test.sh +552 -0
  521. package/.agents/tests/fm-brief.test.sh +785 -0
  522. package/.agents/tests/fm-busy-adapter-wiring.test.sh +324 -0
  523. package/.agents/tests/fm-busy-state.test.sh +465 -0
  524. package/.agents/tests/fm-calm-pi-extension.test.sh +3979 -0
  525. package/.agents/tests/fm-captain-hold-lifecycle.test.sh +1187 -0
  526. package/.agents/tests/fm-cd-pretool-check.test.sh +400 -0
  527. package/.agents/tests/fm-check-unregister.test.sh +198 -0
  528. package/.agents/tests/fm-classify-corr-token.test.sh +543 -0
  529. package/.agents/tests/fm-classify-decision-key.test.sh +340 -0
  530. package/.agents/tests/fm-claude-stop-autoarm-live-e2e.test.sh +164 -0
  531. package/.agents/tests/fm-claude-stop-autoarm.test.sh +1190 -0
  532. package/.agents/tests/fm-cmux-claude-composer-live-e2e.test.sh +110 -0
  533. package/.agents/tests/fm-codex-continuity-live-e2e.test.sh +55 -0
  534. package/.agents/tests/fm-composer-ghost.test.sh +716 -0
  535. package/.agents/tests/fm-composer-lib.test.sh +666 -0
  536. package/.agents/tests/fm-composer-matrix-live-e2e.test.sh +220 -0
  537. package/.agents/tests/fm-control-herdr-smoke.test.sh +149 -0
  538. package/.agents/tests/fm-control-relaunch.test.sh +1530 -0
  539. package/.agents/tests/fm-control.test.sh +910 -0
  540. package/.agents/tests/fm-crew-state.test.sh +1610 -0
  541. package/.agents/tests/fm-cursor-harness.test.sh +404 -0
  542. package/.agents/tests/fm-cursor-primary-live-e2e.test.sh +217 -0
  543. package/.agents/tests/fm-cursor-primary.test.sh +708 -0
  544. package/.agents/tests/fm-daemon.test.sh +2740 -0
  545. package/.agents/tests/fm-documentation-audiences.test.sh +141 -0
  546. package/.agents/tests/fm-ensure-agents-md.test.sh +371 -0
  547. package/.agents/tests/fm-extension-binding.test.sh +2187 -0
  548. package/.agents/tests/fm-fleet-snapshot-view.test.sh +816 -0
  549. package/.agents/tests/fm-fleet-sync.test.sh +721 -0
  550. package/.agents/tests/fm-gate-refuse.test.sh +356 -0
  551. package/.agents/tests/fm-gitignore-config.test.sh +89 -0
  552. package/.agents/tests/fm-gotmp.test.sh +230 -0
  553. package/.agents/tests/fm-grok-continuity-live-e2e.test.sh +112 -0
  554. package/.agents/tests/fm-grok-harness.test.sh +117 -0
  555. package/.agents/tests/fm-grok-stop-live-e2e.test.sh +219 -0
  556. package/.agents/tests/fm-guard-stale-banner.test.sh +709 -0
  557. package/.agents/tests/fm-harness-adapter-instructions-live-e2e.test.sh +134 -0
  558. package/.agents/tests/fm-harness-adapter-references.test.sh +30 -0
  559. package/.agents/tests/fm-harness-liveness-drift-live-e2e.test.sh +151 -0
  560. package/.agents/tests/fm-herdr-lab.test.sh +243 -0
  561. package/.agents/tests/fm-herdr-session-cleanup-e2e.test.sh +146 -0
  562. package/.agents/tests/fm-herdr-session-cleanup.test.sh +329 -0
  563. package/.agents/tests/fm-herdr-submit-confirm-live-e2e.test.sh +125 -0
  564. package/.agents/tests/fm-herdr-version-floor-live-e2e.test.sh +144 -0
  565. package/.agents/tests/fm-home-summary-refresh.test.sh +971 -0
  566. package/.agents/tests/fm-inactive-reconcile.test.sh +485 -0
  567. package/.agents/tests/fm-kimi-harness.test.sh +687 -0
  568. package/.agents/tests/fm-lint-workflows.test.sh +532 -0
  569. package/.agents/tests/fm-lint.test.sh +1023 -0
  570. package/.agents/tests/fm-muse-harness.test.sh +932 -0
  571. package/.agents/tests/fm-muse-signals-live-e2e.test.sh +205 -0
  572. package/.agents/tests/fm-no-mistakes-required.test.sh +72 -0
  573. package/.agents/tests/fm-on.test.sh +507 -0
  574. package/.agents/tests/fm-opencode-primary-live-e2e.test.sh +357 -0
  575. package/.agents/tests/fm-operational-input.test.sh +160 -0
  576. package/.agents/tests/fm-peek-remote.test.sh +110 -0
  577. package/.agents/tests/fm-pending-reply.test.sh +1288 -0
  578. package/.agents/tests/fm-pi-branch-extension.test.sh +3182 -0
  579. package/.agents/tests/fm-pi-branch-live-e2e.test.sh +527 -0
  580. package/.agents/tests/fm-pi-primary-live-e2e.test.sh +345 -0
  581. package/.agents/tests/fm-pi-primary-types.test.sh +68 -0
  582. package/.agents/tests/fm-pi-watch-extension.test.sh +2843 -0
  583. package/.agents/tests/fm-pr-check-security.test.sh +2140 -0
  584. package/.agents/tests/fm-pr-merge.test.sh +2135 -0
  585. package/.agents/tests/fm-procevent-quota.test.sh +225 -0
  586. package/.agents/tests/fm-procevent-when.test.sh +406 -0
  587. package/.agents/tests/fm-procevent.test.sh +1745 -0
  588. package/.agents/tests/fm-project-origin.test.sh +110 -0
  589. package/.agents/tests/fm-public-followup.test.sh +2272 -0
  590. package/.agents/tests/fm-quota-array-dispatch-live-e2e.test.sh +526 -0
  591. package/.agents/tests/fm-quota-choose.test.sh +626 -0
  592. package/.agents/tests/fm-remote-backlog-handoff.test.sh +453 -0
  593. package/.agents/tests/fm-remote-doctor.test.sh +625 -0
  594. package/.agents/tests/fm-remote-entrypoint.test.sh +59 -0
  595. package/.agents/tests/fm-remote-job-orphan-reap.test.sh +205 -0
  596. package/.agents/tests/fm-remote-job.test.sh +768 -0
  597. package/.agents/tests/fm-remote-reply.test.sh +520 -0
  598. package/.agents/tests/fm-remote-secondmate-lifecycle-e2e.test.sh +1256 -0
  599. package/.agents/tests/fm-remote-secondmate-parent-binding.test.sh +326 -0
  600. package/.agents/tests/fm-remote-secondmate-trace-context.test.sh +310 -0
  601. package/.agents/tests/fm-remote-transport-lanes.test.sh +431 -0
  602. package/.agents/tests/fm-review-diff.test.sh +176 -0
  603. package/.agents/tests/fm-secondmate-harness.test.sh +2600 -0
  604. package/.agents/tests/fm-secondmate-lifecycle-e2e.test.sh +259 -0
  605. package/.agents/tests/fm-secondmate-liveness.test.sh +559 -0
  606. package/.agents/tests/fm-secondmate-reconcile.test.sh +732 -0
  607. package/.agents/tests/fm-secondmate-safety.test.sh +2972 -0
  608. package/.agents/tests/fm-secondmate-sync.test.sh +913 -0
  609. package/.agents/tests/fm-send-inbox-doorbell-live-e2e.test.sh +206 -0
  610. package/.agents/tests/fm-send-inbox.test.sh +352 -0
  611. package/.agents/tests/fm-send-popup-settle.test.sh +167 -0
  612. package/.agents/tests/fm-send-remote-delivery.test.sh +796 -0
  613. package/.agents/tests/fm-send-resolve-key.test.sh +551 -0
  614. package/.agents/tests/fm-send-secondmate-marker-herdr-e2e.test.sh +187 -0
  615. package/.agents/tests/fm-send-secondmate-marker.test.sh +278 -0
  616. package/.agents/tests/fm-send-settle.test.sh +147 -0
  617. package/.agents/tests/fm-send-strict.test.sh +241 -0
  618. package/.agents/tests/fm-session-lock-ancestry.test.sh +365 -0
  619. package/.agents/tests/fm-session-start.test.sh +2515 -0
  620. package/.agents/tests/fm-sessionstart-hook-live-e2e.test.sh +631 -0
  621. package/.agents/tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh +230 -0
  622. package/.agents/tests/fm-sessionstart-nudge.test.sh +1039 -0
  623. package/.agents/tests/fm-shared-captain-inheritance.test.sh +404 -0
  624. package/.agents/tests/fm-spawn-batch.test.sh +148 -0
  625. package/.agents/tests/fm-spawn-dispatch-profile.test.sh +827 -0
  626. package/.agents/tests/fm-spawn-pool-base-freshen.test.sh +440 -0
  627. package/.agents/tests/fm-spawn-worktree-settle.test.sh +147 -0
  628. package/.agents/tests/fm-startup-memory-budget.test.sh +333 -0
  629. package/.agents/tests/fm-startup-network.test.sh +712 -0
  630. package/.agents/tests/fm-stow-cascade.test.sh +370 -0
  631. package/.agents/tests/fm-subagent-pretool-check.test.sh +291 -0
  632. package/.agents/tests/fm-supervision-events.test.sh +156 -0
  633. package/.agents/tests/fm-supervision-instructions.test.sh +189 -0
  634. package/.agents/tests/fm-tangle-guard.test.sh +277 -0
  635. package/.agents/tests/fm-task-delivery.test.sh +420 -0
  636. package/.agents/tests/fm-task-inbox.test.sh +513 -0
  637. package/.agents/tests/fm-teardown-endpoint-safety.test.sh +374 -0
  638. package/.agents/tests/fm-teardown.test.sh +2665 -0
  639. package/.agents/tests/fm-test-fixture-cleanup.test.sh +172 -0
  640. package/.agents/tests/fm-test-fixtures.test.sh +132 -0
  641. package/.agents/tests/fm-test-isolation-proof.test.sh +278 -0
  642. package/.agents/tests/fm-test-run.test.sh +1196 -0
  643. package/.agents/tests/fm-tmux-agent-liveness.test.sh +354 -0
  644. package/.agents/tests/fm-tmux-submit-busy.test.sh +340 -0
  645. package/.agents/tests/fm-tool-update-check.test.sh +1044 -0
  646. package/.agents/tests/fm-trace-context-lib.test.sh +253 -0
  647. package/.agents/tests/fm-trace-context-spawn.test.sh +581 -0
  648. package/.agents/tests/fm-transition-lib.test.sh +54 -0
  649. package/.agents/tests/fm-turnend-guard.test.sh +1822 -0
  650. package/.agents/tests/fm-update.test.sh +304 -0
  651. package/.agents/tests/fm-vendor-auth-probe.test.sh +395 -0
  652. package/.agents/tests/fm-voice-relay.test.sh +4741 -0
  653. package/.agents/tests/fm-wake-daemon-lifecycle-e2e.test.sh +171 -0
  654. package/.agents/tests/fm-wake-drain-open-decisions-cursor.test.sh +356 -0
  655. package/.agents/tests/fm-wake-drain-open-decisions.test.sh +226 -0
  656. package/.agents/tests/fm-wake-drain-unread-status.test.sh +379 -0
  657. package/.agents/tests/fm-wake-queue.test.sh +1224 -0
  658. package/.agents/tests/fm-watch-arm.test.sh +821 -0
  659. package/.agents/tests/fm-watch-checkpoint.test.sh +87 -0
  660. package/.agents/tests/fm-watch-recovery-loop.test.sh +223 -0
  661. package/.agents/tests/fm-watch-triage.test.sh +3889 -0
  662. package/.agents/tests/fm-watcher-lock.test.sh +1133 -0
  663. package/.agents/tests/fm-x-mode.test.sh +3042 -0
  664. package/.agents/tests/gleipnir-guard-pretool-check.test.sh +51 -0
  665. package/.agents/tests/gleipnir-machine-lock.test.sh +130 -0
  666. package/.agents/tests/herdr-test-safety.sh +42 -0
  667. package/.agents/tests/lib.sh +315 -0
  668. package/.agents/tests/remote-herdr-fixture.sh +125 -0
  669. package/.agents/tests/secondmate-helpers.sh +193 -0
  670. package/.agents/tests/smoke.test.sh +93 -0
  671. package/.agents/tests/wake-helpers.sh +338 -0
  672. package/.agents/tests/zellij-test-safety.sh +60 -0
  673. package/.agents/tools/README.md +10 -0
  674. package/.agents/tools/bin/tasks-cli +267 -0
  675. package/.agents/tools/tasks-cli.ts +216 -0
  676. package/AGENTS.md +454 -0
  677. package/CHANGELOG.md +881 -0
  678. package/LICENSE +1 -1
  679. package/NOTICE +63 -0
  680. package/README.md +113 -59
  681. package/RULES/01-domains.md +51 -0
  682. package/RULES/02-agents.md +78 -0
  683. package/RULES/03-houses.md +61 -0
  684. package/RULES/04-hoard.md +65 -0
  685. package/RULES/05-platforms.md +67 -0
  686. package/RULES/06-append-only.md +58 -0
  687. package/RULES/07-config.md +45 -0
  688. package/RULES/08-delivery-gate.md +75 -0
  689. package/RULES/README.md +20 -0
  690. package/bin/README.md +107 -0
  691. package/bin/a2a-mcp.sh +98 -0
  692. package/bin/a2a-serve.py +103 -0
  693. package/bin/a2a-serve.sh +8 -0
  694. package/bin/a2a-talk.sh +50 -0
  695. package/bin/a2abridge-ensure.sh +108 -0
  696. package/bin/agent-run.sh +43 -0
  697. package/bin/agents-config.sh +233 -0
  698. package/bin/apodex-smoke-test.sh +92 -0
  699. package/bin/bifrost-bridge.sh +112 -0
  700. package/bin/bifrost-ingress.sh +77 -0
  701. package/bin/bootstrap-macos.sh +107 -0
  702. package/bin/bootstrap-windows.ps1 +112 -0
  703. package/bin/branch-guard.sh +64 -0
  704. package/bin/brokk +73 -0
  705. package/bin/brokk-classify-lib.sh +1770 -0
  706. package/bin/brokk-lease-lib.sh +222 -0
  707. package/bin/brokk-lease.sh +193 -0
  708. package/bin/brokk-lint.sh +65 -0
  709. package/bin/brokk-send.sh +38 -0
  710. package/bin/brokk-update.sh +9 -0
  711. package/bin/brokk-wake-grant.sh +127 -0
  712. package/bin/brokk-wake-lib.sh +1849 -0
  713. package/bin/changelog-guard.sh +165 -0
  714. package/bin/crash-sense.sh +160 -0
  715. package/bin/desktop-place.sh +224 -0
  716. package/bin/docs-guard.sh +39 -0
  717. package/bin/editor-place.sh +207 -0
  718. package/bin/eindri-acclaim.sh +34 -0
  719. package/bin/eindri-control.sh +28 -0
  720. package/bin/eindri-role.sh +83 -0
  721. package/bin/eindri-seen.sh +41 -0
  722. package/bin/eindri-send.sh +15 -0
  723. package/bin/eindri-start.sh +85 -0
  724. package/bin/eindri-watch.sh +57 -0
  725. package/bin/einherjar-spawn.sh +576 -0
  726. package/bin/eir-doctor.sh +112 -0
  727. package/bin/erindi-brief.sh +271 -0
  728. package/bin/fleet-apply.sh +68 -0
  729. package/bin/github-deploy.sh +81 -0
  730. package/bin/gjallarhorn-expose.sh +102 -0
  731. package/bin/gjallarhorn-notify.sh +58 -0
  732. package/bin/gjallarhorn-purge.sh +54 -0
  733. package/bin/gjallarhorn-tunnel.sh +72 -0
  734. package/bin/gleipnir-lock-lib.sh +284 -0
  735. package/bin/groa-update.sh +122 -0
  736. package/bin/hall-snapshot.sh +92 -0
  737. package/bin/hamr-harness.sh +246 -0
  738. package/bin/heimdall-ssh-keys.sh +374 -0
  739. package/bin/herdr-agents.py +44 -0
  740. package/bin/herdr-ensure.sh +104 -0
  741. package/bin/herdr-run.sh +431 -0
  742. package/bin/hermes-ensure.sh +88 -0
  743. package/bin/hoard-lib.sh +25 -0
  744. package/bin/hodd.sh +78 -0
  745. package/bin/host-sense.sh +199 -0
  746. package/bin/huginn-research-worker.sh +108 -0
  747. package/bin/local-model-lock.sh +51 -0
  748. package/bin/mimir-bridge.py +226 -0
  749. package/bin/mimir-bridge.sh +87 -0
  750. package/bin/mimir-ingest.sh +131 -0
  751. package/bin/mimir.sh +113 -0
  752. package/bin/mjollnir-webhook.sh +59 -0
  753. package/bin/mjollnir.sh +85 -0
  754. package/bin/model-hardware.sh +82 -0
  755. package/bin/model-resolve.sh +147 -0
  756. package/bin/models-detect.sh +178 -0
  757. package/bin/nornir-cron-start.sh +162 -0
  758. package/bin/nornir-job-daily-briefing.sh +191 -0
  759. package/bin/nornir-job-git-sync.sh +152 -0
  760. package/bin/nornir-job-memory-housekeeping.sh +114 -0
  761. package/bin/nornir-job-observer.sh +166 -0
  762. package/bin/nornir-job-skillopt-sleep.sh +76 -0
  763. package/bin/omarchy-hook-install.sh +60 -0
  764. package/bin/omarchy-install.sh +148 -0
  765. package/bin/omarchy-plugins.sh +174 -0
  766. package/bin/omarchy-sense.sh +149 -0
  767. package/bin/perm-guard.sh +30 -0
  768. package/bin/pi-agent.sh +68 -0
  769. package/bin/pi-ensure.sh +77 -0
  770. package/bin/pi-local.sh +37 -0
  771. package/bin/pi-seat.sh +63 -0
  772. package/bin/prereq-ensure.sh +166 -0
  773. package/bin/private-guard.sh +38 -0
  774. package/bin/project-git.sh +55 -0
  775. package/bin/public-guard.sh +70 -0
  776. package/bin/ratatoskr.sh +49 -0
  777. package/bin/realm-lib.sh +32 -0
  778. package/bin/repo-scrub.sh +62 -0
  779. package/bin/rodd-operational-input.sh +250 -0
  780. package/bin/runes-append.sh +198 -0
  781. package/bin/saga-session-start.sh +172 -0
  782. package/bin/saga-sessionstart-run.sh +79 -0
  783. package/bin/saga-wake-drain.sh +41 -0
  784. package/bin/secret-guard.sh +63 -0
  785. package/bin/sessrumnir-ensure.sh +100 -0
  786. package/bin/sessrumnir-sync.sh +237 -0
  787. package/bin/sessrumnir.sh +91 -0
  788. package/bin/skillopt-setup.sh +76 -0
  789. package/bin/skuld-branch-outcome.sh +203 -0
  790. package/bin/skuld-branch-prompt.sh +101 -0
  791. package/bin/smidja-bootstrap.sh +90 -0
  792. package/bin/smidja-observe.sh +86 -0
  793. package/bin/syn-arm-pretool-check.sh +57 -0
  794. package/bin/syn-asset-pretool-check.sh +76 -0
  795. package/bin/syn-cd-pretool-check.sh +28 -0
  796. package/bin/syn-guard-pretool-check.sh +83 -0
  797. package/bin/syn-turnend-guard.sh +36 -0
  798. package/bin/syn-watch-arm.sh +99 -0
  799. package/bin/tailscale-sync.sh +190 -0
  800. package/bin/telegram-bot.sh +69 -0
  801. package/bin/toolchain.sh +74 -0
  802. package/bin/utgard.sh +111 -0
  803. package/bin/valhalla.sh +115 -0
  804. package/bin/valknut-load.sh +260 -0
  805. package/bin/vor-crew-state.sh +124 -0
  806. package/bin/wedge-notify.sh +47 -0
  807. package/bin/workspace-provision.sh +80 -0
  808. package/bin/workspace-rag.sh +150 -0
  809. package/bin/wyrd-db.sh +47 -0
  810. package/bin/yggdrasil.sh +152 -0
  811. package/bin/ymir-edit.sh +145 -0
  812. package/bin/ymir-install.sh +641 -0
  813. package/bin/ymir-invite.sh +93 -0
  814. package/bin/ymir-isolation.sh +88 -0
  815. package/bin/ymir-migrate.sh +61 -0
  816. package/bin/ymir-platform.sh +348 -0
  817. package/bin/ymir-say.sh +97 -0
  818. package/bin/ymir-setup-auth.sh +108 -0
  819. package/bin/ymir-validate.sh +173 -0
  820. package/bin/ymir.js +74 -16
  821. package/docs/Architecture.md +339 -0
  822. package/docs/design.md +351 -0
  823. package/docs/installations/README.md +63 -0
  824. package/docs/installations/ymir-home.md +93 -0
  825. package/docs/integration.md +105 -0
  826. package/docs/lore.md +1056 -0
  827. package/docs/research/agent-harnesses.md +290 -0
  828. package/docs/runbooks/README.md +19 -0
  829. package/docs/runbooks/a2a.md +91 -0
  830. package/docs/runbooks/agent-permissions.md +99 -0
  831. package/docs/runbooks/agents.md +57 -0
  832. package/docs/runbooks/models.md +69 -0
  833. package/docs/runbooks/realm-onboarding.md +60 -0
  834. package/docs/runbooks/secrets-and-hoard.md +60 -0
  835. package/docs/runbooks/updates-and-migrations.md +110 -0
  836. package/docs/runbooks/worktrees-isolation.md +72 -0
  837. package/docs/session-start.md +105 -0
  838. package/docs/skillopt-integration.md +321 -0
  839. package/docs/sync-system.md +66 -0
  840. package/docs/workspaces.md +104 -0
  841. package/docs/ymir-rut.md +260 -0
  842. package/package.json +44 -14
  843. package/packaging/build.sh +74 -0
  844. package/packaging/macos/Ymir Installer.command +43 -0
  845. package/packaging/windows/ymir-setup.nsi +74 -0
  846. package/scripts/README.md +22 -0
  847. package/scripts/electron.sh +210 -0
  848. package/scripts/start.sh +167 -0
  849. package/scripts/stop.sh +113 -0
  850. package/install.sh +0 -35
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env bash
2
+ # fm-public-followup-emit.sh - emit ONE structured terminal work result for work
3
+ # bound to a public commitment, into the owning home's private event inbox.
4
+ #
5
+ # WHY THIS EXISTS: a public promise is kept by the home that owns the relay
6
+ # consent and the thread binding. The home doing the work only has to report a
7
+ # TYPED result. Firstmate must never recover the source home, work id, outcome,
8
+ # or deliverables by parsing a free-form "done: ..." status sentence, so this
9
+ # script is the structured channel that carries them.
10
+ #
11
+ # WHAT IT DOES NOT DO: it never posts anything, never reads relay credentials,
12
+ # and never resolves a public thread. Outward delivery stays with the owning
13
+ # home (bin/fm-public-followup.sh deliver).
14
+ #
15
+ # Usage:
16
+ # fm-public-followup-emit.sh --home <owning-home> \
17
+ # --obligation <obligation-id> --relation <relation-id> \
18
+ # --source-home <main|secondmate:<id>> --work-id <task-id> \
19
+ # --generation <n> --outcome <outcome-type> \
20
+ # [--deliverable <key>=<value>]... \
21
+ # (--outcome-text <text> | --outcome-text-file <path> | --outcome-text -)
22
+ #
23
+ # Options:
24
+ # --home <path> The home that owns the public commitment (the primary
25
+ # that took the mention). Must already have a
26
+ # registration for --obligation; see
27
+ # `fm-public-followup.sh register`.
28
+ # --obligation <id> tasks-axi public-followup obligation id.
29
+ # --relation <id> The relation_id this work fulfills or contributes to.
30
+ # --source-home <id> This worker's stable home identity, exactly as bound:
31
+ # "main" or "secondmate:<stable-id>".
32
+ # --work-id <id> This worker's exact task id, exactly as bound.
33
+ # --generation <n> The bound relation generation (integer >= 1).
34
+ # --outcome <type> Typed outcome. tasks-axi owns the vocabulary and
35
+ # refuses anything it does not accept; this script only
36
+ # checks the token is a safe slug.
37
+ # --deliverable k=v Repeatable safe deliverable (for example
38
+ # pr_url=https://...). tasks-axi owns which keys a given
39
+ # expected-final type permits.
40
+ # --outcome-text ... Public-safe outcome sentence, from an argument, a
41
+ # file, or stdin ("-"). Collapsed to one line; the
42
+ # event builder bounds it by codepoint, so control
43
+ # characters cannot survive.
44
+ #
45
+ # Output: the event id on stdout. Exit 0 on a published or already-present event
46
+ # (both are successes: the id is derived, so re-emitting the same terminal result
47
+ # is a no-op), 2 on a usage or validation error, 1 on a publication failure.
48
+ #
49
+ # IDEMPOTENCY: the event id is a digest of the identity tuple (obligation,
50
+ # relation, source home, work id, generation, outcome type, deliverables), so a
51
+ # retry, a duplicate report, or a rerun after restart resolves to the same file
52
+ # and the first published copy wins. Nothing here needs coordination.
53
+ #
54
+ # SAFETY: the event is published through the shared private-artifact primitive -
55
+ # atomic rename into place, single link, mode 0600 (never executable), inside a
56
+ # 0700 directory this script refuses to create. The owning home must already have
57
+ # registered the obligation, so a home that never opted into the relay can never
58
+ # be given public-followup artifacts by a child.
59
+ set -u
60
+
61
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
62
+ # shellcheck source=bin/fm-public-followup-lib.sh
63
+ . "$SCRIPT_DIR/fm-public-followup-lib.sh"
64
+
65
+ usage() {
66
+ cat >&2 <<'EOF'
67
+ usage: fm-public-followup-emit.sh --home <owning-home> --obligation <id> --relation <id>
68
+ --source-home <main|secondmate:<id>> --work-id <id> --generation <n>
69
+ --outcome <type> [--deliverable <key>=<value>]...
70
+ (--outcome-text <text> | --outcome-text-file <path> | --outcome-text -)
71
+ EOF
72
+ }
73
+
74
+ # The header comment IS the help text, so the two can never drift apart.
75
+ help() {
76
+ sed -n '2,/^set -u$/p' "$0" | sed '$d; s/^# \{0,1\}//'
77
+ }
78
+
79
+ die() { printf 'fm-public-followup-emit: %s\n' "$1" >&2; exit "${2:-2}"; }
80
+
81
+ HOME_DIR=
82
+ OBLIGATION=
83
+ RELATION=
84
+ SOURCE_HOME=
85
+ WORK_ID=
86
+ GENERATION=
87
+ OUTCOME=
88
+ TEXT_SOURCE=
89
+ TEXT_MODE=
90
+ DELIVERABLE_KEYS=()
91
+ DELIVERABLE_VALUES=()
92
+
93
+ case "${1:-}" in
94
+ --help|-h) help; exit 0 ;;
95
+ '') usage; exit 2 ;;
96
+ esac
97
+
98
+ while [ "$#" -gt 0 ]; do
99
+ case "$1" in
100
+ --home) shift; HOME_DIR=${1:-} ;;
101
+ --obligation) shift; OBLIGATION=${1:-} ;;
102
+ --relation) shift; RELATION=${1:-} ;;
103
+ --source-home) shift; SOURCE_HOME=${1:-} ;;
104
+ --work-id) shift; WORK_ID=${1:-} ;;
105
+ --generation) shift; GENERATION=${1:-} ;;
106
+ --outcome) shift; OUTCOME=${1:-} ;;
107
+ --outcome-text) shift; TEXT_MODE='inline'; TEXT_SOURCE=${1:-} ;;
108
+ --outcome-text-file) shift; TEXT_MODE='file'; TEXT_SOURCE=${1:-} ;;
109
+ --deliverable)
110
+ shift
111
+ case "${1:-}" in
112
+ *=*) ;;
113
+ *) die "--deliverable needs <key>=<value>, got '${1:-}'" ;;
114
+ esac
115
+ DELIVERABLE_KEYS+=("${1%%=*}")
116
+ DELIVERABLE_VALUES+=("${1#*=}")
117
+ ;;
118
+ --help|-h) help; exit 0 ;;
119
+ *) die "unknown argument '$1'" ;;
120
+ esac
121
+ shift || true
122
+ done
123
+
124
+ [ -n "$HOME_DIR" ] || { usage; exit 2; }
125
+ [ -n "$OBLIGATION" ] || { usage; exit 2; }
126
+ [ -n "$RELATION" ] || { usage; exit 2; }
127
+ [ -n "$SOURCE_HOME" ] || { usage; exit 2; }
128
+ [ -n "$WORK_ID" ] || { usage; exit 2; }
129
+ [ -n "$GENERATION" ] || { usage; exit 2; }
130
+ [ -n "$OUTCOME" ] || { usage; exit 2; }
131
+ [ -n "$TEXT_MODE" ] || { usage; exit 2; }
132
+
133
+ fm_pf_slug_valid "$OBLIGATION" || die "unsafe obligation id: $OBLIGATION"
134
+ fm_pf_slug_valid "$RELATION" || die "unsafe relation id: $RELATION"
135
+ fm_pf_slug_valid "$WORK_ID" || die "unsafe work id: $WORK_ID"
136
+ fm_pf_slug_valid "$OUTCOME" || die "unsafe outcome type: $OUTCOME"
137
+ fm_pf_home_id_valid "$SOURCE_HOME" \
138
+ || die "source home must be 'main' or 'secondmate:<stable-id>', got '$SOURCE_HOME'"
139
+ case "$GENERATION" in
140
+ ''|*[!0-9]*) die "generation must be a positive integer, got '$GENERATION'" ;;
141
+ esac
142
+ [ "$GENERATION" -ge 1 ] || die "generation must be >= 1, got '$GENERATION'"
143
+
144
+ i=0
145
+ while [ "$i" -lt "${#DELIVERABLE_KEYS[@]}" ]; do
146
+ key=${DELIVERABLE_KEYS[$i]}
147
+ case "$key" in
148
+ ''|*[!a-z0-9_]*) die "deliverable key must be lowercase [a-z0-9_], got '$key'" ;;
149
+ esac
150
+ [ "${#DELIVERABLE_VALUES[$i]}" -le 512 ] \
151
+ || die "deliverable '$key' exceeds 512 characters"
152
+ case "${DELIVERABLE_VALUES[$i]}" in
153
+ *[[:cntrl:]]*) die "deliverable '$key' must be single-line text with no control characters" ;;
154
+ esac
155
+ i=$((i + 1))
156
+ done
157
+
158
+ # Resolve the owning home to a real absolute directory before composing any path
159
+ # under it, so a relative or symlinked argument cannot make the destination
160
+ # ambiguous in a later message or write.
161
+ case "$HOME_DIR" in
162
+ /*) ;;
163
+ *) HOME_DIR=$(CDPATH='' cd -- "$HOME_DIR" 2>/dev/null && pwd -P) \
164
+ || die "--home is not a reachable directory: $1" ;;
165
+ esac
166
+ [ -d "$HOME_DIR" ] && [ ! -L "$HOME_DIR" ] \
167
+ || die "--home must name an existing directory, got '$HOME_DIR'"
168
+
169
+ fm_pf_relay_active "$HOME_DIR" || exit 0
170
+ command -v jq >/dev/null 2>&1 || die "jq is required to build a typed terminal event" 1
171
+
172
+ STATE="$HOME_DIR/state"
173
+ REGISTRY="$(fm_pf_registry_dir "$STATE")/$OBLIGATION"
174
+ if [ ! -f "$REGISTRY" ] || [ -L "$REGISTRY" ]; then
175
+ die "home '$HOME_DIR' has no public-followup registration for '$OBLIGATION'; the owning home registers a commitment before its work can report one" 1
176
+ fi
177
+
178
+ # The registration is the owning home's own record of what it bound, so checking
179
+ # the identity tuple against it catches a mis-briefed worker at the edge with a
180
+ # clear message. tasks-axi still re-validates everything at consume time and
181
+ # remains the authority; this is a cheap early refusal, not a second gatekeeper.
182
+ reg_mismatch() {
183
+ local field=$1 expected=$2 got=$3
184
+ [ -z "$expected" ] || [ "$expected" = "$got" ] \
185
+ || die "event $field '$got' does not match this home's registration ('$expected')"
186
+ }
187
+ reg_mismatch relation "$(fm_pf_registry_get "$STATE" "$OBLIGATION" relation_id)" "$RELATION"
188
+ reg_mismatch source-home "$(fm_pf_registry_get "$STATE" "$OBLIGATION" work_home)" "$SOURCE_HOME"
189
+ reg_mismatch work-id "$(fm_pf_registry_get "$STATE" "$OBLIGATION" work_id)" "$WORK_ID"
190
+ reg_mismatch generation "$(fm_pf_registry_get "$STATE" "$OBLIGATION" generation)" "$GENERATION"
191
+
192
+ case "$TEXT_MODE" in
193
+ inline) OUTCOME_TEXT=$(printf '%s' "$TEXT_SOURCE" | fm_pf_clean_outcome_text) ;;
194
+ file)
195
+ if [ "$TEXT_SOURCE" = '-' ]; then
196
+ OUTCOME_TEXT=$(fm_pf_clean_outcome_text)
197
+ else
198
+ [ -f "$TEXT_SOURCE" ] || die "outcome text file not found: $TEXT_SOURCE"
199
+ OUTCOME_TEXT=$(fm_pf_clean_outcome_text < "$TEXT_SOURCE")
200
+ fi
201
+ ;;
202
+ esac
203
+ [ -n "$OUTCOME_TEXT" ] || die "outcome text is empty once whitespace and control characters are removed"
204
+
205
+ # Canonical deliverables object: sorted keys, compact, so the same deliverables
206
+ # always hash to the same identity regardless of flag order.
207
+ DELIVERABLES_JSON=$(
208
+ {
209
+ i=0
210
+ while [ "$i" -lt "${#DELIVERABLE_KEYS[@]}" ]; do
211
+ printf '%s\n%s\n' "${DELIVERABLE_KEYS[$i]}" "${DELIVERABLE_VALUES[$i]}"
212
+ i=$((i + 1))
213
+ done
214
+ } | jq -Rsc 'split("\n") | .[:-1] | [range(0; length; 2) as $i | {key: .[$i], value: .[$i+1]}] | from_entries | to_entries | sort_by(.key) | from_entries'
215
+ ) || die "could not encode deliverables" 1
216
+
217
+ EVENT_ID=$(fm_pf_event_id \
218
+ "$OBLIGATION" "$RELATION" "$SOURCE_HOME" "$WORK_ID" "$GENERATION" "$OUTCOME" \
219
+ "$DELIVERABLES_JSON") || die "sha256 (shasum or sha256sum) is required" 1
220
+ # The derived id becomes a filename, so require the exact digest shape rather
221
+ # than trusting whatever the hashing tool printed.
222
+ case "$EVENT_ID" in
223
+ *[!0-9a-f]*|'') die "could not derive a usable event id" 1 ;;
224
+ esac
225
+ [ "${#EVENT_ID}" -eq 64 ] || die "could not derive a usable event id" 1
226
+
227
+ # jq bounds the outcome text by codepoint, so a long or non-ASCII sentence is
228
+ # capped without ever splitting a multi-byte character.
229
+ EVENT_JSON=$(jq -Sc -n \
230
+ --argjson schema_version "$FM_PF_EVENT_SCHEMA_VERSION" \
231
+ --arg event_id "$EVENT_ID" \
232
+ --arg obligation_id "$OBLIGATION" \
233
+ --arg relation_id "$RELATION" \
234
+ --arg work_id "$WORK_ID" \
235
+ --argjson generation "$GENERATION" \
236
+ --arg source_home_id "$SOURCE_HOME" \
237
+ --arg outcome_type "$OUTCOME" \
238
+ --argjson deliverables "$DELIVERABLES_JSON" \
239
+ --arg public_safe_outcome "$OUTCOME_TEXT" \
240
+ --argjson outcome_max "$FM_PF_OUTCOME_TEXT_MAX" \
241
+ --arg occurred_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
242
+ '{schema_version:$schema_version, event_id:$event_id, obligation_id:$obligation_id,
243
+ relation_id:$relation_id, work_id:$work_id, generation:$generation,
244
+ source_home_id:$source_home_id, outcome_type:$outcome_type,
245
+ deliverables:$deliverables,
246
+ public_safe_outcome:($public_safe_outcome[0:$outcome_max]),
247
+ occurred_at:$occurred_at, successor:null}') \
248
+ || die "could not build the typed terminal event" 1
249
+
250
+ EVENT_BYTES=$(printf '%s\n' "$EVENT_JSON" | LC_ALL=C wc -c | tr -d ' ') \
251
+ || die "could not measure the typed terminal event" 1
252
+ [ "$EVENT_BYTES" -le "$FM_PF_EVENT_BYTES_MAX" ] \
253
+ || die "typed terminal event exceeds $FM_PF_EVENT_BYTES_MAX bytes" 2
254
+
255
+ printf '%s\n' "$EVENT_JSON" \
256
+ | fmx_private_artifact_publish_stdin_once "$(fm_pf_events_dir "$STATE")" "$EVENT_ID.json" 600
257
+ case $? in
258
+ 0|1) printf '%s\n' "$EVENT_ID" ;;
259
+ *) die "could not publish the terminal event into $HOME_DIR" 1 ;;
260
+ esac
@@ -0,0 +1,398 @@
1
+ # shellcheck shell=bash
2
+ # fm-public-followup-lib.sh - shared gating and private-transport helpers for the
3
+ # deterministic public-followup consumer.
4
+ #
5
+ # Firstmate promises a public final reply when a myfirstmate relay mention (X or
6
+ # Discord) asks for work. `tasks-axi public-followup` is the sole owner of that
7
+ # typed obligation and its state machine; state/x-context/ is the sole owner of
8
+ # the private full request context. This library owns Firstmate's activation
9
+ # gate, private per-home transport paths, retained-loop state and locking
10
+ # helpers, follow-up window classification, and deterministic terminal-event
11
+ # identity.
12
+ #
13
+ # Sourced, never executed. No side effects on source (it creates nothing), which
14
+ # is what keeps a relay-disabled home free of public-followup artifacts.
15
+ # set -u / set -e safe.
16
+ #
17
+ # GATE ORDER - the acceptance criterion for relay-disabled homes:
18
+ # 1. fm_pf_relay_active <home> the authoritative myfirstmate activation
19
+ # contract, a non-empty FMX_PAIRING_TOKEN in
20
+ # <home>/.env. There is no second flag. When
21
+ # <home>/.env is absent this is a single
22
+ # [ -f ] test and nothing else runs.
23
+ # 2. fm_pf_has_registrations O(1) presence check on the registry created
24
+ # / fm_pf_has_events only by the relay path (fm-public-followup.sh
25
+ # / fm_pf_has_open_loops register). Open loops ARE registrations:
26
+ # a delivered final keeps the record, so this
27
+ # same check is the fail-loud session-start
28
+ # gate. Relay-enabled homes with no public
29
+ # loops stop here, so no tasks-axi call and
30
+ # no backlog scan happens.
31
+ #
32
+ # Private transport layout, all under <home>/state/public-followup (mode 0700,
33
+ # initialized by `fm-public-followup.sh register` and extended only by these
34
+ # public-followup commands):
35
+ # registry/<obligation-id> registration record: the bounded private binding
36
+ # (obligation, relation, work ref and canonical
37
+ # secondmate path, generation, platform, request id)
38
+ # plus the loop fields that survive delivery (state,
39
+ # delivered_at, followup_expires_at,
40
+ # request_context_b64). Presence means the public
41
+ # loop is still open. Delivery
42
+ # stamps state=delivered; only `retire` removes the
43
+ # record. The obligation itself always remains
44
+ # tasks-axi truth.
45
+ # events/<event-id>.json inbound typed terminal events awaiting
46
+ # reconciliation, one file per event id.
47
+ # consumed/<event-id> idempotency ledger: an accepted event id is never
48
+ # replayed, so duplicate emits and restart replay
49
+ # are no-ops.
50
+ # rejected/<event-id>.json events tasks-axi refused, kept with a
51
+ # rejected/<event-id>.reason one-line reason so a refusal is inspectable and
52
+ # never retried in a loop.
53
+ # surfaced last surfaced pending-event signature, so the
54
+ # existing relay poll wakes once per new event set
55
+ # instead of every cycle.
56
+ # retired/<obligation-id> private retirement receipt containing the bounded
57
+ # reason and timestamp recorded before the registry
58
+ # entry is removed; its presence prevents replayed
59
+ # registration from reopening the closed loop.
60
+ #
61
+ # Event identity is DERIVED, never random: fm_pf_event_id hashes the canonical
62
+ # identity tuple, so re-emitting the same terminal result produces the same
63
+ # event id and the same destination path. Idempotency therefore holds across
64
+ # retries, restarts, and duplicate child reports without any coordination.
65
+ #
66
+ # Depends on bin/fm-x-lib.sh for .env reading and the private-artifact
67
+ # publication primitives (atomic, single-link, mode-validated, non-executable);
68
+ # those remain that file's contract and are not restated here.
69
+
70
+ _FM_PF_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd 2>/dev/null)" || _FM_PF_LIB_DIR="."
71
+ # shellcheck source=bin/fm-x-lib.sh
72
+ . "$_FM_PF_LIB_DIR/fm-x-lib.sh"
73
+
74
+ FM_PF_DIRNAME='public-followup'
75
+ # Consumed by the sourcing scripts, not by this library.
76
+ # shellcheck disable=SC2034
77
+ FM_PF_EVENT_SCHEMA_VERSION=1
78
+ # Bounded so a public-safe outcome line can never carry a raw public message,
79
+ # and so one event file stays small enough to read and validate cheaply.
80
+ FM_PF_OUTCOME_TEXT_MAX=${FM_PF_OUTCOME_TEXT_MAX:-600}
81
+ FM_PF_EVENT_BYTES_MAX=${FM_PF_EVENT_BYTES_MAX:-8192}
82
+
83
+ # --- gate 1: the authoritative relay activation contract --------------------
84
+
85
+ # fm_pf_relay_active <home>: 0 when this home has opted into the myfirstmate
86
+ # relay, 1 otherwise. Identical contract to bootstrap's X-mode activation - a
87
+ # non-empty FMX_PAIRING_TOKEN in <home>/.env - so no second activation flag
88
+ # exists to drift. FMX_PAIRING_TOKEN in the environment wins, matching
89
+ # fmx_load_config, so a direct client call and this gate agree.
90
+ fm_pf_relay_active() {
91
+ local home=$1 token
92
+ if [ -n "${FMX_PAIRING_TOKEN+x}" ]; then
93
+ [ -n "${FMX_PAIRING_TOKEN-}" ]
94
+ return $?
95
+ fi
96
+ [ -f "$home/.env" ] || return 1
97
+ token=$(fmx_env_get FMX_PAIRING_TOKEN "$home/.env")
98
+ [ -n "$token" ]
99
+ }
100
+
101
+ # --- gate 2: O(1) presence checks on relay-path-owned registrations ---------
102
+
103
+ fm_pf_root() { printf '%s\n' "$1/$FM_PF_DIRNAME"; }
104
+ fm_pf_registry_dir() { printf '%s\n' "$1/$FM_PF_DIRNAME/registry"; }
105
+ fm_pf_events_dir() { printf '%s\n' "$1/$FM_PF_DIRNAME/events"; }
106
+ fm_pf_consumed_dir() { printf '%s\n' "$1/$FM_PF_DIRNAME/consumed"; }
107
+ fm_pf_rejected_dir() { printf '%s\n' "$1/$FM_PF_DIRNAME/rejected"; }
108
+ fm_pf_retired_dir() { printf '%s\n' "$1/$FM_PF_DIRNAME/retired"; }
109
+
110
+ fm_pf_retirement_receipt_exists() {
111
+ local file
112
+ file="$(fm_pf_retired_dir "$1")/$2"
113
+ [ -f "$file" ] && [ ! -L "$file" ]
114
+ }
115
+
116
+ # fm_pf_dir_has_entry <dir>: 0 when <dir> is a real directory holding at least
117
+ # one non-dot entry. Stops at the first hit, so cost does not grow with the
118
+ # directory's size.
119
+ fm_pf_dir_has_entry() {
120
+ local dir=$1 entry
121
+ [ -d "$dir" ] && [ ! -L "$dir" ] || return 1
122
+ for entry in "$dir"/*; do
123
+ [ -e "$entry" ] || continue
124
+ return 0
125
+ done
126
+ return 1
127
+ }
128
+
129
+ fm_pf_has_registrations() { fm_pf_dir_has_entry "$(fm_pf_registry_dir "$1")"; }
130
+ fm_pf_has_events() { fm_pf_dir_has_entry "$(fm_pf_events_dir "$1")"; }
131
+ # Every retained registration is an open public loop (owed or delivered). Same
132
+ # O(1) directory presence check as fm_pf_has_registrations; the name is the
133
+ # post-retention semantic so callers do not treat "a reply is owed" as the
134
+ # only reason a record exists.
135
+ fm_pf_has_open_loops() { fm_pf_has_registrations "$1"; }
136
+
137
+ # fm_pf_active <home> <state>: both gates, in order. The single predicate every
138
+ # caller outside the relay path should use before doing any public-followup work.
139
+ fm_pf_active() {
140
+ fm_pf_relay_active "$1" || return 1
141
+ fm_pf_has_registrations "$2" || fm_pf_has_events "$2"
142
+ }
143
+
144
+ # --- identifiers ------------------------------------------------------------
145
+
146
+ # fm_pf_slug_valid <value>: obligation ids, relation ids, work ids, and request
147
+ # ids all compose filenames. They arrive from tasks-axi, the relay, and child
148
+ # homes, so every one is checked against a conservative slug before use.
149
+ fm_pf_slug_valid() {
150
+ local v=$1
151
+ case "$v" in
152
+ ''|.*|*[!A-Za-z0-9._-]*) return 1 ;;
153
+ esac
154
+ [ "${#v}" -le 128 ]
155
+ }
156
+
157
+ # fm_pf_home_id_valid <home_id>: tasks-axi accepts "main" or
158
+ # "secondmate:<stable-id>" as a work_ref home. Validate the same shape here so a
159
+ # malformed source home is refused before it reaches a filename or a CLI call.
160
+ fm_pf_home_id_valid() {
161
+ local v=$1
162
+ case "$v" in
163
+ main) return 0 ;;
164
+ secondmate:*) fm_pf_slug_valid "${v#secondmate:}" ;;
165
+ *) return 1 ;;
166
+ esac
167
+ }
168
+
169
+ fm_pf_sha256() {
170
+ if command -v shasum >/dev/null 2>&1; then
171
+ shasum -a 256 2>/dev/null | awk '{print $1}'
172
+ elif command -v sha256sum >/dev/null 2>&1; then
173
+ sha256sum 2>/dev/null | awk '{print $1}'
174
+ else
175
+ return 1
176
+ fi
177
+ }
178
+
179
+ # fm_pf_event_id <obligation> <relation> <source_home> <work_id> <generation>
180
+ # <outcome_type> <deliverables-canonical>
181
+ # The stable idempotency identity. Derived from the identity tuple only, so the
182
+ # same terminal result always yields the same id no matter who emits it or how
183
+ # often. Public-safe outcome text is deliberately excluded: rewording the same
184
+ # landed outcome must not create a second event.
185
+ fm_pf_event_id() {
186
+ printf '%s\037%s\037%s\037%s\037%s\037%s\037%s' "$1" "$2" "$3" "$4" "$5" "$6" "$7" \
187
+ | fm_pf_sha256
188
+ }
189
+
190
+ # --- bounded public-safe text ----------------------------------------------
191
+
192
+ # fm_pf_clean_outcome_text: read stdin, drop control characters, collapse every
193
+ # whitespace run to a single space, and trim. An event line therefore stays
194
+ # single-line and a raw pasted public message cannot ride along inside it.
195
+ # Deliberately does NOT truncate: a byte-wise cut would split a multi-byte
196
+ # character, so length bounding happens where it can count codepoints - jq, at
197
+ # the point the typed event is built.
198
+ fm_pf_clean_outcome_text() {
199
+ LC_ALL=C tr -d '\000-\010\013\014\016-\037\177' \
200
+ | LC_ALL=C tr '\011\012\015' ' ' \
201
+ | LC_ALL=C tr -s ' ' \
202
+ | sed 's/^ //; s/ $//'
203
+ }
204
+
205
+ # fm_pf_bound_bytes <max>: hard byte cap for text that never becomes JSON, such
206
+ # as a quarantined event's one-line refusal reason.
207
+ fm_pf_bound_bytes() {
208
+ LC_ALL=C cut -b "1-$1"
209
+ }
210
+
211
+ # --- registry records -------------------------------------------------------
212
+
213
+ # fm_pf_registry_get <state> <obligation-id> <key>: read one key=value line from
214
+ # a registration record. Prints nothing and succeeds when absent.
215
+ fm_pf_registry_get() {
216
+ local state=$1 id=$2 key=$3 file line
217
+ fm_pf_slug_valid "$id" || return 1
218
+ file="$(fm_pf_registry_dir "$state")/$id"
219
+ [ -f "$file" ] && [ ! -L "$file" ] || return 0
220
+ line=$(grep -E "^${key}=" "$file" 2>/dev/null | tail -n1) || return 0
221
+ printf '%s' "${line#*=}"
222
+ }
223
+
224
+ # fm_pf_registry_ids <state>: every registered obligation id, one per line.
225
+ # The registry only ever holds this home's live public commitments, so this stays
226
+ # a bounded listing rather than a backlog scan.
227
+ fm_pf_registry_ids() {
228
+ local dir entry
229
+ dir=$(fm_pf_registry_dir "$1")
230
+ [ -d "$dir" ] && [ ! -L "$dir" ] || return 0
231
+ for entry in "$dir"/*; do
232
+ [ -f "$entry" ] && [ ! -L "$entry" ] || continue
233
+ basename "$entry"
234
+ done
235
+ }
236
+
237
+ # fm_pf_registry_ids_for_work <state> <work_home_id> <work_id>: the obligations
238
+ # this home registered against one exact work relation. Used by the completion
239
+ # guard so cleanup cannot declare bound work finished while its public promise is
240
+ # still open.
241
+ fm_pf_registry_ids_for_work() {
242
+ local state=$1 home_id=$2 work_id=$3 id
243
+ while IFS= read -r id; do
244
+ [ -n "$id" ] || continue
245
+ [ "$(fm_pf_registry_get "$state" "$id" work_home)" = "$home_id" ] || continue
246
+ [ "$(fm_pf_registry_get "$state" "$id" work_id)" = "$work_id" ] || continue
247
+ printf '%s\n' "$id"
248
+ done <<EOF
249
+ $(fm_pf_registry_ids "$state")
250
+ EOF
251
+ }
252
+
253
+ # fm_pf_now_epoch: wall clock as epoch seconds. FMX_NOW_OVERRIDE pins it for
254
+ # tests, matching bin/fm-x-lib.sh.
255
+ fm_pf_now_epoch() {
256
+ printf '%s\n' "${FMX_NOW_OVERRIDE:-$(date +%s)}"
257
+ }
258
+
259
+ # fm_pf_now_rfc3339: UTC timestamp for delivered_at and similar stamps.
260
+ fm_pf_now_rfc3339() {
261
+ local epoch
262
+ epoch=$(fm_pf_now_epoch)
263
+ date -u -r "$epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
264
+ || date -u -d "@$epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
265
+ || date -u +%Y-%m-%dT%H:%M:%SZ
266
+ }
267
+
268
+ # fm_pf_rfc3339_to_epoch <rfc3339>: parse a Zulu timestamp. Empty on failure.
269
+ fm_pf_rfc3339_to_epoch() {
270
+ local ts=$1
271
+ [ -n "$ts" ] || return 1
272
+ date -u -j -f '%Y-%m-%dT%H:%M:%SZ' "$ts" +%s 2>/dev/null \
273
+ || date -u -d "$ts" +%s 2>/dev/null \
274
+ || return 1
275
+ }
276
+
277
+ # fm_pf_followup_window_class <rfc3339>: ok, closing (<48h), expired, or unknown.
278
+ fm_pf_followup_window_class() {
279
+ local ts=$1 exp now
280
+ exp=$(fm_pf_rfc3339_to_epoch "$ts") || { printf 'unknown\n'; return 0; }
281
+ now=$(fm_pf_now_epoch)
282
+ if [ "$now" -ge "$exp" ]; then
283
+ printf 'expired\n'
284
+ elif [ $((exp - now)) -lt 172800 ]; then
285
+ printf 'closing\n'
286
+ else
287
+ printf 'ok\n'
288
+ fi
289
+ }
290
+
291
+ # fm_pf_b64_encode: stdin to a single-line base64 payload (no wrapping).
292
+ fm_pf_b64_encode() {
293
+ base64 2>/dev/null | tr -d '\n\r'
294
+ }
295
+
296
+ # fm_pf_b64_decode: stdin (single-line or wrapped base64) to bytes on stdout.
297
+ fm_pf_b64_decode() {
298
+ local data
299
+ data=$(cat)
300
+ printf '%s\n' "$data" | base64 -d 2>/dev/null \
301
+ || printf '%s\n' "$data" | base64 -D 2>/dev/null
302
+ }
303
+
304
+ # fm_pf_registry_loop_state <state> <id>: open or delivered. A pre-change
305
+ # record with no state= is treated as open so live homes never crash.
306
+ fm_pf_registry_loop_state() {
307
+ local v
308
+ v=$(fm_pf_registry_get "$1" "$2" state)
309
+ case "$v" in
310
+ delivered) printf 'delivered\n' ;;
311
+ *) printf 'open\n' ;;
312
+ esac
313
+ }
314
+
315
+ # fm_pf_registry_rechainable <state> <id>: 0 when request_context_b64 is present.
316
+ fm_pf_registry_rechainable() {
317
+ local ctx
318
+ ctx=$(fm_pf_registry_get "$1" "$2" request_context_b64)
319
+ [ -n "$ctx" ]
320
+ }
321
+
322
+ # fm_pf_has_delivered_open_loops <state>: 0 when any retained record is
323
+ # state=delivered (an open loop with nothing owed). Pre-change records have no
324
+ # state= and are treated as still-owed, not delivered.
325
+ fm_pf_has_delivered_open_loops() {
326
+ local state=$1 id
327
+ while IFS= read -r id; do
328
+ [ -n "$id" ] || continue
329
+ [ "$(fm_pf_registry_get "$state" "$id" state)" = delivered ] || continue
330
+ return 0
331
+ done <<EOF
332
+ $(fm_pf_registry_ids "$state")
333
+ EOF
334
+ return 1
335
+ }
336
+
337
+ fm_pf_registry_lock_path() {
338
+ printf '%s/.registry-%s.lock\n' "$(fm_pf_root "$1")" "$2"
339
+ }
340
+
341
+ fm_pf_registry_lock_acquire() {
342
+ local state=$1 id=$2
343
+ fm_pf_slug_valid "$id" || return 1
344
+ fmx_private_artifact_dir_prepare "$(fm_pf_root "$state")" >/dev/null || return 1
345
+ if ! command -v fm_lock_acquire_wait >/dev/null 2>&1; then
346
+ # shellcheck source=bin/fm-wake-lib.sh
347
+ . "$_FM_PF_LIB_DIR/fm-wake-lib.sh"
348
+ fi
349
+ fm_lock_acquire_wait "$(fm_pf_registry_lock_path "$state" "$id")"
350
+ }
351
+
352
+ fm_pf_registry_lock_release() {
353
+ fm_lock_release "$(fm_pf_registry_lock_path "$1" "$2")"
354
+ }
355
+
356
+ # fm_pf_registry_stamp_delivered <state> <id> <rfc3339>: rewrite one record
357
+ # with state=delivered and delivered_at, keeping every other field. The record
358
+ # stays; only retire removes it.
359
+ fm_pf_registry_stamp_delivered() {
360
+ local state=$1 id=$2 delivered_at=$3 file rest rc=0
361
+ fm_pf_slug_valid "$id" || return 1
362
+ [ -n "$delivered_at" ] || return 1
363
+ fm_pf_registry_lock_acquire "$state" "$id" || return 1
364
+ file="$(fm_pf_registry_dir "$state")/$id"
365
+ if [ -f "$file" ] && [ ! -L "$file" ]; then
366
+ rest=$(grep -v -E '^(state|delivered_at|delivered_obligation)=' "$file" 2>/dev/null || true)
367
+ printf '%s\nstate=delivered\ndelivered_at=%s\ndelivered_obligation=%s\n' \
368
+ "$rest" "$delivered_at" "$id" \
369
+ | fmx_private_artifact_publish_stdin "$(fm_pf_registry_dir "$state")" "$id" 600 \
370
+ || rc=$?
371
+ else
372
+ rc=3
373
+ fi
374
+ fm_pf_registry_lock_release "$state" "$id"
375
+ return "$rc"
376
+ }
377
+
378
+ # --- pending-event signature ------------------------------------------------
379
+
380
+ # Consumed by the sourcing scripts, not by this library.
381
+ # shellcheck disable=SC2034
382
+ FM_PF_SURFACED_BASENAME=surfaced
383
+
384
+ # fm_pf_events_signature <state>: a stable digest of the pending event id set.
385
+ # The relay poll compares it against the surfaced record so an unconsumed event
386
+ # wakes firstmate once per new event, not once per poll cycle.
387
+ fm_pf_events_signature() {
388
+ local dir entry pending_names=
389
+ dir=$(fm_pf_events_dir "$1")
390
+ [ -d "$dir" ] && [ ! -L "$dir" ] || return 1
391
+ for entry in "$dir"/*.json; do
392
+ [ -f "$entry" ] && [ ! -L "$entry" ] || continue
393
+ pending_names="$pending_names$(basename "$entry")
394
+ "
395
+ done
396
+ [ -n "$pending_names" ] || return 1
397
+ printf '%s' "$pending_names" | LC_ALL=C sort | fm_pf_sha256
398
+ }