@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,779 @@
1
+ # shellcheck shell=bash
2
+ # Fused backlog transitions for the scripts that own a task's physical record.
3
+ # Usage: . bin/fm-tasks-axi-lib.sh; . bin/fm-backlog-transition-lib.sh
4
+ # (this library reads that one's backend gate and never sources it itself, so a
5
+ # caller that already sourced it keeps its memoised compatibility verdict).
6
+ #
7
+ # INVARIANT. In ordinary successful lifecycle state, `state/<id>.meta` exists
8
+ # <=> this home's backlog row for <id> is In flight; the one teardown crash
9
+ # window is represented by `state/<id>.backlog-close`. The script performing the
10
+ # mechanical record change owns the paired backlog transition and runs it in the
11
+ # same process, under the per-task meta lock it already holds, before it reports
12
+ # success. Nothing else - not a later agent turn, not a printed reminder - is
13
+ # load-bearing for the pairing.
14
+ # bin/fm-spawn.sh meta published => `tasks-axi start`
15
+ # bin/fm-teardown.sh meta removed => `tasks-axi done`
16
+ # bin/fm-bootstrap.sh replays whatever a crash left behind, THIS HOME ONLY.
17
+ # bin/fm-fleet-snapshot.sh's classifier and bin/fm-secondmate-reconcile.sh's
18
+ # cross-home nudge stay defense in depth, not the primary mechanism.
19
+ #
20
+ # SCOPE. fm_backlog_transition_applies is the single gate. It excludes
21
+ # secondmates (persistent agents are never backlog items, AGENTS.md section 10),
22
+ # homes whose configured backlog backend is manual and homes that keep no
23
+ # backlog file at all. Those return-1 exemptions are never errors; an
24
+ # unresolvable configured data directory or incompatible tasks-axi instead
25
+ # returns 2 so callers refuse before mutation.
26
+ #
27
+ # ADDRESSING. Every call passes `--file <data>/backlog.md` so the mutation lands
28
+ # in the home that owns the task regardless of the caller's working directory,
29
+ # and runs from that data directory's parent so the same home's `.tasks.toml`
30
+ # supplies done_keep and the archive path. The parent of the data directory is
31
+ # the addressing root rather than FM_HOME, so a home whose data directory is
32
+ # relocated keeps its backlog and its archive together. A root with no
33
+ # `.tasks.toml` gets tasks-axi's built-in defaults.
34
+ #
35
+ # CRASH RECOVERY. Only teardown needs a durable record: it removes the meta and
36
+ # with it the completion links, so a process killed between the two halves would
37
+ # leave nothing to reconstruct the close from. It writes
38
+ # `state/<id>.backlog-close` first, and removes it once the close lands.
39
+ # The writer and replay share one complete-record validator, and teardown stages
40
+ # that record before destructive cleanup, so it never publishes or acts on a close
41
+ # replay would reject. The validator pins the data path to this home's configured
42
+ # root before any recovery mutation, then re-runs exactly that close.
43
+ # `tasks-axi done` on an already-closed task backfills links
44
+ # without moving the close date, so replay is idempotent. Spawn needs no marker:
45
+ # it publishes the meta first, so a crash
46
+ # leaves the meta itself as the evidence that the row is owed a start.
47
+
48
+ # Set by fm_backlog_transition_applies for a return-1 exemption.
49
+ # shellcheck disable=SC2034 # Output global, read by the sourcing caller.
50
+ FM_BACKLOG_TRANSITION_SKIP=
51
+ # Set by the mutating helpers when they return non-zero.
52
+ FM_BACKLOG_TRANSITION_ERROR=
53
+ FM_BACKLOG_ROW_RESULT=
54
+ FM_BACKLOG_ROW_STATE=
55
+ FM_BACKLOG_ROW_ERROR=
56
+ # Set by fm_backlog_close_marker_replay: closed | closed_incomplete | stale | noop.
57
+ # shellcheck disable=SC2034 # Output global, read by the sourcing caller.
58
+ FM_BACKLOG_CLOSE_REPLAY_RESULT=
59
+
60
+ # Emit each byte of a value as a decimal number, locale-independently.
61
+ # Deliberately perl rather than od: the spawn and teardown lifecycle runs under a
62
+ # curated PATH (tests/fm-teardown.test.sh make_path_without_lsof pins that set)
63
+ # that excludes od, and a validator that cannot run must never wedge dispatch or
64
+ # cleanup. perl is already in that curated set and is already used elsewhere in
65
+ # this repo for the same portability reason.
66
+ fm_backlog_bytes_of_string() { # <string>
67
+ perl -e 'print join(" ", unpack("C*", $ARGV[0])), "\n"' -- "$1"
68
+ }
69
+
70
+ fm_backlog_bytes_of_file() { # <path>
71
+ perl -e 'open(my $f, "<", $ARGV[0]) or exit 1; binmode $f; local $/; my $c = <$f>; $c = "" unless defined $c; print join(" ", unpack("C*", $c)), "\n"' -- "$1"
72
+ }
73
+
74
+ fm_backlog_control_bytes_valid() { # <allow-newline: 0|1> <od-bytes>
75
+ printf '%s\n' "$2" | awk -v allow_newline="$1" '
76
+ { for (i = 1; i <= NF; i++) if (($i < 32 && !(allow_newline && $i == 10)) || $i == 127) exit 1 }
77
+ '
78
+ }
79
+
80
+ fm_backlog_directory_present() {
81
+ local path=$1 label=$2 check=$1
82
+ while [ "$check" != / ] && [ "${check%/}" != "$check" ]; do
83
+ check=${check%/}
84
+ done
85
+ if [ ! -d "$check" ] || [ -L "$check" ]; then
86
+ FM_BACKLOG_TRANSITION_ERROR="$label is not a real directory at $path"
87
+ return 1
88
+ fi
89
+ }
90
+
91
+ fm_backlog_data_absolute() {
92
+ local data=$1 raw_bytes check
93
+ raw_bytes=$(fm_backlog_bytes_of_string "$data") || return 1
94
+ if ! fm_backlog_control_bytes_valid 0 "$raw_bytes"; then
95
+ printf 'error: data directory contains an invalid control byte\n' >&2
96
+ return 2
97
+ fi
98
+ check=$data
99
+ while [ "$check" != / ] && [ "${check%/}" != "$check" ]; do
100
+ check=${check%/}
101
+ done
102
+ if [ ! -d "$check" ]; then
103
+ FM_BACKLOG_TRANSITION_ERROR="data directory is not a directory at $data"
104
+ return 1
105
+ fi
106
+ if ! data=$(CDPATH='' cd -- "$data" 2>/dev/null && pwd -P); then
107
+ return 1
108
+ fi
109
+ printf '%s\n' "$data"
110
+ }
111
+
112
+ fm_backlog_file() { # <data-dir>
113
+ local data
114
+ data=$(fm_backlog_data_absolute "$1") || {
115
+ FM_BACKLOG_TRANSITION_ERROR="data directory cannot be resolved: $1"
116
+ return 1
117
+ }
118
+ if [ "$data" = / ]; then
119
+ printf '/backlog.md\n'
120
+ else
121
+ printf '%s/backlog.md\n' "$data"
122
+ fi
123
+ }
124
+
125
+ # The directory a backlog's own `.tasks.toml` is resolved from.
126
+ fm_backlog_root() { # <data-dir>
127
+ local data parent
128
+ data=$(fm_backlog_data_absolute "$1") || {
129
+ FM_BACKLOG_TRANSITION_ERROR="data directory cannot be resolved: $1"
130
+ return 1
131
+ }
132
+ case "$data" in
133
+ */*)
134
+ parent=${data%/*}
135
+ [ -n "$parent" ] || parent=/
136
+ ;;
137
+ *) parent=. ;;
138
+ esac
139
+ printf '%s\n' "$parent"
140
+ }
141
+
142
+ fm_backlog_data_relative() { # <data-dir>
143
+ local data root
144
+ data=$(fm_backlog_data_absolute "$1") || {
145
+ FM_BACKLOG_TRANSITION_ERROR="data directory cannot be resolved: $1"
146
+ return 1
147
+ }
148
+ root=$(fm_backlog_root "$data") || return 1
149
+ if [ "$data" = "$root" ]; then
150
+ printf '.\n'
151
+ return 0
152
+ fi
153
+ if [ "$root" = / ]; then
154
+ printf '%s\n' "${data#/}"
155
+ return 0
156
+ fi
157
+ case "$data" in
158
+ "$root"/*) printf '%s\n' "${data#"$root"/}" ;;
159
+ *) printf '%s\n' "$data" ;;
160
+ esac
161
+ }
162
+
163
+ fm_backlog_transition_applies() { # <config-dir> <data-dir> <kind>
164
+ local config=$1 data authorized_data=$2 kind=$3 file
165
+ FM_BACKLOG_TRANSITION_SKIP=
166
+ if [ "$kind" = secondmate ]; then
167
+ FM_BACKLOG_TRANSITION_SKIP="secondmates are not backlog items"
168
+ return 1
169
+ fi
170
+ if fm_backlog_backend_manual "$config"; then
171
+ FM_BACKLOG_TRANSITION_SKIP="config/backlog-backend selects manual editing"
172
+ return 1
173
+ fi
174
+ if ! data=$(fm_backlog_data_absolute "$2"); then
175
+ FM_BACKLOG_TRANSITION_ERROR="data directory cannot be resolved: $2"
176
+ return 2
177
+ fi
178
+ file=$(fm_backlog_file "$data")
179
+ if [ ! -e "$file" ] && [ ! -L "$file" ]; then
180
+ FM_BACKLOG_TRANSITION_SKIP="this home keeps no backlog at $file"
181
+ return 1
182
+ fi
183
+ if ! fm_backlog_record_present "$file" "backlog file" "$authorized_data"; then
184
+ return 2
185
+ fi
186
+ if ! fm_tasks_axi_compatible; then
187
+ FM_BACKLOG_TRANSITION_ERROR="automatic backlog transitions require tasks-axi $FM_TASKS_AXI_MIN or newer with the required update and mv features"
188
+ return 2
189
+ fi
190
+ return 0
191
+ }
192
+
193
+ fm_backlog_row_probe() { # <data-dir> <id>
194
+ local data authorized_data=$1 file id=$2 out state held blocked command_status
195
+ if ! data=$(fm_backlog_data_absolute "$1"); then
196
+ FM_BACKLOG_ROW_RESULT=error
197
+ FM_BACKLOG_ROW_STATE=
198
+ FM_BACKLOG_ROW_ERROR="data directory cannot be resolved: $1"
199
+ return 1
200
+ fi
201
+ FM_BACKLOG_ROW_RESULT=error
202
+ FM_BACKLOG_ROW_STATE=
203
+ FM_BACKLOG_ROW_ERROR=
204
+ file=$(fm_backlog_file "$data") || {
205
+ FM_BACKLOG_ROW_ERROR=$FM_BACKLOG_TRANSITION_ERROR
206
+ return 1
207
+ }
208
+ if ! fm_backlog_record_present "$file" "backlog file" "$authorized_data"; then
209
+ FM_BACKLOG_ROW_ERROR=$FM_BACKLOG_TRANSITION_ERROR
210
+ return 1
211
+ fi
212
+ out=$(cd "$(fm_backlog_root "$data")" 2>/dev/null && tasks-axi show "$id" \
213
+ --file "$file" 2>&1)
214
+ command_status=$?
215
+ if [ "$command_status" -ne 0 ]; then
216
+ if printf '%s\n' "$out" | grep -q '^code: NOT_FOUND$'; then
217
+ FM_BACKLOG_ROW_RESULT=not_found
218
+ else
219
+ FM_BACKLOG_ROW_ERROR=$(printf '%s\n' "$out" | sed -n '1p')
220
+ [ -n "$FM_BACKLOG_ROW_ERROR" ] \
221
+ || FM_BACKLOG_ROW_ERROR="tasks-axi show $id failed with no output"
222
+ fi
223
+ return "$command_status"
224
+ fi
225
+ state=$(printf '%s\n' "$out" | sed -n 's/^ state: *//p' | head -1)
226
+ held=$(printf '%s\n' "$out" | sed -n 's/^ held: *//p' | head -1)
227
+ blocked=$(printf '%s\n' "$out" | sed -n 's/^ blocked: *//p' | head -1)
228
+ if [ -z "$state" ]; then
229
+ FM_BACKLOG_ROW_ERROR="tasks-axi show $id returned no state"
230
+ return 1
231
+ fi
232
+ FM_BACKLOG_ROW_RESULT=found
233
+ FM_BACKLOG_ROW_STATE="$state ${held:-no} ${blocked:-no}"
234
+ return 0
235
+ }
236
+
237
+ # Run one tasks-axi mutation against <home>'s backlog, capturing its first
238
+ # output line in FM_BACKLOG_TRANSITION_ERROR on failure.
239
+ fm_backlog_mutate() { # <data-dir> <verb> <id> [flag...]
240
+ local data authorized_data=$1 file verb=$2 id=$3 out command_status
241
+ if ! data=$(fm_backlog_data_absolute "$1"); then
242
+ FM_BACKLOG_TRANSITION_ERROR="data directory cannot be resolved: $1"
243
+ return 1
244
+ fi
245
+ shift 3
246
+ FM_BACKLOG_TRANSITION_ERROR=
247
+ file=$(fm_backlog_file "$data") || return 1
248
+ fm_backlog_record_present "$file" "backlog file" "$authorized_data" || return 1
249
+ out=$(cd "$(fm_backlog_root "$data")" 2>/dev/null && tasks-axi "$verb" "$id" \
250
+ --file "$file" "$@" 2>&1)
251
+ command_status=$?
252
+ [ "$command_status" -ne 0 ] || return 0
253
+ FM_BACKLOG_TRANSITION_ERROR=$(printf '%s\n' "$out" | sed -n '1p')
254
+ [ -n "$FM_BACKLOG_TRANSITION_ERROR" ] \
255
+ || FM_BACKLOG_TRANSITION_ERROR="tasks-axi $verb $id failed with no output"
256
+ return "$command_status"
257
+ }
258
+
259
+ fm_backlog_start() { # <data-dir> <id>
260
+ fm_backlog_mutate "$1" start "$2"
261
+ }
262
+
263
+ fm_backlog_done() { # <data-dir> <id> [flag...]
264
+ local data=$1 id=$2
265
+ shift 2
266
+ fm_backlog_mutate "$data" "done" "$id" "$@"
267
+ }
268
+
269
+ fm_backlog_canonical_existing() {
270
+ LC_ALL=C perl -MCwd=realpath -e '
271
+ my $resolved = realpath($ARGV[0]);
272
+ exit 1 unless defined $resolved;
273
+ print $resolved;
274
+ ' "$1" 2>/dev/null
275
+ }
276
+
277
+ fm_backlog_record_parent_authorized() {
278
+ local path=$1 label=$2 root=$3 parent base parent_resolved expected_path
279
+ local path_resolved root_resolved home_resolved final_matches=1
280
+ parent=${path%/*}
281
+ [ "$parent" != "$path" ] || parent=.
282
+ base=${path##*/}
283
+ root_resolved=$(fm_backlog_canonical_existing "$root") || {
284
+ FM_BACKLOG_TRANSITION_ERROR="$label authorized directory cannot be resolved at $root"
285
+ return 1
286
+ }
287
+ [ -d "$root_resolved" ] || {
288
+ FM_BACKLOG_TRANSITION_ERROR="$label authorized directory is not a directory at $root"
289
+ return 1
290
+ }
291
+ if [ -n "${FM_HOME:-}" ]; then
292
+ case "$root" in
293
+ "$FM_HOME"|"$FM_HOME"/*)
294
+ home_resolved=$(fm_backlog_canonical_existing "$FM_HOME") || {
295
+ FM_BACKLOG_TRANSITION_ERROR="$label home directory cannot be resolved at $FM_HOME"
296
+ return 1
297
+ }
298
+ case "$root_resolved" in
299
+ "$home_resolved"|"$home_resolved"/*) ;;
300
+ *)
301
+ FM_BACKLOG_TRANSITION_ERROR="$label authorized directory resolves outside this home at $root"
302
+ return 1
303
+ ;;
304
+ esac
305
+ ;;
306
+ esac
307
+ fi
308
+ parent_resolved=$(fm_backlog_canonical_existing "$parent") || {
309
+ FM_BACKLOG_TRANSITION_ERROR="$label parent directory cannot be resolved at $path"
310
+ return 1
311
+ }
312
+ expected_path=${parent_resolved%/}/$base
313
+ if [ -e "$path" ] || [ -L "$path" ]; then
314
+ path_resolved=$(fm_backlog_canonical_existing "$path") || {
315
+ FM_BACKLOG_TRANSITION_ERROR="$label cannot be resolved at $path"
316
+ return 1
317
+ }
318
+ [ "$path_resolved" = "$expected_path" ] || final_matches=0
319
+ else
320
+ path_resolved=$expected_path
321
+ fi
322
+ case "$path_resolved" in
323
+ "$root_resolved"/*) ;;
324
+ *)
325
+ FM_BACKLOG_TRANSITION_ERROR="$label resolves outside its authorized directory at $path"
326
+ return 1
327
+ ;;
328
+ esac
329
+ if [ "$final_matches" != 1 ]; then
330
+ FM_BACKLOG_TRANSITION_ERROR="$label resolves through a different final path at $path"
331
+ return 1
332
+ fi
333
+ }
334
+
335
+ fm_backlog_record_present() {
336
+ local path=$1 label=${2:-record} root=$3
337
+ fm_backlog_record_parent_authorized "$path" "$label" "$root" || return 1
338
+ if [ ! -f "$path" ]; then
339
+ FM_BACKLOG_TRANSITION_ERROR="$label is not a regular file at $path"
340
+ return 1
341
+ fi
342
+ return 0
343
+ }
344
+
345
+ fm_backlog_record_remove() {
346
+ local path=$1 label=$2 root=$3
347
+ fm_backlog_record_parent_authorized "$path" "$label" "$root" || return 1
348
+ if [ -e "$path" ] || [ -L "$path" ]; then
349
+ fm_backlog_record_present "$path" "$label" "$root" || return 1
350
+ fi
351
+ if ! rm -f "$path" 2>/dev/null || [ -e "$path" ] || [ -L "$path" ]; then
352
+ FM_BACKLOG_TRANSITION_ERROR="$label could not be removed at $path"
353
+ return 1
354
+ fi
355
+ return 0
356
+ }
357
+
358
+ fm_backlog_record_publish() {
359
+ local source=$1 target=$2 label=$3 root=$4
360
+ fm_backlog_record_present "$source" "$label staged record" "$root" || return 1
361
+ fm_backlog_record_parent_authorized "$target" "$label target" "$root" || return 1
362
+ if [ -e "$target" ] || [ -L "$target" ]; then
363
+ fm_backlog_record_present "$target" "$label target" "$root" || return 1
364
+ fi
365
+ if ! mv -f "$source" "$target" 2>/dev/null || ! fm_backlog_record_present "$target" "$label" "$root"; then
366
+ [ -n "$FM_BACKLOG_TRANSITION_ERROR" ] \
367
+ || FM_BACKLOG_TRANSITION_ERROR="$label publication failed at $target"
368
+ return 1
369
+ fi
370
+ return 0
371
+ }
372
+
373
+ fm_backlog_meta_spawn_gen() {
374
+ local meta=$1 state=$2 count value
375
+ FM_BACKLOG_META_SPAWN_GEN=
376
+ fm_backlog_record_present "$meta" "task record" "$state" || return 1
377
+ count=$(LC_ALL=C awk -F= '$1 == "spawn_gen" { count++ } END { print count + 0 }' "$meta" 2>/dev/null) || {
378
+ FM_BACKLOG_TRANSITION_ERROR="unreadable spawn generation in task record $meta"
379
+ return 1
380
+ }
381
+ if [ "$count" -ne 1 ]; then
382
+ FM_BACKLOG_TRANSITION_ERROR="task record $meta has $count spawn generation fields; exactly one is required"
383
+ return 1
384
+ fi
385
+ value=$(LC_ALL=C awk -F= '$1 == "spawn_gen" { sub(/^[^=]*=/, ""); print }' "$meta" 2>/dev/null) || {
386
+ FM_BACKLOG_TRANSITION_ERROR="unreadable spawn generation in task record $meta"
387
+ return 1
388
+ }
389
+ case "$value" in
390
+ ''|.*|*[!A-Za-z0-9._-]*)
391
+ FM_BACKLOG_TRANSITION_ERROR="invalid spawn generation in task record $meta"
392
+ return 1
393
+ ;;
394
+ esac
395
+ FM_BACKLOG_META_SPAWN_GEN=$value
396
+ }
397
+
398
+ fm_backlog_row_dispatchable() {
399
+ case "$1" in
400
+ in_flight\ no\ no|queued\ no\ no) return 0 ;;
401
+ *) return 1 ;;
402
+ esac
403
+ }
404
+
405
+ fm_backlog_dispatch_transition() {
406
+ local meta=$1 data=$2 id=$3 state=$4 row row_status
407
+ fm_backlog_record_present "$meta" "task record" "$state" || return 1
408
+ fm_backlog_row_probe "$data" "$id"
409
+ row_status=$?
410
+ if [ "$row_status" -ne 0 ]; then
411
+ if [ "$FM_BACKLOG_ROW_RESULT" = not_found ]; then
412
+ FM_BACKLOG_TRANSITION_ERROR="backlog item $id vanished before dispatch commit"
413
+ else
414
+ FM_BACKLOG_TRANSITION_ERROR=$FM_BACKLOG_ROW_ERROR
415
+ fi
416
+ return "$row_status"
417
+ fi
418
+ row=$FM_BACKLOG_ROW_STATE
419
+ if ! fm_backlog_row_dispatchable "$row"; then
420
+ FM_BACKLOG_TRANSITION_ERROR="backlog item $id is not dispatchable in state $row"
421
+ return 1
422
+ fi
423
+ case "$row" in
424
+ in_flight\ no\ no) return 0 ;;
425
+ queued\ no\ no) fm_backlog_start "$data" "$id" ;;
426
+ esac
427
+ }
428
+
429
+ fm_backlog_dispatch_rollback() {
430
+ local meta=$1 busy_script=$2 state=$3 id=$4 gen=$5 failed=0
431
+ fm_backlog_record_remove "$meta" "provisional task record" "$state" || failed=1
432
+ if [ -n "$gen" ]; then
433
+ "$busy_script" retire "$state" "$id" --gen "$gen" >/dev/null 2>&1 || failed=1
434
+ if [ -e "$state/$id.busy-state" ] || [ -L "$state/$id.busy-state" ] \
435
+ || [ -e "$state/$id.busy-gen" ] || [ -L "$state/$id.busy-gen" ]; then
436
+ failed=1
437
+ fi
438
+ fi
439
+ if [ "$failed" -ne 0 ]; then
440
+ FM_BACKLOG_TRANSITION_ERROR="failed-dispatch cleanup did not remove both task and busy records for $id"
441
+ return 1
442
+ fi
443
+ return 0
444
+ }
445
+
446
+ fm_backlog_close_transition() {
447
+ local meta=$1 marker=$2 data=$3 id=$4 state=$5
448
+ shift 5
449
+ [ -z "$meta" ] || fm_backlog_record_remove "$meta" "task record" "$state" || return 1
450
+ fm_backlog_done "$data" "$id" "$@" || return 1
451
+ fm_backlog_record_remove "$marker" "pending-close record" "$state"
452
+ }
453
+
454
+ fm_backlog_atomic_transition() {
455
+ local operation=$1
456
+ shift
457
+ case "$operation" in
458
+ publish) fm_backlog_record_publish "$@" ;;
459
+ remove) fm_backlog_record_remove "$@" ;;
460
+ dispatch) fm_backlog_dispatch_transition "$@" ;;
461
+ rollback) fm_backlog_dispatch_rollback "$@" ;;
462
+ close) fm_backlog_close_transition "$@" ;;
463
+ *) FM_BACKLOG_TRANSITION_ERROR="unknown backlog atomic transition $operation"; return 2 ;;
464
+ esac
465
+ }
466
+
467
+ fm_backlog_close_marker_path() { # <state-dir> <id>
468
+ printf '%s/%s.backlog-close\n' "$1" "$2"
469
+ }
470
+
471
+ fm_backlog_close_marker_validate() { # <marker-path> <authorized-data-dir> <expected-id> <state-dir>
472
+ local marker=$1 authorized_data data_resolved expected_id=$3 state=$4
473
+ local id='' data='' marker_spawn_gen='' cleanup_incomplete=0 line raw_bytes arg_value
474
+ local url_tail url_authority url_path url_host url_port host_rest host_label host_valid
475
+ local percent_tail percent_valid
476
+ local id_count=0 data_count=0 spawn_gen_count=0 cleanup_incomplete_count=0
477
+ local args=()
478
+ FM_BACKLOG_CLOSE_VALIDATED_ID=
479
+ FM_BACKLOG_CLOSE_VALIDATED_DATA=
480
+ FM_BACKLOG_CLOSE_VALIDATED_SPAWN_GEN=
481
+ FM_BACKLOG_CLOSE_VALIDATED_CLEANUP_INCOMPLETE=0
482
+ FM_BACKLOG_CLOSE_VALIDATED_ARGS=()
483
+ fm_backlog_record_present "$marker" "pending-close record" "$state" || return 1
484
+ raw_bytes=$(fm_backlog_bytes_of_file "$marker" 2>/dev/null) || {
485
+ FM_BACKLOG_TRANSITION_ERROR="unreadable pending-close record $marker"
486
+ return 1
487
+ }
488
+ if ! fm_backlog_control_bytes_valid 1 "$raw_bytes"; then
489
+ FM_BACKLOG_TRANSITION_ERROR="invalid control byte in pending-close record $marker"
490
+ return 1
491
+ fi
492
+ while IFS= read -r line || [ -n "$line" ]; do
493
+ case "$line" in
494
+ id=*) id=${line#id=}; id_count=$((id_count + 1)) ;;
495
+ data=*) data=${line#data=}; data_count=$((data_count + 1)) ;;
496
+ spawn_gen=*) marker_spawn_gen=${line#spawn_gen=}; spawn_gen_count=$((spawn_gen_count + 1)) ;;
497
+ cleanup_incomplete=*) cleanup_incomplete=${line#cleanup_incomplete=}; cleanup_incomplete_count=$((cleanup_incomplete_count + 1)) ;;
498
+ arg=*) args+=("${line#arg=}") ;;
499
+ *) FM_BACKLOG_TRANSITION_ERROR="unreadable pending-close record $marker"; return 1 ;;
500
+ esac
501
+ done < "$marker"
502
+ case "$id" in
503
+ ''|.*|*[!A-Za-z0-9._-]*)
504
+ FM_BACKLOG_TRANSITION_ERROR="invalid task identity in pending-close record $marker"
505
+ return 1
506
+ ;;
507
+ esac
508
+ if [ "$id_count" -ne 1 ] || [ "$id" != "$expected_id" ] \
509
+ || [ "$data_count" -ne 1 ] || [ -z "$data" ] \
510
+ || [ "$spawn_gen_count" -ne 1 ]; then
511
+ FM_BACKLOG_TRANSITION_ERROR="unreadable pending-close record $marker"
512
+ return 1
513
+ fi
514
+ case "$marker_spawn_gen" in
515
+ ''|.*|*[!A-Za-z0-9._-]*)
516
+ FM_BACKLOG_TRANSITION_ERROR="invalid spawn generation in pending-close record $marker"
517
+ return 1
518
+ ;;
519
+ esac
520
+ if [ "$cleanup_incomplete_count" -gt 1 ]; then
521
+ FM_BACKLOG_TRANSITION_ERROR="unreadable pending-close record $marker"
522
+ return 1
523
+ fi
524
+ case "$cleanup_incomplete" in
525
+ 0|1) ;;
526
+ *)
527
+ FM_BACKLOG_TRANSITION_ERROR="invalid cleanup state in pending-close record $marker"
528
+ return 1
529
+ ;;
530
+ esac
531
+ case "$data" in
532
+ /*) ;;
533
+ *) FM_BACKLOG_TRANSITION_ERROR="invalid data directory in pending-close record $marker"; return 1 ;;
534
+ esac
535
+ case "$data" in
536
+ */../*|*/..)
537
+ FM_BACKLOG_TRANSITION_ERROR="invalid data directory in pending-close record $marker"
538
+ return 1
539
+ ;;
540
+ esac
541
+ authorized_data=$(fm_backlog_data_absolute "$2") || {
542
+ FM_BACKLOG_TRANSITION_ERROR="authorized data directory cannot be resolved: $2"
543
+ return 1
544
+ }
545
+ data_resolved=$(fm_backlog_data_absolute "$data") || {
546
+ FM_BACKLOG_TRANSITION_ERROR="data directory in pending-close record cannot be resolved: $data"
547
+ return 1
548
+ }
549
+ if [ "$data_resolved" != "$authorized_data" ]; then
550
+ FM_BACKLOG_TRANSITION_ERROR="foreign data directory in pending-close record $marker"
551
+ return 1
552
+ fi
553
+ case "${#args[@]}" in
554
+ 0) ;;
555
+ 2)
556
+ case "${args[0]}" in
557
+ --note) [ "${args[1]}" = "local%20main" ] ;;
558
+ --pr)
559
+ arg_value=${args[1]}
560
+ [ "${#arg_value}" -le 2048 ] \
561
+ && case "$arg_value" in https://*) true ;; *) false ;; esac \
562
+ && case "$arg_value" in
563
+ *[[:space:]]*|*[!A-Za-z0-9:/?\&=._#%+~@-]*) false ;;
564
+ *) true ;;
565
+ esac \
566
+ && {
567
+ url_tail=${arg_value#https://}
568
+ url_authority=${url_tail%%/*}
569
+ url_path=${url_tail#*/}
570
+ url_host=$url_authority
571
+ url_port=
572
+ case "$url_authority" in
573
+ *:*) url_host=${url_authority%%:*}; url_port=${url_authority#*:} ;;
574
+ esac
575
+ [ "$url_path" != "$url_tail" ] \
576
+ && case "$url_host" in
577
+ ''|[-.]*|*[-.]|*..*|*[!A-Za-z0-9.-]*) false ;;
578
+ *[A-Za-z0-9]*) true ;;
579
+ *) false ;;
580
+ esac \
581
+ && {
582
+ host_rest=$url_host
583
+ host_valid=1
584
+ while :; do
585
+ host_label=${host_rest%%.*}
586
+ case "$host_label" in ''|-*|*-) host_valid=0; break ;; esac
587
+ [ "$host_rest" = "$host_label" ] && break
588
+ host_rest=${host_rest#*.}
589
+ done
590
+ [ "$host_valid" = 1 ]
591
+ } \
592
+ && case "$url_authority" in
593
+ *:*) case "$url_port" in ''|*[!0-9]*|??????*) false ;; *) true ;; esac ;;
594
+ *) true ;;
595
+ esac \
596
+ && case "$url_path" in *[A-Za-z0-9]*) true ;; *) false ;; esac \
597
+ && {
598
+ percent_tail=$url_path
599
+ percent_valid=1
600
+ while case "$percent_tail" in *%*) true ;; *) false ;; esac; do
601
+ percent_tail=${percent_tail#*%}
602
+ case "$percent_tail" in
603
+ [0-9A-Fa-f][0-9A-Fa-f]*) percent_tail=${percent_tail#??} ;;
604
+ *) percent_valid=0; break ;;
605
+ esac
606
+ done
607
+ [ "$percent_valid" = 1 ]
608
+ }
609
+ }
610
+ ;;
611
+ --report)
612
+ arg_value=${args[1]}
613
+ [ "${#arg_value}" -le 4096 ] \
614
+ && [ -n "${arg_value// /}" ] \
615
+ && case "$arg_value" in .|..|-*|/*|../*|*/../*|*/..) false ;; *) true ;; esac
616
+ ;;
617
+ *) false ;;
618
+ esac || { FM_BACKLOG_TRANSITION_ERROR="invalid pending-close arguments in $marker"; return 1; }
619
+ ;;
620
+ *) FM_BACKLOG_TRANSITION_ERROR="invalid pending-close arguments in $marker"; return 1 ;;
621
+ esac
622
+ FM_BACKLOG_CLOSE_VALIDATED_ID=$id
623
+ FM_BACKLOG_CLOSE_VALIDATED_DATA=$data_resolved
624
+ FM_BACKLOG_CLOSE_VALIDATED_SPAWN_GEN=$marker_spawn_gen
625
+ FM_BACKLOG_CLOSE_VALIDATED_CLEANUP_INCOMPLETE=$cleanup_incomplete
626
+ FM_BACKLOG_CLOSE_VALIDATED_ARGS=("${args[@]+"${args[@]}"}")
627
+ }
628
+
629
+ fm_backlog_close_marker_stage() { # <temporary-path> <id> <data-dir> <spawn-gen> <state-dir> <cleanup-incomplete: 0|1> [flag...]
630
+ local tmp=$1 id=$2 data spawn_gen=$4 state=$5 cleanup_incomplete=$6 arg previous_arg=''
631
+ local serialized_args=()
632
+ data=$(fm_backlog_data_absolute "$3") || {
633
+ FM_BACKLOG_TRANSITION_ERROR="data directory cannot be resolved: $3"
634
+ return 1
635
+ }
636
+ fm_backlog_record_parent_authorized "$tmp" "pending-close staging path" "$state" || return 1
637
+ if [ -e "$tmp" ] || [ -L "$tmp" ]; then
638
+ FM_BACKLOG_TRANSITION_ERROR="unsafe pending-close staging path $tmp"
639
+ return 1
640
+ fi
641
+ case "$cleanup_incomplete" in
642
+ 0|1) ;;
643
+ *) FM_BACKLOG_TRANSITION_ERROR="invalid pending-close cleanup state"; return 1 ;;
644
+ esac
645
+ shift 6
646
+ for arg in "$@"; do
647
+ if [ "$previous_arg" = --note ] && [ "$arg" = "local main" ]; then
648
+ serialized_args+=("local%20main")
649
+ else
650
+ serialized_args+=("$arg")
651
+ fi
652
+ previous_arg=$arg
653
+ done
654
+ {
655
+ printf 'id=%s\n' "$id"
656
+ printf 'data=%s\n' "$data"
657
+ printf 'spawn_gen=%s\n' "$spawn_gen"
658
+ printf 'cleanup_incomplete=%s\n' "$cleanup_incomplete"
659
+ for arg in "${serialized_args[@]+"${serialized_args[@]}"}"; do
660
+ printf 'arg=%s\n' "$arg"
661
+ done
662
+ } > "$tmp" || { rm -f "$tmp"; return 1; }
663
+ fm_backlog_close_marker_validate "$tmp" "$data" "$id" "$state" \
664
+ || { rm -f "$tmp"; return 1; }
665
+ }
666
+
667
+ # Record the exact close a teardown is about to perform.
668
+ fm_backlog_close_marker_write() { # <state-dir> <id> <data-dir> <spawn-gen> [flag...]
669
+ local state=$1 id=$2 data=$3 spawn_gen=$4 marker tmp
670
+ fm_backlog_directory_present "$state" "state directory" || return 1
671
+ shift 4
672
+ marker=$(fm_backlog_close_marker_path "$state" "$id") || return 1
673
+ tmp="$state/.$id.backlog-close.${BASHPID:-$$}"
674
+ fm_backlog_close_marker_stage "$tmp" "$id" "$data" "$spawn_gen" "$state" 0 "$@" || return 1
675
+ fm_backlog_atomic_transition publish "$tmp" "$marker" "pending-close record" "$state" \
676
+ || { rm -f "$tmp"; return 1; }
677
+ }
678
+
679
+ fm_backlog_close_marker_mark_cleanup_incomplete() { # <state-dir> <marker-path> <id> <data-dir> <spawn-gen> [flag...]
680
+ local state=$1 marker=$2 id=$3 data=$4 spawn_gen=$5 tmp
681
+ shift 5
682
+ tmp="$state/.$id.backlog-close.${BASHPID:-$$}"
683
+ fm_backlog_close_marker_stage "$tmp" "$id" "$data" "$spawn_gen" "$state" 1 "$@" || return 1
684
+ fm_backlog_atomic_transition publish "$tmp" "$marker" "pending-close record" "$state" \
685
+ || { rm -f "$tmp"; return 1; }
686
+ }
687
+
688
+ fm_backlog_close_marker_remove() { # <marker-path> <state-dir>
689
+ fm_backlog_atomic_transition remove "$1" "pending-close record" "$2"
690
+ }
691
+
692
+ fm_backlog_close_marker_clear() { # <state-dir> <id>
693
+ local marker
694
+ marker=$(fm_backlog_close_marker_path "$1" "$2") || return 1
695
+ fm_backlog_close_marker_remove "$marker" "$1"
696
+ }
697
+
698
+ # Replay one recorded close. Returns 0 when the row is closed or the marker is
699
+ # stale, and 1 when marker validation or recovery fails. Validation completes
700
+ # before any meta or backlog mutation.
701
+ fm_backlog_close_marker_replay() { # <state-dir> <marker-path> <authorized-data-dir>
702
+ local state=$1 marker=$2 marker_name expected_id
703
+ local id data marker_spawn_gen meta meta_spawn_gen row_state cleanup_incomplete
704
+ local args=()
705
+ FM_BACKLOG_CLOSE_REPLAY_RESULT=noop
706
+ fm_backlog_directory_present "$state" "state directory" || return 1
707
+ [ -e "$marker" ] || [ -L "$marker" ] || return 0
708
+ marker_name=${marker##*/}
709
+ case "$marker_name" in
710
+ *.backlog-close) expected_id=${marker_name%.backlog-close} ;;
711
+ *) FM_BACKLOG_TRANSITION_ERROR="invalid pending-close record name $marker"; return 1 ;;
712
+ esac
713
+ fm_backlog_close_marker_validate "$marker" "$3" "$expected_id" "$state" || return 1
714
+ id=$FM_BACKLOG_CLOSE_VALIDATED_ID
715
+ data=$FM_BACKLOG_CLOSE_VALIDATED_DATA
716
+ marker_spawn_gen=$FM_BACKLOG_CLOSE_VALIDATED_SPAWN_GEN
717
+ cleanup_incomplete=$FM_BACKLOG_CLOSE_VALIDATED_CLEANUP_INCOMPLETE
718
+ args=("${FM_BACKLOG_CLOSE_VALIDATED_ARGS[@]+"${FM_BACKLOG_CLOSE_VALIDATED_ARGS[@]}"}")
719
+ if [ "${args[0]-}" = --note ]; then
720
+ args[1]="local main"
721
+ fi
722
+ meta="$state/$id.meta"
723
+ if [ -e "$meta" ] || [ -L "$meta" ]; then
724
+ if ! fm_backlog_record_present "$meta" "task record" "$state"; then
725
+ FM_BACKLOG_TRANSITION_ERROR="unsafe interrupted task record at $meta"
726
+ return 1
727
+ fi
728
+ fm_backlog_meta_spawn_gen "$meta" "$state" || return 1
729
+ meta_spawn_gen=$FM_BACKLOG_META_SPAWN_GEN
730
+ if [ "$meta_spawn_gen" != "$marker_spawn_gen" ]; then
731
+ fm_backlog_close_marker_remove "$marker" "$state" || return 1
732
+ FM_BACKLOG_CLOSE_REPLAY_RESULT=stale
733
+ return 0
734
+ fi
735
+ fm_backlog_close_marker_mark_cleanup_incomplete "$state" "$marker" "$id" "$data" \
736
+ "$marker_spawn_gen" "${args[@]+"${args[@]}"}" || return 1
737
+ cleanup_incomplete=1
738
+ fm_backlog_atomic_transition remove "$meta" "the interrupted task record" "$state" \
739
+ || return 1
740
+ fi
741
+ if fm_backlog_row_probe "$data" "$id"; then
742
+ row_state=$FM_BACKLOG_ROW_STATE
743
+ else
744
+ if [ "$FM_BACKLOG_ROW_RESULT" != not_found ]; then
745
+ FM_BACKLOG_TRANSITION_ERROR=$FM_BACKLOG_ROW_ERROR
746
+ return 1
747
+ fi
748
+ row_state=
749
+ fi
750
+ case "$row_state" in
751
+ done\ *)
752
+ if fm_backlog_atomic_transition close '' "$marker" "$data" "$id" "$state" \
753
+ "${args[@]+"${args[@]}"}"; then
754
+ if [ "$cleanup_incomplete" = 1 ]; then
755
+ FM_BACKLOG_CLOSE_REPLAY_RESULT=closed_incomplete
756
+ else
757
+ FM_BACKLOG_CLOSE_REPLAY_RESULT=closed
758
+ fi
759
+ return 0
760
+ fi
761
+ return 1
762
+ ;;
763
+ '')
764
+ fm_backlog_close_marker_remove "$marker" "$state" || return 1
765
+ FM_BACKLOG_CLOSE_REPLAY_RESULT=stale
766
+ return 0
767
+ ;;
768
+ esac
769
+ if fm_backlog_atomic_transition close '' "$marker" "$data" "$id" "$state" \
770
+ "${args[@]+"${args[@]}"}"; then
771
+ if [ "$cleanup_incomplete" = 1 ]; then
772
+ FM_BACKLOG_CLOSE_REPLAY_RESULT=closed_incomplete
773
+ else
774
+ FM_BACKLOG_CLOSE_REPLAY_RESULT=closed
775
+ fi
776
+ return 0
777
+ fi
778
+ return 1
779
+ }