@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,440 @@
1
+ #!/usr/bin/env bash
2
+ # Regression tests for fm-spawn's pooled-worktree base refresh.
3
+ #
4
+ # A treehouse pool can return a clean detached worktree whose origin/main was
5
+ # advanced after the worktree was allocated.
6
+ # These tests drive the real spawn path with a fake terminal, then prove it
7
+ # starts the worker from the fetched origin/main tip or stops when origin is
8
+ # unreachable.
9
+ set -u
10
+
11
+ # shellcheck source=tests/fixtures.sh
12
+ . "$(dirname "${BASH_SOURCE[0]}")/fixtures.sh"
13
+
14
+ TMP_ROOT=$(fm_test_tmproot fm-spawn-pool-base-freshen)
15
+
16
+ make_case() {
17
+ local name=$1 id=$2 default=${3:-main} case_dir home project origin pool publisher fakebin initial
18
+ case_dir="$TMP_ROOT/$name"
19
+ home="$case_dir/home"
20
+ project="$case_dir/project"
21
+ origin="$case_dir/origin.git"
22
+ pool="$case_dir/pool"
23
+ publisher="$case_dir/publisher"
24
+ fakebin=$(make_spawn_fakebin "$case_dir/fake")
25
+
26
+ mkdir -p "$home/data/$id" "$home/projects" "$home/state" "$home/config"
27
+ printf 'codex\n' > "$home/config/crew-harness"
28
+ printf 'brief for %s\n' "$id" > "$home/data/$id/brief.md"
29
+ touch "$home/state/.last-watcher-beat"
30
+
31
+ git init --quiet -b "$default" "$project"
32
+ printf 'base\n' > "$project/README.md"
33
+ git -C "$project" add README.md
34
+ git -C "$project" -c user.name='Firstmate Tests' -c user.email='tests@example.invalid' commit -qm initial
35
+ git clone --quiet --bare "$project" "$origin"
36
+ git -C "$project" remote add origin "file://$origin"
37
+ initial=$(git -C "$project" rev-parse HEAD)
38
+ git -C "$project" worktree add --quiet --detach "$pool" "$initial"
39
+
40
+ git clone --quiet "file://$origin" "$publisher"
41
+ printf 'must survive a newly spawned branch\n' > "$publisher/advanced-main.txt"
42
+ git -C "$publisher" add advanced-main.txt
43
+ git -C "$publisher" -c user.name='Firstmate Tests' -c user.email='tests@example.invalid' commit -qm advance-main
44
+ git -C "$publisher" push --quiet origin "$default"
45
+
46
+ printf '%s\n' "$case_dir|$home|$project|$pool|$fakebin|$initial|$default"
47
+ }
48
+
49
+ read_case_record() {
50
+ IFS='|' read -r CASE_DIR HOME_DIR PROJECT_DIR POOL_DIR FAKEBIN_DIR INITIAL_SHA DEFAULT_BRANCH <<EOF
51
+ $1
52
+ EOF
53
+ }
54
+
55
+ run_spawn() {
56
+ local id=$1
57
+ shift
58
+ fm_test_run_spawn "$HOME_DIR" "$POOL_DIR" "$FAKEBIN_DIR" \
59
+ "$id" "$PROJECT_DIR" "$@"
60
+ }
61
+
62
+ test_stale_pool_base_refreshes_before_branching() {
63
+ local rec id out status current branch_head
64
+ id='pool-current-base-r1'
65
+ rec=$(make_case current-base "$id")
66
+ read_case_record "$rec"
67
+
68
+ out=$(run_spawn "$id" --mode no-mistakes --yolo off)
69
+ status=$?
70
+ expect_code 0 "$status" "spawn should refresh a stale pooled worktree"
71
+ assert_contains "$out" "spawned $id" "spawn did not report success"
72
+ current=$(git -C "$POOL_DIR" rev-parse origin/main)
73
+ branch_head=$(git -C "$POOL_DIR" rev-parse HEAD)
74
+ [ "$branch_head" = "$current" ] || fail "spawn left the pooled worktree on stale history"
75
+ [ "$branch_head" != "$INITIAL_SHA" ] || fail "fixture did not prove origin/main advanced past the pool base"
76
+ if [ "${FM_TEST_EVIDENCE:-0}" = 1 ]; then
77
+ printf '# observed spawn: %s\n' "$(printf '%s\n' "$out" | tail -n 1)"
78
+ printf '# observed base: HEAD=%s origin/main=%s advanced-main=%s\n' \
79
+ "$branch_head" "$current" "$(cat "$POOL_DIR/advanced-main.txt")"
80
+ fi
81
+
82
+ id='pool-current-base-repeat-r1'
83
+ mkdir -p "$HOME_DIR/data/$id"
84
+ printf 'brief for %s\n' "$id" > "$HOME_DIR/data/$id/brief.md"
85
+ out=$(run_spawn "$id" --mode no-mistakes --yolo off)
86
+ status=$?
87
+ expect_code 0 "$status" "repeating the base refresh should be idempotent"
88
+ [ "$(git -C "$POOL_DIR" rev-parse HEAD)" = "$current" ] \
89
+ || fail "an idempotent repeat moved the pool away from current origin/main"
90
+
91
+ git -C "$POOL_DIR" checkout --quiet -b "fm/$id"
92
+ git -C "$POOL_DIR" diff --exit-code origin/main...HEAD >/dev/null \
93
+ || fail "a branch created after spawn differs from current origin/main"
94
+ assert_grep 'must survive a newly spawned branch' "$POOL_DIR/advanced-main.txt" \
95
+ "the branch created after spawn omitted advanced-main content"
96
+ pass "a stale pooled worktree refreshes to current origin/main before a crew branch is created"
97
+ }
98
+
99
+ test_non_main_default_branch_refreshes_before_branching() {
100
+ local rec id out status current branch_head
101
+ id='pool-current-trunk-r2'
102
+ rec=$(make_case current-trunk "$id" trunk)
103
+ read_case_record "$rec"
104
+
105
+ out=$(run_spawn "$id" --mode no-mistakes --yolo off)
106
+ status=$?
107
+ expect_code 0 "$status" "spawn should refresh a stale pooled worktree on a non-main default branch"
108
+ current=$(git -C "$POOL_DIR" rev-parse "origin/$DEFAULT_BRANCH")
109
+ branch_head=$(git -C "$POOL_DIR" rev-parse HEAD)
110
+ [ "$branch_head" = "$current" ] || fail "spawn did not refresh to current origin/$DEFAULT_BRANCH"
111
+ [ "$branch_head" != "$INITIAL_SHA" ] || fail "fixture did not prove origin/$DEFAULT_BRANCH advanced past the pool base"
112
+ pass "a stale pooled worktree resolves and refreshes a non-main default branch"
113
+ }
114
+
115
+ test_unreachable_origin_refuses_stale_pool_base() {
116
+ local rec id out status before after
117
+ id='pool-unreachable-origin-r2'
118
+ rec=$(make_case unreachable-origin "$id")
119
+ read_case_record "$rec"
120
+ git -C "$POOL_DIR" remote set-url origin "file://$CASE_DIR/missing-origin.git"
121
+ before=$(git -C "$POOL_DIR" rev-parse HEAD)
122
+
123
+ out=$(run_spawn "$id" --mode no-mistakes --yolo off)
124
+ status=$?
125
+ [ "$status" -ne 0 ] || fail "spawn succeeded despite an unreachable origin"
126
+ assert_contains "$out" "could not fetch origin" \
127
+ "spawn did not clearly refuse an unreachable origin"
128
+ after=$(git -C "$POOL_DIR" rev-parse HEAD)
129
+ [ "$after" = "$before" ] || fail "spawn changed the pooled worktree after origin became unreachable"
130
+ if [ "${FM_TEST_EVIDENCE:-0}" = 1 ]; then
131
+ printf '# observed unreachable-origin refusal: %s\n' "$(printf '%s\n' "$out" | tail -n 1)"
132
+ fi
133
+ pass "an unreachable origin refuses a potentially stale pooled worktree"
134
+ }
135
+
136
+ test_direct_pr_and_scout_refresh_before_launch() {
137
+ local rec id out status contract current
138
+ for contract in direct-pr scout; do
139
+ id="pool-${contract}-r3"
140
+ rec=$(make_case "$contract" "$id")
141
+ read_case_record "$rec"
142
+ if [ "$contract" = scout ]; then
143
+ out=$(run_spawn "$id" --scout)
144
+ else
145
+ out=$(run_spawn "$id" --mode direct-PR --yolo off)
146
+ fi
147
+ status=$?
148
+ expect_code 0 "$status" "$contract spawn should refresh a stale pooled worktree"
149
+ current=$(git -C "$POOL_DIR" rev-parse origin/main)
150
+ [ "$(git -C "$POOL_DIR" rev-parse HEAD)" = "$current" ] \
151
+ || fail "$contract spawn did not start at current origin/main"
152
+ assert_grep 'must survive a newly spawned branch' "$POOL_DIR/advanced-main.txt" \
153
+ "$contract spawn omitted advanced-main content"
154
+ if [ "${FM_TEST_EVIDENCE:-0}" = 1 ]; then
155
+ printf '# observed %s spawn: %s\n' "$contract" "$(printf '%s\n' "$out" | tail -n 1)"
156
+ fi
157
+ done
158
+ pass "direct-PR ships and scouts both refresh stale pooled worktrees before launch"
159
+ }
160
+
161
+ test_dirty_pool_refuses_without_discarding_work() {
162
+ local rec id out status before
163
+ id='pool-dirty-refusal-r4'
164
+ rec=$(make_case dirty-refusal "$id")
165
+ read_case_record "$rec"
166
+ before=$(git -C "$POOL_DIR" rev-parse HEAD)
167
+ printf 'keep this local work\n' > "$POOL_DIR/uncommitted.txt"
168
+
169
+ out=$(run_spawn "$id" --mode no-mistakes --yolo off)
170
+ status=$?
171
+ [ "$status" -ne 0 ] || fail "spawn succeeded despite a dirty pooled worktree"
172
+ assert_contains "$out" "is not clean" "spawn did not clearly refuse a dirty pooled worktree"
173
+ [ "$(git -C "$POOL_DIR" rev-parse HEAD)" = "$before" ] \
174
+ || fail "spawn moved HEAD while refusing a dirty pooled worktree"
175
+ assert_grep 'keep this local work' "$POOL_DIR/uncommitted.txt" \
176
+ "spawn discarded uncommitted work while refusing the pool"
177
+ if [ "${FM_TEST_EVIDENCE:-0}" = 1 ]; then
178
+ printf '# observed dirty refusal: %s; preserved=%s\n' \
179
+ "$(printf '%s\n' "$out" | tail -n 1)" "$(cat "$POOL_DIR/uncommitted.txt")"
180
+ fi
181
+ pass "a dirty pooled worktree is refused without discarding its local work"
182
+ }
183
+
184
+ test_unresolved_remote_default_refuses_pool() {
185
+ local rec id out status before
186
+ id='pool-unresolved-default-r5'
187
+ rec=$(make_case unresolved-default "$id")
188
+ read_case_record "$rec"
189
+ git --git-dir="$CASE_DIR/origin.git" symbolic-ref HEAD refs/heads/missing-default
190
+ before=$(git -C "$POOL_DIR" rev-parse HEAD)
191
+
192
+ out=$(run_spawn "$id" --mode no-mistakes --yolo off)
193
+ status=$?
194
+ [ "$status" -ne 0 ] || fail "spawn succeeded despite an unresolved remote default branch"
195
+ assert_contains "$out" "could not resolve origin's current default branch" \
196
+ "spawn did not clearly refuse an unresolved remote default branch"
197
+ [ "$(git -C "$POOL_DIR" rev-parse HEAD)" = "$before" ] \
198
+ || fail "spawn moved HEAD after failing to resolve the remote default branch"
199
+ if [ "${FM_TEST_EVIDENCE:-0}" = 1 ]; then
200
+ printf '# observed unresolved-default refusal: %s\n' "$(printf '%s\n' "$out" | tail -n 1)"
201
+ fi
202
+ pass "an unresolved remote default branch refuses the pooled worktree"
203
+ }
204
+
205
+ # A slot left on a stale submodule pin is the field failure this diagnosis exists
206
+ # for: a refresh moved the superproject and left the submodule behind, so the
207
+ # refusal fires a spawn later, on a slot whose own `git status` looks clean to the
208
+ # operator. Nothing here is converged - the gate only has to say why. The fixture
209
+ # only builds the repositories; the residue itself is produced by a real spawn, so
210
+ # these tests cover the reset that actually strands the submodule.
211
+ make_submodule_case() { # <name> <id>
212
+ local name=$1 id=$2 case_dir home project origin pool publisher fakebin sub subpin1 subpin2 advanced
213
+ case_dir="$TMP_ROOT/$name"
214
+ home="$case_dir/home"
215
+ project="$case_dir/project"
216
+ origin="$case_dir/origin.git"
217
+ pool="$case_dir/pool"
218
+ publisher="$case_dir/publisher"
219
+ sub="$case_dir/sub-origin"
220
+ fakebin=$(make_spawn_fakebin "$case_dir/fake")
221
+
222
+ mkdir -p "$home/data/$id" "$home/projects" "$home/state" "$home/config"
223
+ printf 'codex\n' > "$home/config/crew-harness"
224
+ printf 'brief for %s\n' "$id" > "$home/data/$id/brief.md"
225
+ touch "$home/state/.last-watcher-beat"
226
+
227
+ git init --quiet -b main "$sub"
228
+ printf 'pin one\n' > "$sub/lib.txt"
229
+ git -C "$sub" add lib.txt
230
+ git -C "$sub" -c user.name='Firstmate Tests' -c user.email='tests@example.invalid' commit -qm sub-one
231
+ subpin1=$(git -C "$sub" rev-parse HEAD)
232
+ printf 'pin two\n' > "$sub/lib.txt"
233
+ git -C "$sub" -c user.name='Firstmate Tests' -c user.email='tests@example.invalid' commit -qam sub-two
234
+ subpin2=$(git -C "$sub" rev-parse HEAD)
235
+ git -C "$sub" checkout --quiet "$subpin1"
236
+
237
+ git init --quiet -b main "$project"
238
+ printf 'base\n' > "$project/README.md"
239
+ git -C "$project" add README.md
240
+ git -C "$project" -c protocol.file.allow=always -c user.name='Firstmate Tests' -c user.email='tests@example.invalid' \
241
+ submodule --quiet add "file://$sub" ui
242
+ git -C "$project" -c user.name='Firstmate Tests' -c user.email='tests@example.invalid' commit -qm initial
243
+ git clone --quiet --bare "$project" "$origin"
244
+ git -C "$project" remote add origin "file://$origin"
245
+ git -C "$project" worktree add --quiet --detach "$pool" HEAD
246
+ git -C "$pool" -c protocol.file.allow=always submodule --quiet update --init
247
+
248
+ # Advance origin and move the submodule pin, exactly as the field incident did.
249
+ git clone --quiet "file://$origin" "$publisher"
250
+ git -C "$publisher" -c protocol.file.allow=always submodule --quiet update --init
251
+ git -C "$publisher/ui" checkout --quiet "$subpin2"
252
+ git -C "$publisher" -c user.name='Firstmate Tests' -c user.email='tests@example.invalid' commit -qam advance-pin
253
+ git -C "$publisher" push --quiet origin main
254
+ advanced=$(git -C "$publisher" rev-parse HEAD)
255
+
256
+ printf '%s\n' "$case_dir|$home|$project|$pool|$fakebin|$subpin1|$subpin2|$advanced"
257
+ }
258
+
259
+ read_submodule_case() {
260
+ IFS='|' read -r CASE_DIR HOME_DIR PROJECT_DIR POOL_DIR FAKEBIN_DIR SUBPIN1 SUBPIN2 ADVANCED_SHA <<EOF
261
+ $1
262
+ EOF
263
+ }
264
+
265
+ # The first of two consecutive spawns: it succeeds, resets the superproject onto
266
+ # the base that moved the pin, and leaves the submodule checkout on the pin the
267
+ # old base recorded. That reset is what strands the slot, so every case below
268
+ # starts from residue this code path actually produced rather than a hand-built one.
269
+ strand_submodule_pin_via_spawn() { # <seed-id>
270
+ local id=$1 out status
271
+ mkdir -p "$HOME_DIR/data/$id"
272
+ printf 'brief for %s\n' "$id" > "$HOME_DIR/data/$id/brief.md"
273
+ out=$(run_spawn "$id" --mode no-mistakes --yolo off)
274
+ status=$?
275
+ expect_code 0 "$status" "the spawn that moves the submodule pin should succeed"
276
+ assert_contains "$out" "spawned $id" "the spawn that moves the submodule pin did not report success"
277
+ [ "$(git -C "$POOL_DIR" rev-parse HEAD)" = "$ADVANCED_SHA" ] \
278
+ || fail "the first spawn did not move the pooled base across the moved submodule pin"
279
+ [ "$(git -C "$POOL_DIR/ui" rev-parse HEAD)" = "$SUBPIN1" ] \
280
+ || fail "the first spawn did not strand the submodule on the pin the old base recorded"
281
+ }
282
+
283
+ test_stale_submodule_pin_explains_itself() {
284
+ local rec id out status before before_sub
285
+ id='pool-stale-pin-r7'
286
+ rec=$(make_submodule_case stale-pin "$id")
287
+ read_submodule_case "$rec"
288
+ strand_submodule_pin_via_spawn 'pool-stale-pin-seed-r7'
289
+ before=$(git -C "$POOL_DIR" rev-parse HEAD)
290
+ before_sub=$(git -C "$POOL_DIR/ui" rev-parse HEAD)
291
+
292
+ out=$(run_spawn "$id" --mode no-mistakes --yolo off)
293
+ status=$?
294
+ [ "$status" -ne 0 ] || fail "the second spawn launched from a slot carrying a stale submodule pin"
295
+ assert_contains "$out" "stale submodule checkout" \
296
+ "refusal did not name the cause as a stale submodule checkout"
297
+ assert_contains "$out" "submodule 'ui'" "refusal did not name the submodule"
298
+ assert_contains "$out" "$SUBPIN1" "refusal did not report the pin the slot actually has"
299
+ assert_contains "$out" "$SUBPIN2" "refusal did not report the pin the base records"
300
+ # No remedy is printed on purpose: the containment check reads local refs only,
301
+ # so a stale remote-tracking ref can make an unpushed commit look contained, and
302
+ # a checkout command on that judgement could cost the operator a commit.
303
+ assert_not_contains "$out" "submodule update --checkout" \
304
+ "refusal printed a remedy command the containment check cannot stand behind"
305
+ assert_not_contains "$out" "refusing to discard uncommitted work" \
306
+ "a stale pin was misreported as uncommitted work"
307
+ [ "$(git -C "$POOL_DIR" rev-parse HEAD)" = "$before" ] \
308
+ || fail "spawn moved HEAD while refusing a stale submodule pin"
309
+ [ "$(git -C "$POOL_DIR/ui" rev-parse HEAD)" = "$before_sub" ] \
310
+ || fail "spawn converged the submodule; this gate must never touch the slot"
311
+ if [ "${FM_TEST_EVIDENCE:-0}" = 1 ]; then
312
+ printf '# observed stale-pin refusal: %s\n' "$(printf '%s\n' "$out" | grep 'submodule' | head -n 1)"
313
+ fi
314
+ pass "two consecutive spawns across a moved submodule pin end in a refusal naming both pins and no remedy"
315
+ }
316
+
317
+ test_unpushed_submodule_commit_is_still_uncommitted_work() {
318
+ local rec id out status unpushed before before_sub
319
+ id='pool-sub-unpushed-r10'
320
+ rec=$(make_submodule_case sub-unpushed "$id")
321
+ read_submodule_case "$rec"
322
+ strand_submodule_pin_via_spawn 'pool-sub-unpushed-seed-r10'
323
+ # A commit made inside the submodule and never pushed leaves the submodule work
324
+ # tree clean and the pins different - the same two facts a stale pin shows. Any
325
+ # checkout of the recorded pin would move HEAD off this commit and leave it
326
+ # unreferenced, so this case must keep the conservative refusal.
327
+ printf 'unlanded submodule work\n' > "$POOL_DIR/ui/unlanded.txt"
328
+ git -C "$POOL_DIR/ui" add unlanded.txt
329
+ git -C "$POOL_DIR/ui" -c user.name='Firstmate Tests' -c user.email='tests@example.invalid' \
330
+ commit -qm unlanded-submodule-work
331
+ unpushed=$(git -C "$POOL_DIR/ui" rev-parse HEAD)
332
+ [ -z "$(git -C "$POOL_DIR/ui" status --porcelain)" ] \
333
+ || fail "fixture did not leave the submodule work tree clean"
334
+ [ "$unpushed" != "$(git -C "$POOL_DIR" rev-parse "HEAD:ui")" ] \
335
+ || fail "fixture did not leave the recorded pin different from what is checked out"
336
+ before=$(git -C "$POOL_DIR" rev-parse HEAD)
337
+ before_sub=$unpushed
338
+
339
+ out=$(run_spawn "$id" --mode no-mistakes --yolo off)
340
+ status=$?
341
+ [ "$status" -ne 0 ] || fail "spawn launched from a slot holding an unpushed submodule commit"
342
+ assert_contains "$out" "refusing to discard uncommitted work" \
343
+ "an unpushed submodule commit was not refused as uncommitted work"
344
+ assert_not_contains "$out" "stale submodule checkout" \
345
+ "an unpushed submodule commit was misreported as a stale pin"
346
+ assert_not_contains "$out" "is checked out at" \
347
+ "an unpushed submodule commit still drew the stale-pin diagnosis"
348
+ [ "$(git -C "$POOL_DIR/ui" rev-parse HEAD)" = "$before_sub" ] \
349
+ || fail "spawn moved the submodule off its unpushed commit"
350
+ git -C "$POOL_DIR/ui" cat-file -e "$unpushed^{commit}" \
351
+ || fail "the unpushed submodule commit did not survive the refusal"
352
+ assert_grep 'unlanded submodule work' "$POOL_DIR/ui/unlanded.txt" \
353
+ "spawn discarded the unpushed submodule work while refusing the pool"
354
+ [ "$(git -C "$POOL_DIR" rev-parse HEAD)" = "$before" ] \
355
+ || fail "spawn moved HEAD while refusing a slot holding an unpushed submodule commit"
356
+ pass "an unpushed submodule commit keeps the uncommitted-work refusal and survives it"
357
+ }
358
+
359
+ test_work_inside_submodule_is_still_uncommitted_work() {
360
+ local rec id out status
361
+ id='pool-sub-work-r8'
362
+ rec=$(make_submodule_case sub-work "$id")
363
+ read_submodule_case "$rec"
364
+ strand_submodule_pin_via_spawn 'pool-sub-work-seed-r8'
365
+ # Put the submodule back on the pin the base records, so the ONLY deviation is
366
+ # real work inside it. This must never be softened into a stale-pin diagnosis.
367
+ git -C "$POOL_DIR/ui" checkout --quiet "$SUBPIN2"
368
+ printf 'work that must survive\n' > "$POOL_DIR/ui/keep-me.txt"
369
+
370
+ out=$(run_spawn "$id" --mode no-mistakes --yolo off)
371
+ status=$?
372
+ [ "$status" -ne 0 ] || fail "spawn launched from a slot holding work inside a submodule"
373
+ assert_contains "$out" "refusing to discard uncommitted work" \
374
+ "work inside a submodule was not refused as uncommitted work"
375
+ assert_not_contains "$out" "stale submodule checkout" \
376
+ "real work inside a submodule was misreported as a stale pin"
377
+ assert_grep 'work that must survive' "$POOL_DIR/ui/keep-me.txt" \
378
+ "spawn discarded work inside the submodule while refusing the pool"
379
+ pass "work inside a submodule is still refused as uncommitted work, not called stale"
380
+ }
381
+
382
+ test_stale_pin_carrying_real_work_is_not_called_stale() {
383
+ local rec id out status
384
+ id='pool-sub-both-r9'
385
+ rec=$(make_submodule_case sub-both "$id")
386
+ read_submodule_case "$rec"
387
+ strand_submodule_pin_via_spawn 'pool-sub-both-seed-r9'
388
+ # Stale pin AND real work inside it: calling this merely stale would be wrong, so
389
+ # the refusal must stay the conservative one.
390
+ printf 'work that must survive\n' > "$POOL_DIR/ui/keep-me.txt"
391
+
392
+ out=$(run_spawn "$id" --mode no-mistakes --yolo off)
393
+ status=$?
394
+ [ "$status" -ne 0 ] || fail "spawn launched from a slot with a stale pin and work inside it"
395
+ assert_contains "$out" "refusing to discard uncommitted work" \
396
+ "a stale pin carrying real work was not refused as uncommitted work"
397
+ assert_not_contains "$out" "stale submodule checkout" \
398
+ "a submodule holding real work was reported as merely stale"
399
+ assert_grep 'work that must survive' "$POOL_DIR/ui/keep-me.txt" \
400
+ "spawn discarded work inside the submodule while refusing the pool"
401
+ pass "a stale pin carrying real work is refused conservatively, never called stale"
402
+ }
403
+
404
+ test_stale_pin_beside_other_dirt_reports_one_verdict() {
405
+ local rec id out status
406
+ id='pool-sub-mixed-r11'
407
+ rec=$(make_submodule_case sub-mixed "$id")
408
+ read_submodule_case "$rec"
409
+ strand_submodule_pin_via_spawn 'pool-sub-mixed-seed-r11'
410
+ # Git sorts status paths, so the stale 'ui' entry is scanned before this file.
411
+ # The conservative verdict must not arrive contradicted by a stale-pin line.
412
+ printf 'notes the operator still wants\n' > "$POOL_DIR/zz-notes.txt"
413
+
414
+ out=$(run_spawn "$id" --mode no-mistakes --yolo off)
415
+ status=$?
416
+ [ "$status" -ne 0 ] || fail "spawn launched from a slot with a stale pin beside an untracked file"
417
+ assert_contains "$out" "refusing to discard uncommitted work" \
418
+ "a stale pin beside an untracked file was not refused as uncommitted work"
419
+ assert_not_contains "$out" "stale submodule checkout" \
420
+ "a slot carrying more than a stale pin was reported as merely stale"
421
+ assert_not_contains "$out" "is checked out at" \
422
+ "the stale-pin diagnosis was printed alongside the conservative refusal"
423
+ assert_grep 'notes the operator still wants' "$POOL_DIR/zz-notes.txt" \
424
+ "spawn discarded the untracked file while refusing the pool"
425
+ pass "a stale pin beside other dirt yields the conservative refusal alone, with no stale-pin line"
426
+ }
427
+
428
+ test_stale_pool_base_refreshes_before_branching
429
+ test_non_main_default_branch_refreshes_before_branching
430
+ test_direct_pr_and_scout_refresh_before_launch
431
+ test_dirty_pool_refuses_without_discarding_work
432
+ test_unresolved_remote_default_refuses_pool
433
+ test_unreachable_origin_refuses_stale_pool_base
434
+ test_stale_submodule_pin_explains_itself
435
+ test_unpushed_submodule_commit_is_still_uncommitted_work
436
+ test_work_inside_submodule_is_still_uncommitted_work
437
+ test_stale_pin_carrying_real_work_is_not_called_stale
438
+ test_stale_pin_beside_other_dirt_reports_one_verdict
439
+
440
+ echo "# all fm-spawn-pool-base-freshen tests passed"
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env bash
2
+ # Regression test for the fm-spawn.sh treehouse-get worktree-detection settle
3
+ # loop (bin/fm-spawn.sh, the `for _ in $(seq 1 60)` loop after `treehouse get`).
4
+ #
5
+ # On some tmux/WSL setups a brand-new window's pane_current_path transiently
6
+ # reports a stale, unrelated-but-real path on the very first poll, before the
7
+ # pane actually settles into the worktree treehouse get moved it to. That stale
8
+ # path still passes the loop's "differs from the project" check and
9
+ # validate_spawn_worktree's "is a real, distinct worktree" check (it IS a real
10
+ # git checkout, just the wrong one), so a naive single-read loop silently
11
+ # records the wrong worktree= in state/<id>.meta. This test simulates that
12
+ # transient-then-settled pane_current_path sequence with a fake tmux and
13
+ # asserts the recorded worktree resolves to the real, settled worktree, never
14
+ # the stale first read.
15
+ set -u
16
+
17
+ # shellcheck source=tests/lib.sh
18
+ . "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
19
+
20
+ SPAWN="$ROOT/bin/fm-spawn.sh"
21
+ TMP_ROOT=$(fm_test_tmproot fm-spawn-worktree-settle)
22
+
23
+ # make_settle_fakebin <dir> builds a fake tmux whose `#{pane_current_path}`
24
+ # query returns FM_FAKE_PANE_STALE for the first FM_FAKE_PANE_STALE_READS
25
+ # calls, then FM_FAKE_PANE_PATH forever after - reproducing a pane that
26
+ # transiently reports a stale cwd before settling into the real worktree.
27
+ make_settle_fakebin() {
28
+ local dir=$1 fakebin
29
+ fakebin=$(fm_fakebin "$dir")
30
+ cat > "$fakebin/tmux" <<'SH'
31
+ #!/usr/bin/env bash
32
+ set -u
33
+ case "$*" in
34
+ *"#{pane_current_path}"*)
35
+ countfile="${FM_FAKE_PANE_COUNTFILE:?FM_FAKE_PANE_COUNTFILE unset}"
36
+ n=0
37
+ [ -f "$countfile" ] && n=$(cat "$countfile")
38
+ n=$((n + 1))
39
+ printf '%s\n' "$n" > "$countfile"
40
+ if [ "$n" -le "${FM_FAKE_PANE_STALE_READS:-0}" ]; then
41
+ printf '%s\n' "${FM_FAKE_PANE_STALE:-}"
42
+ else
43
+ printf '%s\n' "${FM_FAKE_PANE_PATH:-}"
44
+ fi
45
+ exit 0
46
+ ;;
47
+ esac
48
+ case "${1:-}" in
49
+ display-message) printf 'firstmate\n'; exit 0 ;;
50
+ list-windows) exit 0 ;;
51
+ has-session|new-session|new-window|kill-window) exit 0 ;;
52
+ send-keys) exit 0 ;;
53
+ esac
54
+ exit 0
55
+ SH
56
+ chmod +x "$fakebin/tmux"
57
+ fm_fake_exit0 "$fakebin" treehouse
58
+ printf '%s\n' "$fakebin"
59
+ }
60
+
61
+ # make_settle_case <name> <id> <stale_reads> builds a home, a primary project
62
+ # with a real worktree (the eventual settled path), and a separate real git
63
+ # repo standing in for the stale path (a real checkout of something else
64
+ # entirely, distinct from both the project and the worktree - mirroring the
65
+ # live incident where the stale read was another real firstmate home).
66
+ make_settle_case() {
67
+ local name=$1 id=$2 stale_reads=$3 case_dir home proj wt stale fakebin countfile
68
+ case_dir="$TMP_ROOT/$name"
69
+ home="$case_dir/home"
70
+ proj="$case_dir/project"
71
+ wt="$case_dir/wt"
72
+ stale="$case_dir/stale-other-checkout"
73
+ countfile="$case_dir/pane-call-count"
74
+ fakebin=$(make_settle_fakebin "$case_dir/fake")
75
+ mkdir -p "$home/data" "$home/projects" "$home/state" "$home/config"
76
+ printf 'codex\n' > "$home/config/crew-harness"
77
+ fm_git_worktree "$proj" "$wt" "wt-$name"
78
+ fm_git_init_commit "$stale"
79
+ mkdir -p "$home/data/$id"
80
+ printf 'brief for %s\n' "$id" > "$home/data/$id/brief.md"
81
+ touch "$home/state/.last-watcher-beat"
82
+ printf '%s\n' "$case_dir|$home|$proj|$wt|$stale|$fakebin|$countfile|$stale_reads"
83
+ }
84
+
85
+ read_settle_record() {
86
+ IFS='|' read -r _ HOME_DIR PROJ_DIR WT_DIR STALE_DIR FAKEBIN_DIR COUNTFILE STALE_READS <<EOF
87
+ $1
88
+ EOF
89
+ }
90
+
91
+ run_settle_spawn() {
92
+ local id=$1
93
+ FM_ROOT_OVERRIDE='' FM_HOME="$HOME_DIR" \
94
+ FM_STATE_OVERRIDE="$HOME_DIR/state" FM_DATA_OVERRIDE="$HOME_DIR/data" \
95
+ FM_PROJECTS_OVERRIDE="$HOME_DIR/projects" FM_CONFIG_OVERRIDE="$HOME_DIR/config" \
96
+ FM_SPAWN_NO_GUARD=1 TMUX="fake,1,0" \
97
+ FM_FAKE_PANE_PATH="$WT_DIR" FM_FAKE_PANE_STALE="$STALE_DIR" \
98
+ FM_FAKE_PANE_STALE_READS="$STALE_READS" FM_FAKE_PANE_COUNTFILE="$COUNTFILE" \
99
+ PATH="$FAKEBIN_DIR:$PATH" \
100
+ "$SPAWN" "$id" "$PROJ_DIR" --mode no-mistakes --yolo off 2>&1
101
+ }
102
+
103
+ # A single stale first read (the exact incident) must not be accepted: the
104
+ # loop should keep polling until two consecutive reads agree, landing on the
105
+ # real settled worktree instead.
106
+ test_single_stale_first_read_is_not_accepted() {
107
+ local rec id out status
108
+ id=settle-single-stale-z1
109
+ rec=$(make_settle_case settle-single "$id" 1)
110
+ read_settle_record "$rec"
111
+
112
+ out=$(run_settle_spawn "$id")
113
+ status=$?
114
+ expect_code 0 "$status" "spawn should succeed once the pane settles"
115
+ assert_contains "$out" "spawned $id" "spawn did not report success"
116
+ assert_grep "worktree=$WT_DIR" "$HOME_DIR/state/$id.meta" \
117
+ "meta did not record the settled worktree"
118
+ assert_no_grep "worktree=$STALE_DIR" "$HOME_DIR/state/$id.meta" \
119
+ "meta wrongly recorded the transient stale path as the worktree"
120
+ pass "a single transient stale pane_current_path read is not accepted as the worktree"
121
+ }
122
+
123
+ # A pane that reports the real worktree from the very first read still only
124
+ # costs the loop's existing one-second inter-poll sleep to confirm - not an
125
+ # extra full cycle on top of that.
126
+ test_already_settled_pane_costs_one_confirm_sleep() {
127
+ local rec id out status start end elapsed
128
+ id=settle-already-settled-z2
129
+ rec=$(make_settle_case settle-already-settled "$id" 0)
130
+ read_settle_record "$rec"
131
+
132
+ start=$(date +%s)
133
+ out=$(run_settle_spawn "$id")
134
+ status=$?
135
+ end=$(date +%s)
136
+ elapsed=$((end - start))
137
+ expect_code 0 "$status" "spawn should succeed when the pane is already settled"
138
+ assert_grep "worktree=$WT_DIR" "$HOME_DIR/state/$id.meta" \
139
+ "meta did not record the already-settled worktree"
140
+ [ "$elapsed" -le 5 ] || fail "already-settled pane took ${elapsed}s to confirm - expected close to the single inter-poll sleep"
141
+ pass "an already-settled pane confirms via the existing inter-poll sleep, not an extra full cycle"
142
+ }
143
+
144
+ test_single_stale_first_read_is_not_accepted
145
+ test_already_settled_pane_costs_one_confirm_sleep
146
+
147
+ echo "# all fm-spawn-worktree-settle tests passed"