@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,404 @@
1
+ #!/usr/bin/env bash
2
+ # tests/fm-cursor-harness.test.sh - the portable regression for the Cursor
3
+ # Agent CLI crewmate/scout adapter.
4
+ #
5
+ # Cursor's identity, liveness, and busy checks are HARNESS-DEPENDENT: their
6
+ # verdicts come from what the vendor emits (a process name, an env marker, a
7
+ # transcript record). This suite pins the LOGIC with real processes, real
8
+ # symlink trees, and real transcript files and NO cursor installed, so CI
9
+ # enforces it everywhere; the live-harness guard in
10
+ # tests/fm-harness-liveness-drift-live-e2e.test.sh is what catches vendor drift
11
+ # against a real cursor-agent. Neither replaces the other.
12
+ #
13
+ # The load-bearing contracts:
14
+ # 1. `agent` and `node` are far too generic to trust by name. Cursor identity
15
+ # requires Cursor's own name or install tree in the path or argv[0].
16
+ # 2. An unrelated `node`/`agent` pane classifies `other`, which the liveness
17
+ # callers fold into `ambiguous` - NEVER `dead`.
18
+ # 3. Cursor's env marker outranks an inherited CLAUDECODE, because cursor does
19
+ # not clear it and whichever marker is tested first wins.
20
+ # 4. The transcript fold brackets a turn: a trailing turn_ended is idle, a
21
+ # later role:user is busy, and an unresolvable binding is unknown.
22
+ # 5. Cursor is a crewmate/scout adapter only and refuses a secondmate launch.
23
+ set -u
24
+
25
+ # shellcheck source=tests/lib.sh
26
+ . "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
27
+
28
+ # shellcheck source=bin/fm-cursor-lib.sh
29
+ . "$ROOT/bin/fm-cursor-lib.sh"
30
+ # shellcheck source=bin/fm-busy-lib.sh
31
+ . "$ROOT/bin/fm-busy-lib.sh"
32
+
33
+ HARNESS="$ROOT/bin/fm-harness.sh"
34
+ TMP_ROOT=$(fm_test_tmproot fm-cursor-harness)
35
+ trap 'rm -rf "$TMP_ROOT"' EXIT
36
+
37
+ # A fake cursor install tree with BOTH installed names, shaped exactly like the
38
+ # real one: ~/.local/share/cursor-agent/versions/<version>/cursor-agent with
39
+ # `cursor-agent` and the legacy `agent` alias symlinked at it.
40
+ make_cursor_tree() { # <root> -> echoes <bindir>
41
+ local root=$1 ver
42
+ ver="$root/share/cursor-agent/versions/2026.08.11-e8db854"
43
+ mkdir -p "$ver" "$root/bin"
44
+ printf '#!/bin/sh\necho "Start the Cursor Agent"\n' > "$ver/cursor-agent"
45
+ chmod +x "$ver/cursor-agent"
46
+ ln -sf "$ver/cursor-agent" "$root/bin/cursor-agent"
47
+ ln -sf "$ver/cursor-agent" "$root/bin/agent"
48
+ printf '%s' "$root/bin"
49
+ }
50
+
51
+ # --- 1. Process identity, against REAL processes ----------------------------
52
+
53
+ test_identity_accepts_cursor_shapes_rejects_lookalikes() {
54
+ local tree bin real_node_pid impostor_dir out
55
+ tree="$TMP_ROOT/tree1"; bin=$(make_cursor_tree "$tree")
56
+
57
+ # Positive: the two real shapes measured on a live pane. tmux reports the
58
+ # pane command as a bare `node` while `ps -o comm=` carries the install path,
59
+ # so BOTH must identify, and neither field may be load-bearing alone.
60
+ fm_cursor_process_matches node '' "$bin/cursor-agent" \
61
+ || fail "tmux's node + cursor-agent argv[0] must identify as cursor"
62
+ fm_cursor_process_matches "$bin/cursor-agent" '' '' \
63
+ || fail "ps's cursor-agent install path must identify as cursor"
64
+ fm_cursor_process_matches cursor-agent '' '' \
65
+ || fail "a bare cursor-agent command name must identify as cursor"
66
+ # The legacy alias identifies only THROUGH the install tree it resolves into.
67
+ fm_cursor_process_matches agent '' "$bin/agent" \
68
+ || fail "the legacy agent alias resolving into cursor's tree must identify"
69
+
70
+ # Negative: a REAL unrelated node process, and a REAL executable named agent.
71
+ impostor_dir="$TMP_ROOT/impostor"; mkdir -p "$impostor_dir"
72
+ printf '#!/bin/sh\nsleep 30\n' > "$impostor_dir/agent"; chmod +x "$impostor_dir/agent"
73
+ "$impostor_dir/agent" & local impostor_pid=$!
74
+ if command -v node >/dev/null 2>&1; then
75
+ node -e 'setTimeout(function(){}, 30000)' & real_node_pid=$!
76
+ out=$(LC_ALL=C ps -p "$real_node_pid" -o comm= 2>/dev/null || true)
77
+ if [ -n "$out" ]; then
78
+ ! fm_cursor_process_matches "$out" '' "$out" \
79
+ || fail "a REAL unrelated node process must not identify as cursor (comm='$out')"
80
+ fi
81
+ kill "$real_node_pid" 2>/dev/null || true
82
+ fi
83
+ out=$(LC_ALL=C ps -p "$impostor_pid" -o comm= 2>/dev/null || true)
84
+ if [ -n "$out" ]; then
85
+ ! fm_cursor_process_matches "$out" '' "$out" \
86
+ || fail "a REAL unrelated executable named agent must not identify (comm='$out')"
87
+ fi
88
+ kill "$impostor_pid" 2>/dev/null || true
89
+
90
+ # A path with a directory component merely named `agent/` or
91
+ # `cursor-agent/` is never enough.
92
+ ! fm_cursor_process_matches node '' /opt/agent/bin/runner \
93
+ || fail "an 'agent/' directory component must not identify as cursor"
94
+ ! fm_cursor_process_matches node '' /tmp/cursor-agent/bin/runner \
95
+ || fail "a cursor-agent directory outside the versioned install tree must not identify"
96
+ ! fm_cursor_process_matches MainThread '' '' \
97
+ || fail "a bare MainThread with no cursor evidence must not identify"
98
+ ! fm_cursor_process_matches node '' '' \
99
+ || fail "a node with no argv[0] evidence must not identify"
100
+ pass "fm_cursor_process_matches: cursor's real shapes identify; real node/agent lookalikes do not"
101
+ }
102
+
103
+ test_identity_signals_diverge() {
104
+ # Two independent signals carry a positive verdict: the executable NAME and
105
+ # the install-tree PATH. Drive them apart so neither is silently load-bearing:
106
+ # a cursor-named executable OUTSIDE any cursor tree, and a non-cursor-named
107
+ # executable INSIDE one. Both must identify.
108
+ local odd="$TMP_ROOT/odd" tree bin
109
+ mkdir -p "$odd"
110
+ printf '#!/bin/sh\nexit 0\n' > "$odd/cursor-agent"; chmod +x "$odd/cursor-agent"
111
+ fm_cursor_process_matches "$odd/cursor-agent" '' '' \
112
+ || fail "name signal alone (cursor-agent outside any cursor tree) must identify"
113
+ tree="$TMP_ROOT/tree2"; bin=$(make_cursor_tree "$tree")
114
+ fm_cursor_process_matches agent '' "$bin/agent" \
115
+ || fail "path signal alone (alias named 'agent' inside cursor's tree) must identify"
116
+ # And the divergence itself: these two really are different signals.
117
+ [ "$(basename "$odd/cursor-agent")" = cursor-agent ] \
118
+ || fail "name-signal fixture lost its cursor-agent basename"
119
+ case "/$(fm_cursor_canonical_path "$bin/agent")/" in
120
+ */cursor-agent/*) : ;;
121
+ *) fail "path-signal fixture must canonicalize into a cursor-agent tree" ;;
122
+ esac
123
+ pass "fm_cursor_process_matches: name and install-tree signals each carry a verdict alone"
124
+ }
125
+
126
+ test_verify_executable_refuses_unrelated_agent() {
127
+ local tree bin odd="$TMP_ROOT/verify"
128
+ tree="$TMP_ROOT/tree3"; bin=$(make_cursor_tree "$tree")
129
+ mkdir -p "$odd"
130
+ printf '#!/bin/sh\necho unrelated\n' > "$odd/agent"; chmod +x "$odd/agent"
131
+ fm_cursor_verify_executable "$bin/agent" \
132
+ || fail "the alias inside cursor's install tree must verify"
133
+ ! fm_cursor_verify_executable "$odd/agent" \
134
+ || fail "an unrelated executable named agent must NOT verify as cursor"
135
+ pass "fm_cursor_verify_executable: the legacy alias is accepted only with cursor evidence"
136
+ }
137
+
138
+ test_resolve_binary_prefers_stable_path() {
139
+ # The canonical path carries a version cursor replaces on its own auto-update,
140
+ # so resolution must print the STABLE launcher even though identity is proven
141
+ # through canonicalization.
142
+ local tree bin out
143
+ tree="$TMP_ROOT/tree4"; bin=$(make_cursor_tree "$tree")
144
+ out=$(PATH="$bin:$PATH" fm_cursor_resolve_binary) \
145
+ || fail "resolve must succeed when cursor-agent is on PATH"
146
+ [ "$out" = "$bin/cursor-agent" ] \
147
+ || fail "resolve must print the stable launcher, got '$out'"
148
+ case "$out" in *versions*) fail "resolve must not pin the versioned install path" ;; esac
149
+ pass "fm_cursor_resolve_binary: prints the stable launcher, not the versioned target"
150
+ }
151
+
152
+ # --- 2. tmux pane liveness ---------------------------------------------------
153
+
154
+ test_tmux_classifies_cursor_pane_without_inferring_dead() {
155
+ local tree bin
156
+ tree="$TMP_ROOT/tree5"; bin=$(make_cursor_tree "$tree")
157
+ # shellcheck source=bin/backends/tmux.sh
158
+ ( FM_BACKEND_LIB_DIR="$ROOT/bin"; . "$ROOT/bin/backends/tmux.sh"
159
+ [ "$(fm_backend_tmux_classify_process_name node "$bin/cursor-agent")" = agent ] \
160
+ || fail "a cursor pane reported as node must classify agent"
161
+ [ "$(fm_backend_tmux_classify_process_name '' "$bin/cursor-agent")" = agent ] \
162
+ || fail "the argv[0]-only call must classify a cursor pane agent"
163
+ # The safety half: an unrelated node is `other`, and the callers turn
164
+ # `other` into `ambiguous`, never `dead`.
165
+ [ "$(fm_backend_tmux_classify_process_name node /usr/bin/node)" = other ] \
166
+ || fail "an unrelated node must stay 'other', never agent"
167
+ [ "$(fm_backend_tmux_classify_process_name agent /usr/local/bin/agent)" = other ] \
168
+ || fail "an unrelated agent must stay 'other', never agent"
169
+ # Neighbours must not regress.
170
+ [ "$(fm_backend_tmux_classify_process_name claude '')" = agent ] || fail "claude regressed"
171
+ [ "$(fm_backend_tmux_classify_process_name zsh '')" = shell ] || fail "zsh regressed"
172
+ ) || exit 1
173
+ pass "tmux liveness: a cursor pane is agent; an unrelated node/agent is other, never dead"
174
+ }
175
+
176
+ # --- 3. Detection ordering ---------------------------------------------------
177
+
178
+ test_cursor_marker_outranks_inherited_claudecode() {
179
+ local out
180
+ # This is the exact hazard: cursor does NOT clear an inherited CLAUDECODE, so
181
+ # a cursor worker under a claude primary carries both markers.
182
+ out=$(CLAUDECODE=1 CURSOR_AGENT=1 "$HARNESS")
183
+ [ "$out" = cursor ] || fail "CLAUDECODE + CURSOR_AGENT must detect cursor, got '$out'"
184
+ out=$(CLAUDECODE=1 CURSOR_INVOKED_AS=cursor-agent "$HARNESS")
185
+ [ "$out" = cursor ] || fail "CLAUDECODE + CURSOR_INVOKED_AS must detect cursor, got '$out'"
186
+ # Both cursor markers stand alone, and neither steals a plain claude session.
187
+ out=$(env -u CLAUDECODE CURSOR_AGENT=1 "$HARNESS")
188
+ [ "$out" = cursor ] || fail "CURSOR_AGENT alone must detect cursor, got '$out'"
189
+ out=$(env -u CURSOR_AGENT -u CURSOR_INVOKED_AS CLAUDECODE=1 "$HARNESS")
190
+ [ "$out" = claude ] || fail "CLAUDECODE alone must still detect claude, got '$out'"
191
+ # A CURSOR_* variable that is not the invocation identity proves nothing.
192
+ out=$(env -u CURSOR_AGENT CLAUDECODE=1 CURSOR_API_ENDPOINT=https://example \
193
+ CURSOR_INVOKED_AS=something-else "$HARNESS")
194
+ [ "$out" = claude ] \
195
+ || fail "an unrelated CURSOR_* setting must not claim the cursor identity, got '$out'"
196
+ pass "fm-harness.sh: cursor's marker outranks an inherited CLAUDECODE"
197
+ }
198
+
199
+ test_harness_ancestry_rejects_cursor_named_node_script() {
200
+ command -v node >/dev/null 2>&1 || return 0
201
+ local helper="$TMP_ROOT/cursor-agent-helper.js" out
202
+ cat > "$helper" <<'JS'
203
+ const { spawnSync } = require('child_process');
204
+ const env = { ...process.env };
205
+ delete env.CURSOR_AGENT;
206
+ delete env.CURSOR_INVOKED_AS;
207
+ delete env.CLAUDECODE;
208
+ delete env.PI_CODING_AGENT;
209
+ delete env.GROK_AGENT;
210
+ const result = spawnSync(process.argv[2], [], { encoding: 'utf8', env });
211
+ process.stdout.write(result.stdout);
212
+ process.stderr.write(result.stderr);
213
+ process.exit(result.status === null ? 1 : result.status);
214
+ JS
215
+ out=$(node "$helper" "$HARNESS")
216
+ [ "$out" != cursor ] \
217
+ || fail "a node script merely containing cursor-agent in its filename must not identify as cursor"
218
+ pass "fm-harness.sh: cursor-like node script names do not establish ancestry identity"
219
+ }
220
+
221
+ # --- 4. The transcript busy fold --------------------------------------------
222
+
223
+ # Build a bound cursor workspace: a project dir keyed by .workspace-trusted, a
224
+ # conversation transcript, and the per-task sidecar fm-spawn writes.
225
+ make_cursor_binding() { # <case> <conversation-id> <transcript-body> -> echoes <state-dir>
226
+ local case_name=$1 conv=$2 body=$3 root ws proj state
227
+ root="$TMP_ROOT/$case_name/projects"
228
+ ws="$TMP_ROOT/$case_name/worktree"
229
+ proj="$root/some-opaque-slug-$case_name"
230
+ state="$TMP_ROOT/$case_name/state"
231
+ mkdir -p "$proj/agent-transcripts/$conv" "$ws" "$state"
232
+ printf '{\n "workspacePath": "%s",\n "trustMethod": "cli-flag"\n}\n' "$ws" \
233
+ > "$proj/.workspace-trusted"
234
+ printf '%s' "$body" > "$proj/agent-transcripts/$conv/$conv.jsonl"
235
+ printf 'projects_root=%s\nworkspace_root=%s\n' "$root" "$ws" > "$state/task.cursor-session"
236
+ printf '%s' "$state"
237
+ }
238
+
239
+ test_transcript_fold_brackets_a_turn() {
240
+ local state out
241
+ # Open turn: a role:user record with no close after it.
242
+ state=$(make_cursor_binding open conv-a '{"role":"user"}
243
+ {"role":"assistant"}
244
+ ')
245
+ out=$(fm_busy_classify tmux none cursor task "$state")
246
+ [ "$out" = "busy cursor-transcript" ] || fail "an open turn must be busy, got '$out'"
247
+
248
+ # Closed turn.
249
+ state=$(make_cursor_binding closed conv-b '{"role":"user"}
250
+ {"role":"assistant"}
251
+ {"type":"turn_ended","status":"success"}
252
+ ')
253
+ out=$(fm_busy_classify tmux none cursor task "$state")
254
+ [ "$out" = "idle cursor-transcript" ] || fail "a closed turn must be idle, got '$out'"
255
+
256
+ # An ABORTED close is still a close. This is the case Claude's Stop hook
257
+ # misses, and it is why this source is preferred over a rendered footer.
258
+ state=$(make_cursor_binding aborted conv-c '{"role":"user"}
259
+ {"type":"turn_ended","status":"aborted","error":"User aborted/interrupted manually."}
260
+ ')
261
+ out=$(fm_busy_classify tmux none cursor task "$state")
262
+ [ "$out" = "idle cursor-transcript" ] || fail "an aborted close must be idle, got '$out'"
263
+
264
+ # A NEW turn opened after a close reopens it.
265
+ state=$(make_cursor_binding reopened conv-d '{"role":"user"}
266
+ {"type":"turn_ended","status":"success"}
267
+ {"role":"user"}
268
+ ')
269
+ out=$(fm_busy_classify tmux none cursor task "$state")
270
+ [ "$out" = "busy cursor-transcript" ] || fail "a turn reopened after a close must be busy, got '$out'"
271
+ pass "cursor transcript fold: role:user opens a turn, turn_ended closes it, aborts included"
272
+ }
273
+
274
+ test_transcript_fold_ignores_lifecycle_tokens_in_message_text() {
275
+ local state out log jq_bin awk_bin no_jq_bin
276
+ state=$(make_cursor_binding quoted-lifecycle conv-quoted '{"role":"user","message":"literal {\"type\":\"turn_ended\"} and \"role\":\"user\""}
277
+ ')
278
+ out=$(fm_busy_classify tmux none cursor task "$state")
279
+ [ "$out" = "busy cursor-transcript" ] \
280
+ || fail "lifecycle-shaped message text must not close an active turn, got '$out'"
281
+
282
+ jq_bin=$(command -v jq) || fail "jq is required to exercise Cursor's primary transcript parser"
283
+ [ -x "$jq_bin" ] || fail "jq must be executable"
284
+ state=$(make_cursor_binding malformed-close conv-malformed '{"role":"user"}
285
+ {"type":"turn_ended",broken}
286
+ ')
287
+ log=$(fm_busy_cursor_transcript "$state" task) \
288
+ || fail "the malformed-close transcript fixture must resolve"
289
+ out=$(fm_busy_cursor_turn_state "$log")
290
+ [ "$out" = busy ] \
291
+ || fail "jq parser must keep an open turn busy after a malformed close, got '$out'"
292
+
293
+ awk_bin=$(command -v awk) || fail "awk is required to exercise Cursor's fallback transcript parser"
294
+ no_jq_bin="$TMP_ROOT/no-jq-bin"
295
+ mkdir -p "$no_jq_bin"
296
+ ln -sf "$awk_bin" "$no_jq_bin/awk"
297
+ out=$(PATH="$no_jq_bin" fm_busy_cursor_turn_state "$log")
298
+ [ "$out" = busy ] \
299
+ || fail "no-jq parser must keep an open turn busy after a malformed close, got '$out'"
300
+ pass "cursor transcript fold: malformed closes cannot settle through either parser"
301
+ }
302
+
303
+ test_transcript_fold_handles_partially_appended_records() {
304
+ local state out
305
+ state=$(make_cursor_binding closed-partial conv-partial-a '{"role":"user"}
306
+ {"type":"turn_ended","status":"success"}
307
+ {"role":"user"
308
+ ')
309
+ out=$(fm_busy_classify tmux none cursor task "$state")
310
+ [ "$out" = "unknown cursor-transcript" ] \
311
+ || fail "a partial record after a close must be unknown, got '$out'"
312
+
313
+ state=$(make_cursor_binding closed-complete conv-partial-b '{"role":"user"}
314
+ {"type":"turn_ended","status":"success"}
315
+ ')
316
+ out=$(fm_busy_classify tmux none cursor task "$state")
317
+ [ "$out" = "idle cursor-transcript" ] \
318
+ || fail "a completed turn without trailing garbage must be idle, got '$out'"
319
+
320
+ state=$(make_cursor_binding open-partial conv-partial-c '{"role":"user"}
321
+ {"role":"assistant"
322
+ ')
323
+ out=$(fm_busy_classify tmux none cursor task "$state")
324
+ [ "$out" = "busy cursor-transcript" ] \
325
+ || fail "a partial record after an open must remain busy, got '$out'"
326
+ pass "cursor transcript fold: partial appends never make an active turn idle"
327
+ }
328
+
329
+ test_transcript_fold_is_unknown_never_idle_when_unresolvable() {
330
+ local state out empty_state
331
+ # A record-free transcript proves nothing either way.
332
+ state=$(make_cursor_binding norecords conv-e '{"type":"session_meta"}
333
+ ')
334
+ out=$(fm_busy_classify tmux none cursor task "$state")
335
+ [ "$out" = "unknown cursor-transcript" ] || fail "a record-free transcript must be unknown, got '$out'"
336
+
337
+ # No sidecar at all.
338
+ empty_state="$TMP_ROOT/nosidecar"; mkdir -p "$empty_state"
339
+ out=$(fm_busy_classify tmux none cursor task "$empty_state")
340
+ [ "$out" = "unknown cursor-transcript" ] || fail "a missing sidecar must be unknown, got '$out'"
341
+
342
+ # A sidecar pointing at a workspace no project directory claims.
343
+ mkdir -p "$TMP_ROOT/unclaimed/state" "$TMP_ROOT/unclaimed/projects"
344
+ printf 'projects_root=%s\nworkspace_root=%s\n' \
345
+ "$TMP_ROOT/unclaimed/projects" "$TMP_ROOT/unclaimed/nowhere" \
346
+ > "$TMP_ROOT/unclaimed/state/task.cursor-session"
347
+ out=$(fm_busy_classify tmux none cursor task "$TMP_ROOT/unclaimed/state")
348
+ [ "$out" = "unknown cursor-transcript" ] || fail "an unclaimed workspace must be unknown, got '$out'"
349
+ pass "cursor transcript fold: an unresolvable binding is unknown, never idle"
350
+ }
351
+
352
+ test_transcript_binding_matches_workspace_exactly() {
353
+ # The binding matches the recorded absolute workspacePath, NOT a reconstructed
354
+ # slug and NOT a prefix - otherwise a nested worktree would fold its parent's
355
+ # transcript. The fixture slug is deliberately opaque so a slug-rebuilding
356
+ # implementation cannot pass this.
357
+ local state out proj
358
+ state=$(make_cursor_binding nested conv-f '{"role":"user"}
359
+ ')
360
+ out=$(fm_busy_classify tmux none cursor task "$state")
361
+ [ "$out" = "busy cursor-transcript" ] || fail "exact workspace match must resolve, got '$out'"
362
+ # Point the project at a PREFIX of the bound workspace: must no longer match.
363
+ proj=$(dirname "$state")/projects/some-opaque-slug-nested
364
+ printf '{\n "workspacePath": "%s"\n}\n' "$(dirname "$state")/worktre" > "$proj/.workspace-trusted"
365
+ out=$(fm_busy_classify tmux none cursor task "$state")
366
+ [ "$out" = "unknown cursor-transcript" ] \
367
+ || fail "a prefix of the workspace path must NOT bind, got '$out'"
368
+ pass "cursor transcript binding: exact recorded workspacePath only, never a prefix or rebuilt slug"
369
+ }
370
+
371
+ test_transcript_fold_excludes_prior_conversations() {
372
+ # A relaunch in a reused worktree must fold ITS turn, not its predecessor's.
373
+ local state proj out
374
+ state=$(make_cursor_binding prior conv-old '{"role":"user"}
375
+ ')
376
+ proj="$TMP_ROOT/prior/projects/some-opaque-slug-prior"
377
+ printf 'prior_conversation=conv-old\n' >> "$state/task.cursor-session"
378
+ # Only the retired conversation exists, so nothing new resolves.
379
+ out=$(fm_busy_classify tmux none cursor task "$state")
380
+ [ "$out" = "unknown cursor-transcript" ] \
381
+ || fail "a retired conversation must not be folded, got '$out'"
382
+ # The relaunched pane's own conversation resolves and wins.
383
+ mkdir -p "$proj/agent-transcripts/conv-new"
384
+ printf '{"role":"user"}\n{"type":"turn_ended","status":"success"}\n' \
385
+ > "$proj/agent-transcripts/conv-new/conv-new.jsonl"
386
+ out=$(fm_busy_classify tmux none cursor task "$state")
387
+ [ "$out" = "idle cursor-transcript" ] \
388
+ || fail "the relaunched pane's own conversation must resolve, got '$out'"
389
+ pass "cursor transcript fold: a prior conversation is excluded so a relaunch folds its own turn"
390
+ }
391
+
392
+ test_identity_accepts_cursor_shapes_rejects_lookalikes
393
+ test_identity_signals_diverge
394
+ test_verify_executable_refuses_unrelated_agent
395
+ test_resolve_binary_prefers_stable_path
396
+ test_tmux_classifies_cursor_pane_without_inferring_dead
397
+ test_cursor_marker_outranks_inherited_claudecode
398
+ test_harness_ancestry_rejects_cursor_named_node_script
399
+ test_transcript_fold_brackets_a_turn
400
+ test_transcript_fold_ignores_lifecycle_tokens_in_message_text
401
+ test_transcript_fold_handles_partially_appended_records
402
+ test_transcript_fold_is_unknown_never_idle_when_unresolvable
403
+ test_transcript_binding_matches_workspace_exactly
404
+ test_transcript_fold_excludes_prior_conversations
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env bash
2
+ # Opt-in live guard for Cursor Agent CLI as a firstmate PRIMARY.
3
+ #
4
+ # The Cursor primary integration rests on facts only the real cursor-agent can
5
+ # answer: that its `stop` hook is awaited so a park can hold the turn boundary,
6
+ # that a returned followup_message genuinely starts another turn, that
7
+ # `sessionStart` carries additional_context into model context, that Cursor's own
8
+ # process appears in the session-lock ancestry, and that an idle Cursor composer
9
+ # can be proven empty so an away-mode escalation can be delivered. A stub can
10
+ # only confirm the assumption already written into the stub, so this exercises
11
+ # the installed binary end to end.
12
+ #
13
+ # tests/fm-cursor-primary.test.sh and the Cursor cases in
14
+ # tests/fm-tmux-agent-liveness.test.sh are the portable regressions that run
15
+ # everywhere; this is the harness-and-credential-gated counterpart. Run it after
16
+ # every Cursor upgrade and before trusting refreshed per-harness evidence in
17
+ # docs/verification/supervision.md and docs/verification/runtime-backends.md.
18
+ #
19
+ # Isolation: a throwaway firstmate home under a temp dir, a private tmux socket,
20
+ # and a Cursor workspace Cursor has never seen. It never touches the fleet's tmux
21
+ # server, never writes a user-scope or global hook, and never runs against a live
22
+ # home. Cursor still records its own per-project transcript under
23
+ # ~/.cursor/projects/<slug of the temp path>, which is keyed to the throwaway
24
+ # path and is the only state left outside the temp dir.
25
+ set -u
26
+
27
+ if [ "${FM_CURSOR_PRIMARY_LIVE_E2E:-0}" != 1 ]; then
28
+ echo "skip: set FM_CURSOR_PRIMARY_LIVE_E2E=1 to run the live Cursor primary guard"
29
+ exit 0
30
+ fi
31
+
32
+ # shellcheck source=tests/lib.sh
33
+ . "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
34
+
35
+ CURSOR_BIN=${FM_CURSOR_BIN:-$(command -v cursor-agent || true)}
36
+ [ -n "$CURSOR_BIN" ] && [ -x "$CURSOR_BIN" ] \
37
+ || fail "cursor-agent not found; install it or set FM_CURSOR_BIN. This guard refuses to pass without checking the real harness."
38
+ REAL_TMUX=$(command -v tmux) || fail "tmux not found"
39
+ command -v jq >/dev/null 2>&1 || fail "jq not found"
40
+ CURSOR_VERSION=$("$CURSOR_BIN" --version 2>/dev/null | head -1)
41
+ [ -n "$CURSOR_VERSION" ] || fail "cursor-agent did not report a version; refusing to claim a verified result"
42
+ printf 'harness: cursor-agent %s\n' "$CURSOR_VERSION"
43
+
44
+ HARNESS_LABEL="cursor-agent $CURSOR_VERSION"
45
+ harness_fail() { # <message>
46
+ fail "$1 [harness: $HARNESS_LABEL]"
47
+ }
48
+
49
+ SOCKET="fm-cursor-primary-$$"
50
+ LAB=$(mktemp -d "${TMPDIR:-/tmp}/fm-cursor-primary.XXXXXX")
51
+ HOME_DIR="$LAB/home"
52
+ MARKER="FM_CURSOR_LIVE_MARKER_$$"
53
+
54
+ cleanup_all() {
55
+ "$REAL_TMUX" -L "$SOCKET" kill-server >/dev/null 2>&1 || true
56
+ [ -n "${LAB:-}" ] && rm -rf "$LAB"
57
+ }
58
+ trap cleanup_all EXIT
59
+
60
+ # A plain (non-worktree) checkout of the CURRENT working tree, so the guard
61
+ # tests the code under review rather than whatever is committed.
62
+ mkdir -p "$HOME_DIR"
63
+ (cd "$ROOT" && tar --exclude=.git --exclude=state --exclude=projects --exclude=node_modules -cf - .) \
64
+ | (cd "$HOME_DIR" && tar -xf -) \
65
+ || harness_fail "could not stage the working tree into the throwaway home"
66
+ git init -q "$HOME_DIR"
67
+ git -C "$HOME_DIR" add -A >/dev/null 2>&1 || true
68
+ git -C "$HOME_DIR" -c user.email=fmtest@example.invalid -c user.name=fmtest \
69
+ commit -q -m "live-e2e fixture" >/dev/null 2>&1 || true
70
+ [ "$(git -C "$HOME_DIR" rev-parse --git-dir)" = "$(git -C "$HOME_DIR" rev-parse --git-common-dir)" ] \
71
+ || harness_fail "the fixture home must be a plain checkout for primary scope to match"
72
+ [ -f "$HOME_DIR/.cursor/hooks.json" ] \
73
+ || harness_fail "the working tree ships no .cursor/hooks.json; there is nothing to verify"
74
+
75
+ mkdir -p "$HOME_DIR/state" "$HOME_DIR/data" "$HOME_DIR/config"
76
+ # A unique token the session-start digest must carry into model context.
77
+ printf '# Captain\n\nLive marker: %s\n' "$MARKER" > "$HOME_DIR/data/captain.md"
78
+ printf '# Backlog\n\n- live probe\n' > "$HOME_DIR/data/backlog.md"
79
+ # One in-flight task so supervision is genuinely needed, plus a captain-relevant
80
+ # status line the watcher's own backstop must surface as a real wake.
81
+ cat > "$HOME_DIR/state/probe.meta" <<EOF
82
+ id=probe
83
+ project=probe
84
+ harness=cursor
85
+ backend=tmux
86
+ window=fm-probe
87
+ EOF
88
+ printf 'blocked: fixture needs a decision\n' > "$HOME_DIR/state/probe.status"
89
+
90
+ "$REAL_TMUX" -L "$SOCKET" new-session -d -s primary -x 220 -y 60 -c "$HOME_DIR" \
91
+ "cd '$HOME_DIR' && FM_HOME='$HOME_DIR' FM_HEARTBEAT=30 FM_HEARTBEAT_MAX=30 exec '$CURSOR_BIN' --trust --yolo --workspace '$HOME_DIR'" \
92
+ || harness_fail "could not start the private tmux server"
93
+
94
+ pane_text() {
95
+ "$REAL_TMUX" -L "$SOCKET" capture-pane -p -t primary 2>/dev/null
96
+ }
97
+
98
+ wait_for_file() { # <path> <seconds> <what>
99
+ local path=$1 limit=$2 what=$3 i=0
100
+ while [ "$i" -lt "$((limit * 2))" ]; do
101
+ [ -e "$path" ] && return 0
102
+ sleep 0.5
103
+ i=$((i + 1))
104
+ done
105
+ harness_fail "$what did not appear within ${limit}s"
106
+ }
107
+
108
+ wait_for_pane() { # <needle> <seconds> <what>
109
+ local needle=$1 limit=$2 what=$3 i=0
110
+ while [ "$i" -lt "$((limit * 2))" ]; do
111
+ case "$(pane_text)" in *"$needle"*) return 0 ;; esac
112
+ sleep 0.5
113
+ i=$((i + 1))
114
+ done
115
+ printf 'pane at failure:\n%s\n' "$(pane_text)" >&2
116
+ harness_fail "$what did not appear within ${limit}s"
117
+ }
118
+
119
+ submit() { # <text>
120
+ "$REAL_TMUX" -L "$SOCKET" send-keys -t primary -l "$1"
121
+ sleep 1
122
+ "$REAL_TMUX" -L "$SOCKET" send-keys -t primary Enter
123
+ }
124
+
125
+ # --- 1. run-tier session start ----------------------------------------------
126
+
127
+ wait_for_file "$HOME_DIR/state/.lock" 180 "the fleet session lock"
128
+ LOCK_PID=$(cat "$HOME_DIR/state/.lock" 2>/dev/null)
129
+ PANE_PID=$("$REAL_TMUX" -L "$SOCKET" display-message -p -t primary '#{pane_pid}' 2>/dev/null)
130
+ [ -n "$LOCK_PID" ] && [ "$LOCK_PID" = "$PANE_PID" ] \
131
+ || harness_fail "the session lock must be owned by the Cursor pane process (lock=$LOCK_PID pane=$PANE_PID); Cursor is not resolving in the session-lock ancestry"
132
+ pass "cursor primary: the sessionStart hook takes the fleet lock as the Cursor process itself"
133
+
134
+ wait_for_file "$HOME_DIR/state/.session-start-complete" 240 "the completed session-start record"
135
+ pass "cursor primary: the run-tier session start completes every stage"
136
+
137
+ submit "Answer only from the context you were given at session start. Do not run any command. Reply with the exact live marker token you can see, and nothing else."
138
+ wait_for_pane "$MARKER" 180 "the session-start digest marker quoted back from model context"
139
+ pass "cursor primary: sessionStart additional_context reaches model context before the first turn"
140
+
141
+ # --- 2. the stop-hook park ---------------------------------------------------
142
+
143
+ # The turn that just ended must have parked, armed a watcher, and delivered a
144
+ # real wake as one follow-up carrying the operational watcher kind.
145
+ wait_for_pane "FIRSTMATE_OP: v1 watcher:" 300 "a watcher wake delivered as a stop-hook follow-up"
146
+ pass "cursor primary: the stop-hook park delivers a real watcher wake as one follow-up"
147
+
148
+ wait_for_file "$HOME_DIR/state/.cursor-park-owner" 60 "the park ownership record"
149
+ PARK_PID=$(sed -n 's/^seq=[0-9][0-9]* pid=\([0-9][0-9]*\) .*/\1/p' "$HOME_DIR/state/.cursor-park-owner")
150
+ [ -n "$PARK_PID" ] || harness_fail "the park never recorded an owner pid"
151
+ BEAT="$HOME_DIR/state/.last-watcher-beat"
152
+ [ -e "$BEAT" ] || harness_fail "the park armed no watcher: there is no liveness beacon"
153
+ pass "cursor primary: the park owns exactly one arm cycle with a live watcher beacon"
154
+
155
+ # --- 3. supersession ---------------------------------------------------------
156
+
157
+ park_seq() {
158
+ sed -n 's/^seq=\([0-9][0-9]*\) .*/\1/p' "$HOME_DIR/state/.cursor-park-owner" 2>/dev/null
159
+ }
160
+
161
+ BEFORE_SEQ=$(park_seq)
162
+ submit "Reply with exactly the token CAPTAIN_INTERRUPT and nothing else. Do not run any command."
163
+ wait_for_pane "CAPTAIN_INTERRUPT" 180 "the captain message answered while the hook was parked"
164
+ # The new park claims only when that answering turn ENDS, so wait for the baton
165
+ # rather than racing it.
166
+ AFTER_SEQ=$BEFORE_SEQ
167
+ i=0
168
+ while [ "$i" -lt 240 ]; do
169
+ AFTER_SEQ=$(park_seq)
170
+ [ -n "$AFTER_SEQ" ] && [ "$AFTER_SEQ" -gt "$BEFORE_SEQ" ] && break
171
+ sleep 0.5
172
+ i=$((i + 1))
173
+ done
174
+ [ -n "$AFTER_SEQ" ] && [ "$AFTER_SEQ" -gt "$BEFORE_SEQ" ] \
175
+ || harness_fail "a captain message mid-park must claim a newer park generation (before=$BEFORE_SEQ after=$AFTER_SEQ)"
176
+ # Give the older park one poll interval to observe the newer stop's claim.
177
+ sleep 5
178
+ LIVE_PARKS=$(pgrep -f "$HOME_DIR/bin/fm-turnend-guard-cursor.sh" 2>/dev/null | wc -l | tr -d ' ')
179
+ [ "${LIVE_PARKS:-0}" -le 1 ] \
180
+ || harness_fail "an older park leaked after the newer stop claim: $LIVE_PARKS park processes are alive, and each could deliver a stale duplicate wake"
181
+ pass "cursor primary: the captain keeps control and the older park stands down after the next stop claim"
182
+
183
+ # --- 4. away-mode escalation delivery ---------------------------------------
184
+
185
+ : > "$HOME_DIR/state/.afk"
186
+ AWAY_TOKEN="AWAY_ACK_$$"
187
+ INJECT_RC=0
188
+ cat > "$LAB/inject.sh" <<EOS
189
+ #!/usr/bin/env bash
190
+ set -u
191
+ tmux() { command "$REAL_TMUX" -L "$SOCKET" "\$@"; }
192
+ export -f tmux 2>/dev/null || true
193
+ export FM_STATE_OVERRIDE="$HOME_DIR/state"
194
+ export FM_SUPERVISOR_TARGET=primary
195
+ export FM_SUPERVISOR_BACKEND=tmux
196
+ export FM_DAEMON_PRIMARY_HARNESS=cursor
197
+ . "$HOME_DIR/bin/fm-supervise-daemon.sh"
198
+ composer=\$(fm_backend_composer_state tmux primary)
199
+ printf 'composer=%s\n' "\$composer"
200
+ [ "\$composer" = empty ] || exit 3
201
+ inject_msg "AWAY PROBE - reply with exactly the token $AWAY_TOKEN and nothing else." "$HOME_DIR/state"
202
+ EOS
203
+ chmod +x "$LAB/inject.sh"
204
+ COMPOSER_OUT=$(bash "$LAB/inject.sh" 2>&1) || INJECT_RC=$?
205
+ case "$COMPOSER_OUT" in
206
+ *composer=empty*) ;;
207
+ *) harness_fail "an idle Cursor composer must be provably empty for away mode; got: $COMPOSER_OUT" ;;
208
+ esac
209
+ [ "$INJECT_RC" -eq 0 ] \
210
+ || harness_fail "the away-mode escalation could not confirm delivery into the Cursor pane (rc=$INJECT_RC): $COMPOSER_OUT"
211
+ wait_for_pane "$AWAY_TOKEN" 180 "the away-mode escalation processed by the Cursor primary"
212
+ pass "cursor primary: an away-mode escalation is delivered, confirmed, and processed"
213
+
214
+ rm -f "$HOME_DIR/state/.afk"
215
+
216
+ cleanup_all
217
+ trap - EXIT