@zerwiz/ymir 0.1.3 → 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 -39
  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,708 @@
1
+ #!/usr/bin/env bash
2
+ # Behavior tests for Cursor Agent CLI as a firstmate PRIMARY
3
+ # (docs/turnend-guard.md, docs/sessionstart-nudge.md,
4
+ # docs/supervision-protocols/cursor.md).
5
+ #
6
+ # Four layers, all hermetic over temp dirs with real processes and NO cursor
7
+ # installed, so CI enforces them everywhere:
8
+ # HOST GUARD - bin/fm-hook-host-lib.sh, and each tracked Claude-shaped hook
9
+ # entrypoint standing down on a Cursor-delivered payload, which
10
+ # is what keeps a Cursor primary from running every covered
11
+ # event twice.
12
+ # PARK - bin/fm-turnend-guard-cursor.sh, the stop-hook park: its
13
+ # follow-up sources, its double loop bound, its bounded repair
14
+ # nag, and its post-claim supersession contract.
15
+ # SESSION - bin/fm-sessionstart-cursor.sh, which injects the digest at
16
+ # sessionStart.
17
+ #
18
+ # The park runs as a child of a fake harness (a bash symlink named cursor-agent)
19
+ # whose pid holds the fixture home's session lock, so the real Cursor ancestry
20
+ # path in bin/fm-session-lock-lib.sh is exercised rather than stubbed.
21
+ # tests/fm-cursor-primary-live-e2e.test.sh is the opt-in guard against a real
22
+ # cursor-agent. Neither replaces the other.
23
+ # shellcheck disable=SC2016 # single quotes are deliberate: $FM_HOME expands inside the fake harness child
24
+ set -u
25
+
26
+ # shellcheck source=tests/lib.sh
27
+ . "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
28
+
29
+ TMP_ROOT=$(fm_test_tmproot fm-cursor-primary)
30
+ fm_git_identity fmtest fmtest@example.invalid
31
+
32
+ FAKEBIN=$(fm_fakebin "$TMP_ROOT/fakebin")
33
+ # Use a real executable whose own canonical basename is cursor-agent. A symlink
34
+ # to bash is not sufficient on Linux: /proc resolves it to bash, so the real
35
+ # Cursor ancestry classifier correctly rejects that process as an impostor.
36
+ CC_BIN=$(command -v cc 2>/dev/null || command -v gcc 2>/dev/null || true)
37
+ [ -n "$CC_BIN" ] || fail "a C compiler is required to build the fake Cursor process"
38
+ cat > "$TMP_ROOT/fake-cursor.c" <<'C'
39
+ #include <errno.h>
40
+ #include <string.h>
41
+ #include <sys/wait.h>
42
+ #include <unistd.h>
43
+
44
+ int main(int argc, char **argv) {
45
+ int status;
46
+ pid_t child;
47
+ if (argc != 3 || strcmp(argv[1], "-c") != 0) return 64;
48
+ child = fork();
49
+ if (child < 0) return 70;
50
+ if (child == 0) {
51
+ execl("/bin/bash", "bash", "-c", argv[2], (char *)0);
52
+ _exit(127);
53
+ }
54
+ while (waitpid(child, &status, 0) < 0) {
55
+ if (errno != EINTR) return 71;
56
+ }
57
+ if (WIFEXITED(status)) return WEXITSTATUS(status);
58
+ if (WIFSIGNALED(status)) return 128 + WTERMSIG(status);
59
+ return 72;
60
+ }
61
+ C
62
+ "$CC_BIN" -o "$FAKEBIN/cursor-agent" "$TMP_ROOT/fake-cursor.c" \
63
+ || fail "could not build the fake Cursor process"
64
+ FAKE_CURSOR="$FAKEBIN/cursor-agent"
65
+
66
+ CURSOR_PAYLOAD='{"session_id":"sess-cursor","generation_id":"gen-1","loop_count":0,"status":"completed","hook_event_name":"stop","cursor_version":"2026.08.11-e8db854"}'
67
+ CLAUDE_STOP_PAYLOAD='{"session_id":"sess-claude","stop_hook_active":false}'
68
+
69
+ install_scripts() {
70
+ local dir=$1 f
71
+ mkdir -p "$dir/bin" "$dir/docs"
72
+ for f in fm-turnend-guard-cursor.sh fm-turnend-guard.sh fm-sessionstart-cursor.sh \
73
+ fm-sessionstart-run.sh fm-sessionstart-nudge.sh fm-arm-pretool-check.sh \
74
+ fm-cd-pretool-check.sh fm-claude-stop-autoarm.sh fm-hook-host-lib.sh \
75
+ fm-primary-scope-lib.sh fm-supervision-lib.sh fm-wake-lib.sh \
76
+ fm-session-lock-lib.sh fm-cursor-lib.sh fm-operational-input.sh \
77
+ fm-supervision-instructions.sh fm-harness.sh fm-lock.sh \
78
+ fm-gate-refuse-lib.sh; do
79
+ cp "$ROOT/bin/$f" "$dir/bin/$f"
80
+ done
81
+ cp "$ROOT/bin/fm-arm-command-policy.mjs" "$dir/bin/fm-arm-command-policy.mjs"
82
+ cp "$ROOT/bin/fm-cd-command-policy.mjs" "$dir/bin/fm-cd-command-policy.mjs"
83
+ cp -R "$ROOT/docs/supervision-protocols" "$dir/docs/supervision-protocols"
84
+ chmod +x "$dir"/bin/*.sh
85
+ }
86
+
87
+ make_primary_dir() {
88
+ local dir=$1
89
+ mkdir -p "$dir/state"
90
+ git init -q "$dir"
91
+ git -C "$dir" commit -q --allow-empty -m init
92
+ : > "$dir/AGENTS.md"
93
+ install_scripts "$dir"
94
+ printf '%s\n' "$dir"
95
+ }
96
+
97
+ # An arm fixture standing in for bin/fm-watch-arm.sh. Real process, real output.
98
+ write_arm_fixture() { # <dir> <kind>
99
+ local dir=$1 kind=$2
100
+ case "$kind" in
101
+ actionable)
102
+ cat > "$dir/bin/fm-watch-arm.sh" <<'SH'
103
+ #!/usr/bin/env bash
104
+ printf '%s\n' "$$" >> "$FM_HOME/state/arm-ran"
105
+ printf 'watcher: started pid=%s (beacon fresh)\n' "$$"
106
+ printf 'stale: fixture-win needs a look\n'
107
+ exit 0
108
+ SH
109
+ ;;
110
+ failed)
111
+ cat > "$dir/bin/fm-watch-arm.sh" <<'SH'
112
+ #!/usr/bin/env bash
113
+ printf '%s\n' "$$" >> "$FM_HOME/state/arm-ran"
114
+ printf 'watcher: FAILED - no live watcher with a fresh beacon\n'
115
+ exit 1
116
+ SH
117
+ ;;
118
+ switchable)
119
+ # Slow until state/arm-fast appears, so a second invocation can be made
120
+ # fast WITHOUT rewriting a script the first one is still executing.
121
+ cat > "$dir/bin/fm-watch-arm.sh" <<'SH'
122
+ #!/usr/bin/env bash
123
+ printf '%s\n' "$$" >> "$FM_HOME/state/arm-ran"
124
+ if [ -e "$FM_HOME/state/arm-fast" ]; then
125
+ printf 'stale: fixture-win fast\n'
126
+ exit 0
127
+ fi
128
+ sleep 30
129
+ printf 'stale: fixture-win late\n'
130
+ exit 0
131
+ SH
132
+ ;;
133
+ esac
134
+ chmod +x "$dir/bin/fm-watch-arm.sh"
135
+ }
136
+
137
+ # The park's child body: claim the home lock as this fake harness process, then
138
+ # run the adapter as its child, so the real Cursor ancestry path decides lock
139
+ # ownership on every platform. Keep the fake harness process alive: Linux
140
+ # changes the process identity when an exec reaches the adapter's shebang.
141
+ PARK_CHILD='
142
+ printf "%s\n" "$$" > "$FM_HOME/state/.lock"
143
+ "$FM_HOME/bin/fm-turnend-guard-cursor.sh"
144
+ '
145
+
146
+ # Run the park as a child of the fake cursor harness that holds the home lock.
147
+ # Clear PI_CODING_AGENT so a Pi host session running this suite cannot make the
148
+ # Cursor park stand down before the fixture under test is exercised.
149
+ run_park() { # <dir> [loop_count] [loop_ceiling]
150
+ local dir=$1 loop=${2:-0} ceiling=${3:-} payload
151
+ payload=$(printf '{"session_id":"sess-cursor","generation_id":"gen-%s","loop_count":%s,"status":"completed","hook_event_name":"stop","cursor_version":"2026.08.11-e8db854"}' "$loop" "$loop")
152
+ if [ -n "$ceiling" ]; then
153
+ printf '%s' "$payload" | env -u PI_CODING_AGENT FM_HOME="$dir" FM_CURSOR_PARK_POLL=1 \
154
+ FM_CURSOR_TURNEND_LOOP_CEILING="$ceiling" "$FAKE_CURSOR" -c "$PARK_CHILD" 2>/dev/null
155
+ else
156
+ printf '%s' "$payload" | env -u PI_CODING_AGENT FM_HOME="$dir" FM_CURSOR_PARK_POLL=1 \
157
+ "$FAKE_CURSOR" -c "$PARK_CHILD" 2>/dev/null
158
+ fi
159
+ }
160
+
161
+ run_session() { # <dir> <event> <source> [session-id]
162
+ local dir=$1 event=$2 source=$3 session_id=${4:-sess-cursor} payload
163
+ payload=$(printf '{"hook_event_name":"%s","session_id":"%s","cursor_version":"x"}' "$event" "$session_id")
164
+ printf '%s' "$payload" | FM_HOME="$dir" FM_SESSION_SOURCE="$source" "$FAKE_CURSOR" -c '
165
+ printf "%s\n" "$$" > "$FM_HOME/state/.lock"
166
+ "$FM_HOME/bin/fm-sessionstart-cursor.sh" --source "$FM_SESSION_SOURCE"
167
+ ' 2>/dev/null
168
+ }
169
+
170
+ followup_of() { # <json>
171
+ printf '%s' "$1" | jq -r '.followup_message // empty' 2>/dev/null
172
+ }
173
+
174
+ kind_of_followup() { # <json> -> the operational kind
175
+ local body
176
+ body=$(followup_of "$1")
177
+ [ -n "$body" ] || return 1
178
+ printf '%s' "$body" | "$ROOT/bin/fm-operational-input.sh" kind
179
+ }
180
+
181
+ # --- HOST GUARD --------------------------------------------------------------
182
+
183
+ test_turnend_guard_stands_down_on_cursor_payload() {
184
+ local dir out status
185
+ dir=$(make_primary_dir "$TMP_ROOT/host-turnend")
186
+ : > "$dir/state/task1.meta"
187
+ out=$(printf '%s' "$CURSOR_PAYLOAD" | bash "$dir/bin/fm-turnend-guard.sh" 2>&1); status=$?
188
+ expect_code 0 "$status" "a Cursor-delivered Stop payload must not block through the Claude-settings duplicate"
189
+ [ -z "$out" ] || fail "duplicate entry produced output: $out"
190
+ out=$(printf '%s' "$CURSOR_PAYLOAD" | bash "$dir/bin/fm-turnend-guard.sh" --cursor 2>&1); status=$?
191
+ expect_code 2 "$status" "--cursor must let Cursor's own adapter reach the shared block decision"
192
+ case "$out" in *'TURN WOULD END BLIND'*) ;; *) fail "expected the shared banner, got: $out" ;; esac
193
+ pass "fm-turnend-guard: Cursor payload is inert without --cursor and blocks with it"
194
+ }
195
+
196
+ test_turnend_guard_still_blocks_for_claude_payload() {
197
+ local dir status
198
+ dir=$(make_primary_dir "$TMP_ROOT/host-claude")
199
+ : > "$dir/state/task1.meta"
200
+ printf '%s' "$CLAUDE_STOP_PAYLOAD" | bash "$dir/bin/fm-turnend-guard.sh" >/dev/null 2>&1
201
+ status=$?
202
+ expect_code 2 "$status" "the host guard must not disturb a genuine Claude Stop payload"
203
+ pass "fm-turnend-guard: a non-Cursor payload keeps blocking"
204
+ }
205
+
206
+ test_autoarm_stands_down_on_cursor_payload() {
207
+ local dir status
208
+ dir=$(make_primary_dir "$TMP_ROOT/host-autoarm")
209
+ : > "$dir/state/task1.meta"
210
+ write_arm_fixture "$dir" actionable
211
+ printf '%s' "$CURSOR_PAYLOAD" | FM_HOME="$dir" "$FAKE_CURSOR" -c '
212
+ printf "%s\n" "$$" > "$FM_HOME/state/.lock"
213
+ exec "$FM_HOME/bin/fm-claude-stop-autoarm.sh"
214
+ ' >/dev/null 2>&1
215
+ status=$?
216
+ expect_code 0 "$status" "the Claude auto-arm must stay inert under Cursor"
217
+ [ ! -e "$dir/state/arm-ran" ] || fail "the Claude auto-arm armed under a Cursor payload; on Cursor it would run synchronously and hold the turn open for its multi-hour timeout"
218
+ pass "fm-claude-stop-autoarm: inert on a Cursor-delivered payload"
219
+ }
220
+
221
+ test_sessionstart_run_stands_down_on_cursor_payload() {
222
+ local dir out
223
+ dir=$(make_primary_dir "$TMP_ROOT/host-sessionstart")
224
+ cat > "$dir/bin/fm-session-start.sh" <<'SH'
225
+ #!/usr/bin/env bash
226
+ printf '%s\n' "$$" >> "$FM_HOME/state/digest-ran"
227
+ printf 'DIGEST BODY\n'
228
+ SH
229
+ chmod +x "$dir/bin/fm-session-start.sh"
230
+ out=$(printf '%s' "$CURSOR_PAYLOAD" | FM_HOME="$dir" bash "$dir/bin/fm-sessionstart-run.sh" 2>&1)
231
+ [ -z "$out" ] || fail "the run wrapper emitted a digest for the Cursor duplicate: $out"
232
+ [ ! -e "$dir/state/digest-ran" ] || fail "the run wrapper took the helm twice under Cursor"
233
+ out=$(printf '{"source":"startup","session_id":"s"}' | FM_HOME="$dir" bash "$dir/bin/fm-sessionstart-run.sh" 2>&1)
234
+ case "$out" in *'DIGEST BODY'*) ;; *) fail "a Claude-shaped payload must still run the digest, got: $out" ;; esac
235
+ pass "fm-sessionstart-run: inert on a Cursor payload, unchanged otherwise"
236
+ }
237
+
238
+ test_pretool_guards_deduplicate_and_render_cursor_deny() {
239
+ local dir payload out status decision
240
+ dir=$(make_primary_dir "$TMP_ROOT/host-pretool")
241
+ payload='{"tool_name":"Shell","tool_input":{"command":"bin/fm-watch-arm.sh &"},"cursor_version":"2026.08.11-e8db854"}'
242
+ out=$(printf '%s' "$payload" | bash "$dir/bin/fm-arm-pretool-check.sh" 2>&1); status=$?
243
+ expect_code 0 "$status" "the Claude-settings duplicate must allow under Cursor"
244
+ [ -z "$out" ] || fail "duplicate pretool entry produced output: $out"
245
+
246
+ out=$(printf '%s' "$payload" | bash "$dir/bin/fm-arm-pretool-check.sh" --cursor 2>/dev/null); status=$?
247
+ expect_code 0 "$status" "Cursor reads the decision object, so the deny path exits 0"
248
+ decision=$(printf '%s' "$out" | jq -r '.permission // empty' 2>/dev/null)
249
+ [ "$decision" = deny ] || fail "expected a Cursor deny object on stdout, got: $out"
250
+ printf '%s' "$out" | jq -e '.user_message | type == "string" and length > 0' >/dev/null 2>&1 \
251
+ || fail "Cursor's deny object must carry a user_message reason, got: $out"
252
+ pass "fm-arm-pretool-check: Cursor duplicate allows, --cursor denies in Cursor's own shape"
253
+ }
254
+
255
+ test_cd_guard_renders_cursor_deny() {
256
+ local dir payload out decision
257
+ dir=$(make_primary_dir "$TMP_ROOT/host-cd")
258
+ payload='{"tool_name":"Shell","tool_input":{"command":"cd projects/example"},"cursor_version":"2026.08.11-e8db854"}'
259
+ out=$(printf '%s' "$payload" | FM_HOME="$dir" bash "$dir/bin/fm-cd-pretool-check.sh" --cursor 2>/dev/null)
260
+ decision=$(printf '%s' "$out" | jq -r '.permission // empty' 2>/dev/null)
261
+ [ "$decision" = deny ] || fail "expected a Cursor deny object from the cd guard, got: $out"
262
+ out=$(printf '%s' "$payload" | FM_HOME="$dir" bash "$dir/bin/fm-cd-pretool-check.sh" 2>&1)
263
+ [ -z "$out" ] || fail "the cd guard's Claude-settings duplicate produced output under Cursor: $out"
264
+ pass "fm-cd-pretool-check: Cursor duplicate allows, --cursor denies in Cursor's own shape"
265
+ }
266
+
267
+ # --- PARK --------------------------------------------------------------------
268
+
269
+ test_park_silent_when_nothing_in_flight() {
270
+ local dir out
271
+ dir=$(make_primary_dir "$TMP_ROOT/park-idle")
272
+ write_arm_fixture "$dir" actionable
273
+ out=$(run_park "$dir")
274
+ [ -z "$out" ] || fail "the park emitted a follow-up with nothing in flight: $out"
275
+ [ ! -e "$dir/state/arm-ran" ] || fail "the park armed with nothing to supervise"
276
+ pass "cursor park: silent no-op when no supervision is needed"
277
+ }
278
+
279
+ test_park_delivers_actionable_wake_as_followup() {
280
+ local dir out body
281
+ dir=$(make_primary_dir "$TMP_ROOT/park-wake")
282
+ : > "$dir/state/task1.meta"
283
+ write_arm_fixture "$dir" actionable
284
+ out=$(run_park "$dir")
285
+ [ -e "$dir/state/arm-ran" ] || fail "the park did not run the arm"
286
+ [ "$(kind_of_followup "$out")" = watcher ] \
287
+ || fail "an actionable close must arrive as a watcher-kind follow-up, got: $out"
288
+ body=$(followup_of "$out")
289
+ case "$body" in *'stale: fixture-win needs a look'*) ;; *) fail "the wake reason was not carried into the follow-up: $body" ;; esac
290
+ case "$body" in *'fm-wake-drain.sh'*) ;; *) fail "the follow-up must tell the session to drain first: $body" ;; esac
291
+ pass "cursor park: an actionable close is delivered as one watcher-kind follow-up"
292
+ }
293
+
294
+ test_park_never_exits_two() {
295
+ local dir status
296
+ dir=$(make_primary_dir "$TMP_ROOT/park-exit")
297
+ : > "$dir/state/task1.meta"
298
+ write_arm_fixture "$dir" failed
299
+ run_park "$dir" >/dev/null; status=$?
300
+ expect_code 0 "$status" "exit 2 is a silent no-op on Cursor's stop step, so the adapter must never use it"
301
+ pass "cursor park: always exits 0, even when supervision is genuinely down"
302
+ }
303
+
304
+ test_park_repair_nag_is_bounded() {
305
+ local dir out i kinds=0
306
+ dir=$(make_primary_dir "$TMP_ROOT/park-nag")
307
+ : > "$dir/state/task1.meta"
308
+ write_arm_fixture "$dir" failed
309
+ for i in 1 2 3; do
310
+ out=$(run_park "$dir")
311
+ [ "$(kind_of_followup "$out")" = turn-end-guard ] \
312
+ || fail "nag $i should be a turn-end-guard follow-up, got: $out"
313
+ kinds=$((kinds + 1))
314
+ done
315
+ out=$(run_park "$dir")
316
+ [ -z "$out" ] || fail "the repair nag must stop after its budget, got a 4th: $out"
317
+ [ "$kinds" -eq 3 ] || fail "expected exactly 3 bounded nags, saw $kinds"
318
+ pass "cursor park: the repair nag is bounded and then goes quiet"
319
+ }
320
+
321
+ test_park_repair_nag_requires_a_persisted_budget() {
322
+ local dir out
323
+ dir=$(make_primary_dir "$TMP_ROOT/park-nag-write-failure")
324
+ : > "$dir/state/task1.meta"
325
+ mkdir "$dir/state/.turnend-cursor-blocks"
326
+ write_arm_fixture "$dir" failed
327
+ out=$(run_park "$dir")
328
+ [ -z "$out" ] || fail "a repair nag without a persisted budget increment must fail open: $out"
329
+ [ -z "$(find "$dir/state/.turnend-cursor-blocks" -mindepth 1 -print -quit 2>/dev/null)" ] \
330
+ || fail "the failed budget commit left partial state"
331
+ pass "cursor park: a repair nag is emitted only after its budget persists"
332
+ }
333
+
334
+ test_park_nag_budget_resets_after_a_real_wake() {
335
+ local dir out
336
+ dir=$(make_primary_dir "$TMP_ROOT/park-nag-reset")
337
+ : > "$dir/state/task1.meta"
338
+ write_arm_fixture "$dir" failed
339
+ run_park "$dir" >/dev/null
340
+ run_park "$dir" >/dev/null
341
+ write_arm_fixture "$dir" actionable
342
+ out=$(run_park "$dir")
343
+ [ "$(kind_of_followup "$out")" = watcher ] || fail "expected a real wake, got: $out"
344
+ write_arm_fixture "$dir" failed
345
+ out=$(run_park "$dir")
346
+ [ "$(kind_of_followup "$out")" = turn-end-guard ] \
347
+ || fail "a productive wake must reset the nag budget, got: $out"
348
+ pass "cursor park: a delivered wake resets the bounded repair budget"
349
+ }
350
+
351
+ test_park_loop_ceiling_warns_once_then_goes_quiet() {
352
+ local dir out body
353
+ dir=$(make_primary_dir "$TMP_ROOT/park-ceiling")
354
+ : > "$dir/state/task1.meta"
355
+ write_arm_fixture "$dir" actionable
356
+ out=$(run_park "$dir" 5 5)
357
+ body=$(followup_of "$out")
358
+ case "$body" in *'CEILING REACHED'*) ;; *) fail "at the ceiling the session must be told once, got: $out" ;; esac
359
+ [ ! -e "$dir/state/arm-ran" ] || fail "the park must not arm at the loop ceiling"
360
+ out=$(run_park "$dir" 6 5)
361
+ [ -z "$out" ] || fail "above the ceiling the adapter must be silent, got: $out"
362
+ pass "cursor park: the loop_count ceiling warns exactly once, then stops the loop"
363
+ }
364
+
365
+
366
+
367
+ test_park_stands_down_when_superseded() {
368
+ local dir first_out first_pid marker
369
+ dir=$(make_primary_dir "$TMP_ROOT/park-supersede")
370
+ : > "$dir/state/task1.meta"
371
+ write_arm_fixture "$dir" switchable
372
+ marker="$dir/state/first-park-out"
373
+ ( run_park "$dir" > "$marker" 2>/dev/null ) &
374
+ first_pid=$!
375
+ local waited=0
376
+ while [ ! -s "$dir/state/.cursor-park-owner" ] || [ ! -e "$dir/state/arm-ran" ]; do
377
+ sleep 0.2
378
+ waited=$((waited + 1))
379
+ [ "$waited" -lt 100 ] || fail "the first park never claimed ownership"
380
+ done
381
+ : > "$dir/state/arm-fast"
382
+ run_park "$dir" >/dev/null 2>&1
383
+ wait "$first_pid" 2>/dev/null || true
384
+ first_out=$(cat "$marker" 2>/dev/null || true)
385
+ [ -z "$first_out" ] || fail "the older park delivered after the newer stop claimed the baton: $first_out"
386
+ pass "cursor park: an older park stands down after a newer stop claim"
387
+ }
388
+
389
+ test_park_serializes_supersession_with_followup_commit() {
390
+ local dir first_pid first_out second_out waited budget_count
391
+ dir=$(make_primary_dir "$TMP_ROOT/park-commit-race")
392
+ : > "$dir/state/task1.meta"
393
+ printf 'session=sess-cursor\ncount=1\n' > "$dir/state/.turnend-cursor-blocks"
394
+ write_arm_fixture "$dir" actionable
395
+ cat >> "$dir/bin/fm-operational-input.sh" <<'SH'
396
+ fm_operational_input_encode() {
397
+ local kind=${1-} body=${2-} result_var=${3-}
398
+ [ -n "$result_var" ] && fm_operational_kind_is_current "$kind" && [ -n "$body" ] || return 2
399
+ if ( set -C; : > "$FM_HOME/state/commit-entered" ) 2>/dev/null; then
400
+ while [ ! -e "$FM_HOME/state/commit-release" ]; do sleep 0.05; done
401
+ fi
402
+ printf -v "$result_var" '%s%s: %s' "$FM_OPERATIONAL_HEADER_PREFIX" "$kind" "$body"
403
+ }
404
+ SH
405
+ ( run_park "$dir" > "$dir/state/first-out" ) &
406
+ first_pid=$!
407
+ waited=0
408
+ while [ ! -e "$dir/state/commit-entered" ]; do
409
+ sleep 0.05
410
+ waited=$((waited + 1))
411
+ [ "$waited" -lt 200 ] || fail "the first park never entered follow-up preparation"
412
+ done
413
+ write_arm_fixture "$dir" failed
414
+ second_out=$(run_park "$dir")
415
+ : > "$dir/state/commit-release"
416
+ wait "$first_pid" 2>/dev/null || true
417
+ first_out=$(cat "$dir/state/first-out" 2>/dev/null || true)
418
+ [ -z "$first_out" ] || fail "the older park emitted after a newer stop arrived: $first_out"
419
+ [ "$(kind_of_followup "$second_out")" = turn-end-guard ] \
420
+ || fail "the newest park did not own the follow-up: $second_out"
421
+ budget_count=$(sed -n '2s/^count=//p' "$dir/state/.turnend-cursor-blocks" 2>/dev/null || true)
422
+ [ "$budget_count" = 2 ] \
423
+ || fail "the superseded actionable park reset shared nag state: $budget_count"
424
+ pass "cursor park: the newest stop exclusively owns a concurrent commit"
425
+ }
426
+
427
+ test_superseded_park_does_not_consume_nag_budget() {
428
+ local dir first_pid second_out waited budget_count
429
+ dir=$(make_primary_dir "$TMP_ROOT/park-nag-supersede")
430
+ : > "$dir/state/task1.meta"
431
+ write_arm_fixture "$dir" failed
432
+ cat > "$dir/bin/fm-turnend-guard.sh" <<'SH'
433
+ #!/usr/bin/env bash
434
+ if ( set -C; : > "$FM_HOME/state/first-guard-entered" ) 2>/dev/null; then
435
+ while [ ! -e "$FM_HOME/state/first-guard-release" ]; do sleep 0.05; done
436
+ fi
437
+ printf 'fixture supervision failure\n' >&2
438
+ exit 2
439
+ SH
440
+ chmod +x "$dir/bin/fm-turnend-guard.sh"
441
+ ( run_park "$dir" > "$dir/state/first-nag-out" ) &
442
+ first_pid=$!
443
+ waited=0
444
+ while [ ! -e "$dir/state/first-guard-entered" ]; do
445
+ sleep 0.05
446
+ waited=$((waited + 1))
447
+ [ "$waited" -lt 200 ] || fail "the first park never reached the guard decision"
448
+ done
449
+ second_out=$(run_park "$dir")
450
+ : > "$dir/state/first-guard-release"
451
+ wait "$first_pid" 2>/dev/null || true
452
+ [ "$(kind_of_followup "$second_out")" = turn-end-guard ] \
453
+ || fail "the current park did not deliver its repair nag: $second_out"
454
+ [ ! -s "$dir/state/first-nag-out" ] \
455
+ || fail "the superseded park delivered a stale repair nag"
456
+ budget_count=$(sed -n '2s/^count=//p' "$dir/state/.turnend-cursor-blocks" 2>/dev/null || true)
457
+ [ "$budget_count" = 1 ] \
458
+ || fail "the superseded park consumed the current park's nag budget: $budget_count"
459
+ pass "cursor park: a superseded park cannot consume repair budget"
460
+ }
461
+
462
+ test_park_inert_when_afk() {
463
+ local dir out
464
+ dir=$(make_primary_dir "$TMP_ROOT/park-afk")
465
+ : > "$dir/state/task1.meta"
466
+ : > "$dir/state/.afk"
467
+ write_arm_fixture "$dir" actionable
468
+ out=$(run_park "$dir")
469
+ [ -z "$out" ] || fail "away mode owns supervision; the park must not wake the primary: $out"
470
+ [ ! -e "$dir/state/arm-ran" ] || fail "the park armed while the away daemon owns the watcher"
471
+ pass "cursor park: inert while away mode is active"
472
+ }
473
+
474
+ test_park_inert_under_pi_coding_agent() {
475
+ local dir out payload
476
+ dir=$(make_primary_dir "$TMP_ROOT/park-pi-host")
477
+ : > "$dir/state/task1.meta"
478
+ write_arm_fixture "$dir" actionable
479
+ payload=$(printf '{"session_id":"sess-cursor","generation_id":"gen-0","loop_count":0,"status":"completed","hook_event_name":"stop","cursor_version":"2026.08.11-e8db854"}')
480
+ # No Cursor identity markers: Pi host alone must stand the park down.
481
+ out=$(printf '%s' "$payload" | env -u CURSOR_AGENT -u CURSOR_INVOKED_AS \
482
+ FM_HOME="$dir" PI_CODING_AGENT=true FM_CURSOR_PARK_POLL=1 \
483
+ "$FAKE_CURSOR" -c "$PARK_CHILD" 2>/dev/null)
484
+ [ -z "$out" ] || fail "Pi-hosted Cursor SDK must not park or wake: $out"
485
+ [ ! -e "$dir/state/arm-ran" ] || fail "the park armed under PI_CODING_AGENT=true"
486
+ pass "cursor park: inert when PI_CODING_AGENT marks a Pi host session"
487
+ }
488
+
489
+ test_park_still_parks_with_pi_leak_and_cursor_identity() {
490
+ local dir out body payload
491
+ dir=$(make_primary_dir "$TMP_ROOT/park-pi-leak-cursor")
492
+ : > "$dir/state/task1.meta"
493
+ write_arm_fixture "$dir" actionable
494
+ payload=$(printf '{"session_id":"sess-cursor","generation_id":"gen-0","loop_count":0,"status":"completed","hook_event_name":"stop","cursor_version":"2026.08.11-e8db854"}')
495
+ # Hand-started cursor-agent may inherit PI_CODING_AGENT; Cursor identity wins.
496
+ out=$(printf '%s' "$payload" | env -u CURSOR_INVOKED_AS \
497
+ FM_HOME="$dir" PI_CODING_AGENT=true CURSOR_AGENT=1 FM_CURSOR_PARK_POLL=1 \
498
+ "$FAKE_CURSOR" -c "$PARK_CHILD" 2>/dev/null)
499
+ [ -e "$dir/state/arm-ran" ] || fail "CURSOR_AGENT must still park despite PI_CODING_AGENT leak"
500
+ [ "$(kind_of_followup "$out")" = watcher ] \
501
+ || fail "CURSOR_AGENT park must deliver the wake despite PI leak: $out"
502
+ body=$(followup_of "$out")
503
+ case "$body" in *'stale: fixture-win needs a look'*) ;; *) fail "CURSOR_AGENT wake reason missing: $body" ;; esac
504
+ rm -f "$dir/state/arm-ran"
505
+ out=$(printf '%s' "$payload" | env -u CURSOR_AGENT \
506
+ FM_HOME="$dir" PI_CODING_AGENT=true CURSOR_INVOKED_AS=cursor-agent FM_CURSOR_PARK_POLL=1 \
507
+ "$FAKE_CURSOR" -c "$PARK_CHILD" 2>/dev/null)
508
+ [ -e "$dir/state/arm-ran" ] || fail "CURSOR_INVOKED_AS must still park despite PI_CODING_AGENT leak"
509
+ [ "$(kind_of_followup "$out")" = watcher ] \
510
+ || fail "CURSOR_INVOKED_AS park must deliver the wake despite PI leak: $out"
511
+ pass "cursor park: parks when PI_CODING_AGENT leaks alongside Cursor identity"
512
+ }
513
+
514
+ test_park_stands_down_when_away_mode_activates_before_commit() {
515
+ local dir park_pid out waited budget_count
516
+ dir=$(make_primary_dir "$TMP_ROOT/park-afk-transition")
517
+ : > "$dir/state/task1.meta"
518
+ printf 'session=sess-cursor\ncount=1\n' > "$dir/state/.turnend-cursor-blocks"
519
+ write_arm_fixture "$dir" actionable
520
+ cat >> "$dir/bin/fm-operational-input.sh" <<'SH'
521
+ fm_operational_input_encode() {
522
+ local kind=${1-} body=${2-} result_var=${3-}
523
+ [ -n "$result_var" ] && fm_operational_kind_is_current "$kind" && [ -n "$body" ] || return 2
524
+ : > "$FM_HOME/state/afk-commit-entered"
525
+ while [ ! -e "$FM_HOME/state/afk-commit-release" ]; do sleep 0.05; done
526
+ printf -v "$result_var" '%s%s: %s' "$FM_OPERATIONAL_HEADER_PREFIX" "$kind" "$body"
527
+ }
528
+ SH
529
+ ( run_park "$dir" > "$dir/state/afk-transition-out" ) &
530
+ park_pid=$!
531
+ waited=0
532
+ while [ ! -e "$dir/state/afk-commit-entered" ]; do
533
+ sleep 0.05
534
+ waited=$((waited + 1))
535
+ [ "$waited" -lt 200 ] || fail "the park never reached follow-up preparation"
536
+ done
537
+ : > "$dir/state/.afk"
538
+ : > "$dir/state/afk-commit-release"
539
+ wait "$park_pid" 2>/dev/null || true
540
+ out=$(cat "$dir/state/afk-transition-out" 2>/dev/null || true)
541
+ [ -z "$out" ] || fail "the park emitted after away mode activated: $out"
542
+ budget_count=$(sed -n '2s/^count=//p' "$dir/state/.turnend-cursor-blocks" 2>/dev/null || true)
543
+ [ "$budget_count" = 1 ] || fail "the park reset nag state after away mode activated: $budget_count"
544
+ pass "cursor park: an away-mode transition wins before follow-up commit"
545
+ }
546
+
547
+ test_park_inert_without_session_lock() {
548
+ local dir out
549
+ dir=$(make_primary_dir "$TMP_ROOT/park-nolock")
550
+ : > "$dir/state/task1.meta"
551
+ write_arm_fixture "$dir" actionable
552
+ out=$(printf '%s' "$CURSOR_PAYLOAD" | env -u PI_CODING_AGENT FM_HOME="$dir" bash "$dir/bin/fm-turnend-guard-cursor.sh" 2>/dev/null)
553
+ [ -z "$out" ] || fail "a session that does not hold the home lock must not arm or wake: $out"
554
+ [ ! -e "$dir/state/arm-ran" ] || fail "the park armed without owning the session lock"
555
+ pass "cursor park: inert when this session does not hold the home lock"
556
+ }
557
+
558
+ test_park_stands_down_after_session_takeover() {
559
+ local dir park_pid out waited budget_count
560
+ dir=$(make_primary_dir "$TMP_ROOT/park-session-takeover")
561
+ : > "$dir/state/task1.meta"
562
+ printf 'session=sess-cursor\ncount=1\n' > "$dir/state/.turnend-cursor-blocks"
563
+ write_arm_fixture "$dir" switchable
564
+ ( run_park "$dir" > "$dir/state/takeover-out" ) &
565
+ park_pid=$!
566
+ waited=0
567
+ while [ ! -e "$dir/state/arm-ran" ]; do
568
+ sleep 0.05
569
+ waited=$((waited + 1))
570
+ [ "$waited" -lt 200 ] || fail "the park never began polling before takeover"
571
+ done
572
+ printf '%s\n' "$$" > "$dir/state/.lock"
573
+ wait "$park_pid" 2>/dev/null || true
574
+ out=$(cat "$dir/state/takeover-out" 2>/dev/null || true)
575
+ [ -z "$out" ] || fail "the replaced session emitted a follow-up after takeover: $out"
576
+ budget_count=$(sed -n '2s/^count=//p' "$dir/state/.turnend-cursor-blocks" 2>/dev/null || true)
577
+ [ "$budget_count" = 1 ] || fail "the replaced session mutated nag state after takeover: $budget_count"
578
+ pass "cursor park: session takeover stops polling without output or state mutation"
579
+ }
580
+
581
+ test_park_inert_in_child_worktree() {
582
+ local base child out
583
+ base=$(make_primary_dir "$TMP_ROOT/park-base")
584
+ child="$TMP_ROOT/park-child"
585
+ fm_git_worktree "$base" "$child" fm/cursor-park-child
586
+ mkdir -p "$child/state"
587
+ : > "$child/AGENTS.md"
588
+ install_scripts "$child"
589
+ : > "$child/state/task1.meta"
590
+ write_arm_fixture "$child" actionable
591
+ out=$(run_park "$child")
592
+ [ -z "$out" ] || fail "a crewmate worktree must stay outside primary scope: $out"
593
+ pass "cursor park: inert inside a child crewmate worktree"
594
+ }
595
+
596
+ test_park_ignores_malformed_payload() {
597
+ local dir out
598
+ dir=$(make_primary_dir "$TMP_ROOT/park-malformed")
599
+ : > "$dir/state/task1.meta"
600
+ write_arm_fixture "$dir" actionable
601
+ out=$(printf 'not json at all' | env -u PI_CODING_AGENT FM_HOME="$dir" bash "$dir/bin/fm-turnend-guard-cursor.sh" 2>/dev/null)
602
+ [ -z "$out" ] || fail "a malformed payload must fail open, got: $out"
603
+ out=$(printf '{"loop_count":"three","cursor_version":"x"}' | env -u PI_CODING_AGENT FM_HOME="$dir" bash "$dir/bin/fm-turnend-guard-cursor.sh" 2>/dev/null)
604
+ [ -z "$out" ] || fail "a non-numeric loop_count must fail open, got: $out"
605
+ pass "cursor park: malformed payloads fail open without arming"
606
+ }
607
+
608
+ # --- SESSION -----------------------------------------------------------------
609
+
610
+ install_digest_fixture() { # <dir>
611
+ cat > "$1/bin/fm-session-start.sh" <<'SH'
612
+ #!/usr/bin/env bash
613
+ printf '%s\n' "$*" >> "$FM_HOME/state/digest-args"
614
+ printf 'FIRSTMATE DIGEST "quoted" line\nsecond line\n'
615
+ SH
616
+ chmod +x "$1/bin/fm-session-start.sh"
617
+ }
618
+
619
+ test_sessionstart_emits_additional_context() {
620
+ local dir out ctx
621
+ dir=$(make_primary_dir "$TMP_ROOT/session-start")
622
+ install_digest_fixture "$dir"
623
+ out=$(run_session "$dir" sessionStart startup)
624
+ ctx=$(printf '%s' "$out" | jq -r '.additional_context // empty' 2>/dev/null)
625
+ case "$ctx" in *'FIRSTMATE DIGEST "quoted" line'*) ;; *) fail "the digest must reach model context verbatim, got: $out" ;; esac
626
+ case "$ctx" in *'second line'*) ;; *) fail "the digest was truncated at the first line: $ctx" ;; esac
627
+ grep -q -- '--source startup' "$dir/state/digest-args" \
628
+ || fail "the adapter must supply --source itself; Cursor's payload has no source field"
629
+ pass "fm-sessionstart-cursor: sessionStart injects context"
630
+ }
631
+
632
+ test_sessionstart_silent_in_child_worktree() {
633
+ local base child out
634
+ base=$(make_primary_dir "$TMP_ROOT/session-base")
635
+ child="$TMP_ROOT/session-child"
636
+ fm_git_worktree "$base" "$child" fm/cursor-session-child
637
+ mkdir -p "$child/state"
638
+ : > "$child/AGENTS.md"
639
+ install_scripts "$child"
640
+ install_digest_fixture "$child"
641
+ out=$(printf '{"hook_event_name":"sessionStart","cursor_version":"x"}' \
642
+ | FM_HOME="$child" bash "$child/bin/fm-sessionstart-cursor.sh" --source startup 2>/dev/null)
643
+ [ -z "$out" ] || fail "a child worktree must never take the helm: $out"
644
+ pass "fm-sessionstart-cursor: silent inside a child crewmate worktree"
645
+ }
646
+
647
+ # --- registration ------------------------------------------------------------
648
+
649
+ test_tracked_registration_covers_the_primary_events() {
650
+ local reg
651
+ reg="$ROOT/.cursor/hooks.json"
652
+ [ -f "$reg" ] || fail "firstmate must ship a tracked project-scope .cursor/hooks.json"
653
+ jq -e '.hooks.stop and .hooks.sessionStart and .hooks.preToolUse' "$reg" >/dev/null 2>&1 \
654
+ || fail "the registration must cover stop, sessionStart, and preToolUse"
655
+ jq -e '.hooks | has("preCompact") | not' "$reg" >/dev/null 2>&1 \
656
+ || fail "preCompact staging is deliberately deferred to a follow-up and must stay unregistered"
657
+ jq -e '[.hooks.stop[] | select(.loop_limit != null and .loop_limit > 0)] | length == 1' "$reg" >/dev/null 2>&1 \
658
+ || fail "the stop registration needs an explicit positive loop_limit: without it Cursor's default is unlimited"
659
+ jq -e '[.hooks.sessionStart[]] | all(.timeout > 120)' "$reg" >/dev/null 2>&1 \
660
+ || fail "the session-open timeout must sit above bin/fm-session-start.sh's own 120s budget"
661
+ pass "cursor registration: covers every primary event with a bounded stop loop"
662
+ }
663
+
664
+ # The two bounds must nest, and the only honest way to prove it is to run the
665
+ # adapter at Cursor's own registered limit with its DEFAULT ceiling: firstmate's
666
+ # bound must already have stopped the loop by then, so Cursor's hard ceiling is
667
+ # never what silently ends supervision.
668
+ test_default_ceiling_bites_before_the_registered_loop_limit() {
669
+ local dir limit out
670
+ dir=$(make_primary_dir "$TMP_ROOT/park-nesting")
671
+ : > "$dir/state/task1.meta"
672
+ write_arm_fixture "$dir" actionable
673
+ limit=$(jq -r '.hooks.stop[0].loop_limit' "$ROOT/.cursor/hooks.json")
674
+ case "$limit" in ''|*[!0-9]*) fail "the stop registration needs a numeric loop_limit, got: $limit" ;; esac
675
+ out=$(run_park "$dir" "$((limit - 1))")
676
+ [ -z "$out" ] || fail "at Cursor's own limit the adapter must already be quiet from its own bound, got: $out"
677
+ [ ! -e "$dir/state/arm-ran" ] || fail "the adapter armed past its own default ceiling"
678
+ pass "cursor bounds nest: firstmate's default ceiling stops the loop before Cursor's loop_limit does"
679
+ }
680
+
681
+ test_turnend_guard_stands_down_on_cursor_payload
682
+ test_turnend_guard_still_blocks_for_claude_payload
683
+ test_autoarm_stands_down_on_cursor_payload
684
+ test_sessionstart_run_stands_down_on_cursor_payload
685
+ test_pretool_guards_deduplicate_and_render_cursor_deny
686
+ test_cd_guard_renders_cursor_deny
687
+ test_park_silent_when_nothing_in_flight
688
+ test_park_delivers_actionable_wake_as_followup
689
+ test_park_never_exits_two
690
+ test_park_repair_nag_is_bounded
691
+ test_park_repair_nag_requires_a_persisted_budget
692
+ test_park_nag_budget_resets_after_a_real_wake
693
+ test_park_loop_ceiling_warns_once_then_goes_quiet
694
+ test_park_stands_down_when_superseded
695
+ test_park_serializes_supersession_with_followup_commit
696
+ test_superseded_park_does_not_consume_nag_budget
697
+ test_park_inert_when_afk
698
+ test_park_inert_under_pi_coding_agent
699
+ test_park_still_parks_with_pi_leak_and_cursor_identity
700
+ test_park_stands_down_when_away_mode_activates_before_commit
701
+ test_park_inert_without_session_lock
702
+ test_park_stands_down_after_session_takeover
703
+ test_park_inert_in_child_worktree
704
+ test_park_ignores_malformed_payload
705
+ test_sessionstart_emits_additional_context
706
+ test_sessionstart_silent_in_child_worktree
707
+ test_tracked_registration_covers_the_primary_events
708
+ test_default_ceiling_bites_before_the_registered_loop_limit