@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,721 @@
1
+ #!/usr/bin/env bash
2
+ # Behavior tests for fm-fleet-sync.sh drift handling.
3
+ #
4
+ # fm-fleet-sync fast-forwards a clone that is cleanly on its default branch. This
5
+ # suite pins the two behavioral additions on top of that:
6
+ # - the one safe drift self-heals: a clean, detached HEAD that holds no unique
7
+ # commits (it is an ancestor of origin/<default>) and whose <default> is free
8
+ # to check out is re-attached and then fast-forwarded ("recovered:").
9
+ # - every other off-default state is left untouched and reported as a loud,
10
+ # quantified "STUCK: ... N commits behind ... - needs attention" warning
11
+ # instead of a quiet skip.
12
+ # The pre-existing fast-forward / already-current / local-only / no-origin paths
13
+ # must be unchanged, and bootstrap must relay the new outcomes as FLEET_SYNC lines.
14
+ #
15
+ # It also pins the clone-root guard: a plain directory under projects/ resolves,
16
+ # through git's upward repository discovery, to the ENCLOSING repository - in a
17
+ # firstmate home, the firstmate checkout itself - so it must be skipped by name
18
+ # with the enclosing repo left untouched, in both the whole-fleet and
19
+ # single-project forms, while a symlinked clone dir still syncs.
20
+ #
21
+ # It also pins the orphaned .git/packed-refs.lock recovery in the fetch step
22
+ # (fetch_with_packed_refs_lock_guard, backed by bin/fm-lock-lib.sh's shared
23
+ # staleness proof): a provably-stale lock is retried then removed and the clone
24
+ # syncs (with a "recovered:" summary on stdout so a session-start refresh, which
25
+ # discards stderr, still surfaces it); a live lock (fake lsof holder) is never
26
+ # removed and the sync fails loudly; a live process merely holding the clone
27
+ # worktree dir as its cwd also blocks removal (the clone-dir liveness check); a
28
+ # transient lock that self-clears is retried without a force-remove; and any
29
+ # non-packed-refs.lock fetch failure keeps today's behavior with no retry.
30
+ set -u
31
+
32
+ # shellcheck source=tests/lib.sh
33
+ . "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
34
+
35
+ fm_git_identity fmtest fmtest@example.invalid
36
+
37
+ TMP_ROOT=$(fm_test_tmproot fm-fleet-sync-tests)
38
+ HOME_N=0
39
+
40
+ # --- fixtures ---------------------------------------------------------------
41
+
42
+ # new_home: fresh isolated FM_HOME with an empty projects/ dir. Each test gets its
43
+ # own so the whole-fleet form never sees another test's clones.
44
+ new_home() {
45
+ HOME_N=$((HOME_N + 1))
46
+ local h="$TMP_ROOT/home-$HOME_N"
47
+ mkdir -p "$h/projects"
48
+ printf '%s\n' "$h"
49
+ }
50
+
51
+ commit_file() {
52
+ local dir=$1 file=$2 content=$3 msg=$4
53
+ printf '%s\n' "$content" > "$dir/$file"
54
+ git -C "$dir" add "$file"
55
+ git -C "$dir" commit -qm "$msg"
56
+ }
57
+
58
+ # build_pair <home> <name>: create projects/<name>, a clone of a fresh bare origin
59
+ # with one commit on main, plus a side "work-<name>" repo wired to that origin for
60
+ # advancing it later. Portable branch naming (no init -b) for older git.
61
+ build_pair() {
62
+ local home=$1 name=$2 work remote clone remote_abs
63
+ work="$home/work-$name"
64
+ remote="$home/remotes/$name.git"
65
+ clone="$home/projects/$name"
66
+ mkdir -p "$home/remotes"
67
+
68
+ git init -q "$work"
69
+ git -C "$work" symbolic-ref HEAD refs/heads/main
70
+ commit_file "$work" file.txt v0 C0
71
+
72
+ git clone --quiet --bare "$work" "$remote"
73
+ remote_abs=$(cd "$remote" && pwd)
74
+ git -C "$work" remote add origin "file://$remote_abs"
75
+ git -C "$work" push -q -u origin main
76
+
77
+ git clone --quiet "file://$remote_abs" "$clone"
78
+ printf '%s\n' "$clone"
79
+ }
80
+
81
+ # advance_origin <home> <name> <msg>: push one more commit to <name>'s origin via
82
+ # its work repo, so the clone (until it fetches) is one commit behind origin/main.
83
+ advance_origin() {
84
+ local home=$1 name=$2 msg=$3 work
85
+ work="$home/work-$name"
86
+ commit_file "$work" file.txt "$msg" "$msg"
87
+ git -C "$work" push -q origin main
88
+ }
89
+
90
+ head_sha() { git -C "$1" rev-parse HEAD; }
91
+
92
+ # run_sync <home> [args...]: run fleet-sync against an isolated home, stdout only.
93
+ run_sync() {
94
+ local home=$1
95
+ shift
96
+ FM_HOME="$home" FM_ROOT_OVERRIDE="$ROOT" "$ROOT/bin/fm-fleet-sync.sh" "$@" 2>/dev/null
97
+ }
98
+
99
+ # build_enclosing_home <name>: an FM_HOME that is itself nested inside another git
100
+ # repository - firstmate's own layout, where projects/ sits inside the firstmate
101
+ # checkout. The enclosing repo is a clean clone of a bare origin that is one commit
102
+ # ahead, so a sync that walked git discovery UP out of projects/<dir> would find a
103
+ # fast-forward available and visibly take it. Echoes the enclosing repo, which is
104
+ # also the home. Its work tree is left pristine so the only thing under projects/
105
+ # is what the test puts there.
106
+ build_enclosing_home() {
107
+ local name=$1 root work remote enclosing remote_abs
108
+ root="$TMP_ROOT/enclosing-$name"
109
+ work="$root/work"
110
+ remote="$root/remote.git"
111
+ enclosing="$root/enclosing"
112
+ mkdir -p "$root"
113
+
114
+ git init -q "$work"
115
+ git -C "$work" symbolic-ref HEAD refs/heads/main
116
+ printf '/projects/\n' > "$work/.gitignore"
117
+ git -C "$work" add .gitignore
118
+ commit_file "$work" AGENTS.md v0 C0
119
+
120
+ git clone --quiet --bare "$work" "$remote"
121
+ remote_abs=$(cd "$remote" && pwd)
122
+ git -C "$work" remote add origin "file://$remote_abs"
123
+ git -C "$work" push -q -u origin main
124
+
125
+ git clone --quiet "file://$remote_abs" "$enclosing"
126
+ commit_file "$work" AGENTS.md v1 C1
127
+ git -C "$work" push -q origin main
128
+
129
+ mkdir -p "$enclosing/projects"
130
+ printf '%s\n' "$enclosing"
131
+ }
132
+
133
+ # --- packed-refs.lock fixtures ----------------------------------------------
134
+
135
+ # build_packed_prunable <home> <name>: like build_pair, but the clone has PACKED
136
+ # refs plus a local `feature` branch tracking a since-deleted origin/feature, so a
137
+ # fetch --prune must rewrite packed-refs - which an orphaned .git/packed-refs.lock
138
+ # blocks with Git's "Unable to create '...packed-refs.lock': File exists". origin/main
139
+ # is advanced by one commit so a successful sync fast-forwards. Echoes the clone path.
140
+ build_packed_prunable() {
141
+ local home=$1 name=$2 work remote clone remote_abs
142
+ work="$home/work-$name"
143
+ remote="$home/remotes/$name.git"
144
+ clone="$home/projects/$name"
145
+ mkdir -p "$home/remotes"
146
+
147
+ git init -q "$work"
148
+ git -C "$work" symbolic-ref HEAD refs/heads/main
149
+ commit_file "$work" file.txt v0 C0
150
+ git clone --quiet --bare "$work" "$remote"
151
+ remote_abs=$(cd "$remote" && pwd)
152
+ git -C "$work" remote add origin "file://$remote_abs"
153
+ git -C "$work" push -q -u origin main
154
+ git -C "$work" push -q origin main:refs/heads/feature
155
+
156
+ git clone --quiet "file://$remote_abs" "$clone"
157
+ git -C "$clone" branch -q feature origin/feature
158
+ commit_file "$work" file.txt v1 C1
159
+ git -C "$work" push -q origin main
160
+ git -C "$work" push -q origin --delete feature
161
+ git -C "$clone" pack-refs --all
162
+ printf '%s\n' "$clone"
163
+ }
164
+
165
+ plant_packed_refs_lock() { : > "$1/.git/packed-refs.lock"; }
166
+
167
+ # lsof shims mirror tests/fm-teardown.test.sh: no-holder (provably free), a live
168
+ # holder, and an lsof error. Written into a per-home fakebin/ prepended to PATH.
169
+ lsof_no_holder() {
170
+ cat > "$1/lsof" <<'SH'
171
+ #!/usr/bin/env bash
172
+ exit 1
173
+ SH
174
+ chmod +x "$1/lsof"
175
+ }
176
+ lsof_live_holder() {
177
+ cat > "$1/lsof" <<'SH'
178
+ #!/usr/bin/env bash
179
+ exit 0
180
+ SH
181
+ chmod +x "$1/lsof"
182
+ }
183
+
184
+ # lsof shim: a holder ONLY for $FLEET_TEST_LIVE_DIR (a live `git -C <clone>` keeping
185
+ # its cwd there), and no holder of the lock file itself - the exact window the
186
+ # clone-dir liveness check must cover.
187
+ lsof_holds_only_live_dir() {
188
+ cat > "$1/lsof" <<'SH'
189
+ #!/usr/bin/env bash
190
+ target=
191
+ for a in "$@"; do case "$a" in --|-*) ;; *) target=$a ;; esac; done
192
+ [ -n "${FLEET_TEST_LIVE_DIR:-}" ] && [ "$target" = "$FLEET_TEST_LIVE_DIR" ] && exit 0
193
+ exit 1
194
+ SH
195
+ chmod +x "$1/lsof"
196
+ }
197
+
198
+ # git shim: fail the FIRST `fetch` with the packed-refs.lock signature and drop
199
+ # the lock (simulating the dying ref-rewrite finishing), then delegate every
200
+ # later call - including the retried fetch - to the real git so the sync completes.
201
+ git_transient_packed_refs_lock() {
202
+ cat > "$1/git" <<'SH'
203
+ #!/usr/bin/env bash
204
+ real=${REAL_GIT_FOR_TEST:?}
205
+ dir=; is_fetch=0
206
+ for a in "$@"; do [ "$a" = fetch ] && is_fetch=1; done
207
+ prev=
208
+ for a in "$@"; do [ "$prev" = -C ] && dir=$a; prev=$a; done
209
+ if [ "$is_fetch" = 1 ]; then
210
+ n=$(cat "${GIT_FETCH_COUNTER:?}" 2>/dev/null || echo 0); n=$(( n + 1 ))
211
+ printf '%s\n' "$n" > "$GIT_FETCH_COUNTER"
212
+ if [ "$n" -eq 1 ]; then
213
+ lock="$dir/.git/packed-refs.lock"
214
+ echo "error: could not delete reference refs/remotes/origin/feature: Unable to create '$lock': File exists." >&2
215
+ rm -f "$lock"
216
+ exit 1
217
+ fi
218
+ fi
219
+ exec "$real" "$@"
220
+ SH
221
+ chmod +x "$1/git"
222
+ }
223
+
224
+ # run_sync_guarded <home> <fakebin> <outfile> <errfile> [args...]: run fleet-sync
225
+ # with the fakebin on PATH and stdout/stderr captured separately. Per-test knobs
226
+ # (FM_FLEET_SYNC_PACKED_REFS_LOCK_*, GIT_FETCH_COUNTER) are read from the caller's
227
+ # exported environment.
228
+ run_sync_guarded() {
229
+ local home=$1 fakebin=$2 outf=$3 errf=$4 realgit
230
+ shift 4
231
+ realgit=$(command -v git)
232
+ PATH="$fakebin:$PATH" REAL_GIT_FOR_TEST="$realgit" \
233
+ FM_HOME="$home" FM_ROOT_OVERRIDE="$ROOT" \
234
+ "$ROOT/bin/fm-fleet-sync.sh" "$@" >"$outf" 2>"$errf"
235
+ }
236
+
237
+ # --- tests ------------------------------------------------------------------
238
+
239
+ test_detached_clean_ancestor_recovers() {
240
+ local home clone out before after
241
+ home=$(new_home)
242
+ clone=$(build_pair "$home" alpha)
243
+ advance_origin "$home" alpha C1
244
+ before=$(head_sha "$clone")
245
+ # Detach at the clone's main (C0), an ancestor of the now-advanced origin/main.
246
+ git -C "$clone" checkout --detach --quiet
247
+
248
+ out=$(run_sync "$home" "$clone")
249
+
250
+ assert_contains "$out" "alpha: recovered: re-attached main, synced" "detached-clean-ancestor reports recovered"
251
+ assert_not_contains "$out" "STUCK" "recovered case is not flagged STUCK"
252
+ [ "$(git -C "$clone" symbolic-ref --short HEAD 2>/dev/null)" = "main" ] \
253
+ || fail "expected re-attach to main, HEAD still detached"
254
+ after=$(head_sha "$clone")
255
+ [ "$after" != "$before" ] || fail "expected fast-forward after re-attach, HEAD unchanged"
256
+ [ "$after" = "$(git -C "$clone" rev-parse origin/main)" ] \
257
+ || fail "expected HEAD at origin/main after recovery"
258
+ pass "detached clean ancestor is re-attached and fast-forwarded (recovered)"
259
+ }
260
+
261
+ test_detached_unique_commit_is_stuck_untouched() {
262
+ local home clone out before
263
+ home=$(new_home)
264
+ clone=$(build_pair "$home" beta)
265
+ git -C "$clone" checkout --detach --quiet
266
+ commit_file "$clone" extra.txt unique "local unique work"
267
+ before=$(head_sha "$clone")
268
+ advance_origin "$home" beta C1
269
+
270
+ out=$(run_sync "$home" "$clone")
271
+
272
+ assert_contains "$out" "beta: STUCK:" "detached-with-unique-commit reports STUCK"
273
+ assert_contains "$out" "unique commits" "STUCK names the unique-commit state"
274
+ assert_contains "$out" "commits behind origin/main - needs attention" "STUCK is quantified"
275
+ assert_not_contains "$out" "recovered" "unique-commit case is never recovered"
276
+ [ "$(head_sha "$clone")" = "$before" ] || fail "expected unique-commit detached HEAD left untouched"
277
+ pass "detached HEAD with unique commits is reported STUCK and left untouched"
278
+ }
279
+
280
+ test_detached_clean_ancestor_with_diverged_local_default_is_stuck_untouched() {
281
+ local home clone out before local_main
282
+ home=$(new_home)
283
+ clone=$(build_pair "$home" beta-local-default)
284
+ commit_file "$clone" local.txt local "local divergent main commit"
285
+ local_main=$(git -C "$clone" rev-parse main)
286
+ git -C "$clone" checkout --detach --quiet HEAD^
287
+ before=$(head_sha "$clone")
288
+ advance_origin "$home" beta-local-default C1
289
+
290
+ out=$(run_sync "$home" "$clone")
291
+
292
+ assert_contains "$out" "beta-local-default: STUCK:" "diverged local default reports STUCK"
293
+ assert_contains "$out" "local main diverged from origin/main" "STUCK names the unsafe local default"
294
+ assert_not_contains "$out" "recovered" "diverged local default is never recovered"
295
+ [ "$(head_sha "$clone")" = "$before" ] || fail "detached HEAD was moved"
296
+ ! git -C "$clone" symbolic-ref -q HEAD >/dev/null || fail "clone re-attached to local default"
297
+ [ "$(git -C "$clone" rev-parse main)" = "$local_main" ] || fail "local default branch was moved"
298
+ pass "detached clean ancestor with diverged local default is reported STUCK and left untouched"
299
+ }
300
+
301
+ test_dirty_is_stuck_untouched() {
302
+ local home clone out before
303
+ home=$(new_home)
304
+ clone=$(build_pair "$home" gamma)
305
+ advance_origin "$home" gamma C1
306
+ before=$(head_sha "$clone")
307
+ printf 'uncommitted edit\n' >> "$clone/file.txt"
308
+
309
+ out=$(run_sync "$home" "$clone")
310
+
311
+ assert_contains "$out" "gamma: STUCK:" "dirty clone reports STUCK"
312
+ assert_contains "$out" "uncommitted changes" "STUCK names the dirty state"
313
+ assert_contains "$out" "1 commits behind origin/main" "STUCK quantifies how far behind"
314
+ [ "$(head_sha "$clone")" = "$before" ] || fail "dirty clone HEAD was moved"
315
+ grep -q "uncommitted edit" "$clone/file.txt" || fail "dirty working-tree change was discarded"
316
+ pass "dirty working tree is reported STUCK and left untouched"
317
+ }
318
+
319
+ test_non_default_branch_is_stuck_untouched() {
320
+ local home clone out
321
+ home=$(new_home)
322
+ clone=$(build_pair "$home" delta)
323
+ git -C "$clone" checkout -q -b feature
324
+ advance_origin "$home" delta C1
325
+
326
+ out=$(run_sync "$home" "$clone")
327
+
328
+ assert_contains "$out" "delta: STUCK: on branch feature" "non-default branch reports STUCK with branch name"
329
+ assert_contains "$out" "commits behind origin/main - needs attention" "STUCK is quantified"
330
+ assert_not_contains "$out" "recovered" "named branch is never auto-changed"
331
+ [ "$(git -C "$clone" symbolic-ref --short HEAD)" = "feature" ] || fail "named branch checkout was changed"
332
+ pass "non-default named branch is reported STUCK and left untouched"
333
+ }
334
+
335
+ test_diverged_is_stuck_untouched() {
336
+ local home clone out before
337
+ home=$(new_home)
338
+ clone=$(build_pair "$home" epsilon)
339
+ # Local main gains its own commit; origin advances down a different line.
340
+ commit_file "$clone" local.txt local "local divergent commit"
341
+ before=$(head_sha "$clone")
342
+ advance_origin "$home" epsilon C1
343
+
344
+ out=$(run_sync "$home" "$clone")
345
+
346
+ assert_contains "$out" "epsilon: STUCK:" "diverged clone reports STUCK"
347
+ assert_contains "$out" "diverged main" "STUCK names the diverged state"
348
+ assert_contains "$out" "commits behind origin/main - needs attention" "STUCK is quantified"
349
+ [ "$(head_sha "$clone")" = "$before" ] || fail "diverged clone was moved"
350
+ pass "diverged default branch is reported STUCK and left untouched"
351
+ }
352
+
353
+ test_on_default_clean_behind_fast_forwards() {
354
+ local home clone out
355
+ home=$(new_home)
356
+ clone=$(build_pair "$home" zeta)
357
+ advance_origin "$home" zeta C1
358
+
359
+ out=$(run_sync "$home" "$clone")
360
+
361
+ assert_contains "$out" "zeta: synced" "on-default clean behind fast-forwards as before"
362
+ assert_not_contains "$out" "recovered" "ordinary fast-forward is not labelled recovered"
363
+ assert_not_contains "$out" "STUCK" "ordinary fast-forward is not flagged STUCK"
364
+ [ "$(head_sha "$clone")" = "$(git -C "$clone" rev-parse origin/main)" ] || fail "clone was not fast-forwarded"
365
+ pass "on-default clean behind clone still fast-forwards"
366
+ }
367
+
368
+ test_already_current_unchanged() {
369
+ local home clone out before
370
+ home=$(new_home)
371
+ clone=$(build_pair "$home" eta)
372
+ before=$(head_sha "$clone")
373
+
374
+ out=$(run_sync "$home" "$clone")
375
+
376
+ assert_contains "$out" "eta: already current" "already-current clone reports unchanged"
377
+ assert_not_contains "$out" "STUCK" "already-current is not flagged STUCK"
378
+ assert_not_contains "$out" "recovered" "already-current is not labelled recovered"
379
+ [ "$(head_sha "$clone")" = "$before" ] || fail "already-current clone was moved"
380
+ pass "already-current clone is reported unchanged"
381
+ }
382
+
383
+ test_no_origin_skipped() {
384
+ local home clone out
385
+ home=$(new_home)
386
+ clone="$home/projects/theta"
387
+ git init -q "$clone"
388
+ git -C "$clone" symbolic-ref HEAD refs/heads/main
389
+ commit_file "$clone" file.txt v0 C0
390
+
391
+ out=$(run_sync "$home" "$clone")
392
+
393
+ assert_contains "$out" "theta: skipped: no origin remote" "no-origin clone is skipped as before"
394
+ assert_not_contains "$out" "STUCK" "no-origin skip is not escalated to STUCK"
395
+ pass "no-origin clone is skipped (benign), not flagged STUCK"
396
+ }
397
+
398
+ test_local_only_skipped() {
399
+ local home clone out
400
+ home=$(new_home)
401
+ clone=$(build_pair "$home" iota)
402
+ advance_origin "$home" iota C1
403
+ mkdir -p "$home/data"
404
+ printf -- '- iota [local-only] - test project (added 2026-06-27)\n' > "$home/data/projects.md"
405
+
406
+ out=$(run_sync "$home" "$clone")
407
+
408
+ assert_contains "$out" "iota: skipped: local-only project" "local-only clone is skipped as before"
409
+ assert_not_contains "$out" "STUCK" "local-only skip is not escalated to STUCK"
410
+ pass "local-only clone is skipped (benign), not flagged STUCK"
411
+ }
412
+
413
+ test_single_project_by_bare_name_resolves() {
414
+ local home out
415
+ home=$(new_home)
416
+ build_pair "$home" kappa >/dev/null
417
+ advance_origin "$home" kappa C1
418
+
419
+ out=$(run_sync "$home" "kappa")
420
+
421
+ assert_contains "$out" "kappa: synced" "bare project name resolves against the home's projects dir"
422
+ pass "single-project form accepts a bare project name"
423
+ }
424
+
425
+ test_single_project_by_bare_name_ignores_cwd_shadow() {
426
+ local home cwd out
427
+ home=$(new_home)
428
+ build_pair "$home" mu >/dev/null
429
+ advance_origin "$home" mu C1
430
+ cwd="$home/shadow"
431
+ mkdir -p "$cwd/mu"
432
+
433
+ out=$(cd "$cwd" && run_sync "$home" "mu")
434
+
435
+ assert_contains "$out" "mu: synced" "bare project name prefers the home's projects dir"
436
+ assert_not_contains "$out" "skipped: not a git repo" "bare project name ignores a cwd shadow directory"
437
+ pass "single-project bare name resolution is not cwd-sensitive"
438
+ }
439
+
440
+ test_single_project_by_projects_relative_name_resolves() {
441
+ local home out
442
+ home=$(new_home)
443
+ build_pair "$home" lambda >/dev/null
444
+ advance_origin "$home" lambda C1
445
+
446
+ out=$(run_sync "$home" "projects/lambda")
447
+
448
+ assert_contains "$out" "lambda: synced" "projects/<name> form resolves against the home's projects dir"
449
+ pass "single-project form accepts a projects/<name> relative name"
450
+ }
451
+
452
+ test_single_project_by_projects_relative_name_ignores_cwd_shadow() {
453
+ local home cwd out
454
+ home=$(new_home)
455
+ build_pair "$home" nu >/dev/null
456
+ advance_origin "$home" nu C1
457
+ cwd="$home/shadow"
458
+ mkdir -p "$cwd/projects/nu"
459
+
460
+ out=$(cd "$cwd" && run_sync "$home" "projects/nu")
461
+
462
+ assert_contains "$out" "nu: synced" "projects/<name> form prefers the home's projects dir"
463
+ assert_not_contains "$out" "skipped: not a git repo" "projects/<name> form ignores a cwd shadow directory"
464
+ pass "single-project projects/<name> resolution is not cwd-sensitive"
465
+ }
466
+
467
+ test_single_project_unresolvable_name_still_skips() {
468
+ local home out
469
+ home=$(new_home)
470
+
471
+ out=$(run_sync "$home" "does-not-exist")
472
+
473
+ assert_contains "$out" "skipped: not a directory" "an unresolvable name still hits the existing not-a-directory skip"
474
+ pass "single-project form leaves a genuinely bad name unresolved"
475
+ }
476
+
477
+ test_whole_fleet_form() {
478
+ local home behind current out
479
+ home=$(new_home)
480
+ behind=$(build_pair "$home" fleet-behind)
481
+ advance_origin "$home" fleet-behind C1
482
+ current=$(build_pair "$home" fleet-current)
483
+
484
+ # Whole-fleet form: no project-dir argument.
485
+ out=$(run_sync "$home")
486
+
487
+ assert_contains "$out" "fleet-behind: synced" "whole-fleet form syncs a behind clone"
488
+ assert_contains "$out" "fleet-current: already current" "whole-fleet form reports a current clone"
489
+ : "$behind $current"
490
+ pass "whole-fleet form processes every clone under projects/"
491
+ }
492
+
493
+ test_bootstrap_relays_recovered_and_stuck() {
494
+ local home stuck rec out
495
+ home=$(new_home)
496
+ # A clone we will leave STUCK (dirty), and one that self-heals (detached-clean-ancestor).
497
+ stuck=$(build_pair "$home" stuck-clone)
498
+ advance_origin "$home" stuck-clone C1
499
+ printf 'dirty\n' >> "$stuck/file.txt"
500
+ rec=$(build_pair "$home" rec-clone)
501
+ advance_origin "$home" rec-clone C1
502
+ git -C "$rec" checkout --detach --quiet
503
+
504
+ # Full bootstrap: no state/ dir -> secondmate sync no-ops; no .env -> X mode off.
505
+ # We only assert the fleet-sync relay lines; other detect lines are irrelevant.
506
+ out=$(FM_HOME="$home" FM_ROOT_OVERRIDE="$ROOT" "$ROOT/bin/fm-bootstrap.sh" 2>/dev/null)
507
+
508
+ assert_contains "$out" "FLEET_SYNC: stuck-clone: STUCK:" "bootstrap relays the STUCK outcome"
509
+ assert_contains "$out" "FLEET_SYNC: rec-clone: recovered:" "bootstrap relays the recovered outcome"
510
+ pass "bootstrap relays recovered: and STUCK: fleet-sync outcomes"
511
+ }
512
+
513
+ # --- packed-refs.lock guard tests -------------------------------------------
514
+
515
+ test_orphaned_stale_packed_refs_lock_recovers() {
516
+ local home fakebin clone out err
517
+ home=$(new_home)
518
+ fakebin="$home/fb-lockstale"; rm -rf "$fakebin"; mkdir -p "$fakebin"
519
+ clone=$(build_packed_prunable "$home" lockstale)
520
+ plant_packed_refs_lock "$clone"
521
+ lsof_no_holder "$fakebin" # provably no live holder
522
+ out="$home/out-lockstale"; err="$home/err-lockstale"
523
+
524
+ set +e
525
+ FM_FLEET_SYNC_PACKED_REFS_LOCK_RETRIES=2 \
526
+ FM_FLEET_SYNC_PACKED_REFS_LOCK_RETRY_WAIT_SECS=0 \
527
+ FM_FLEET_SYNC_PACKED_REFS_LOCK_AGE_SECS=0 \
528
+ run_sync_guarded "$home" "$fakebin" "$out" "$err" lockstale
529
+ set -e
530
+
531
+ assert_grep "removed provably-stale packed-refs lock" "$err" \
532
+ "stale lock: guard did not force-remove the provably-stale lock"
533
+ assert_grep "fetch succeeded after stale packed-refs lock cleanup" "$err" \
534
+ "stale lock: fetch did not succeed after cleanup"
535
+ assert_contains "$(cat "$out")" "lockstale: synced" "stale lock: clone did not sync after recovery"
536
+ assert_grep "recovered: removed a stale packed-refs lock" "$out" \
537
+ "stale lock: recovery summary not emitted on stdout (bootstrap relays stdout, discards stderr)"
538
+ assert_absent "$clone/.git/packed-refs.lock" "stale lock: lock should be gone after removal"
539
+ [ "$(git -C "$clone" rev-parse HEAD)" = "$(git -C "$clone" rev-parse origin/main)" ] \
540
+ || fail "stale lock: clone HEAD not at origin/main after recovery"
541
+ pass "orphaned provably-stale packed-refs.lock is cleared and the clone syncs"
542
+ }
543
+
544
+ test_live_packed_refs_lock_is_never_removed() {
545
+ local home fakebin clone out err before
546
+ home=$(new_home)
547
+ fakebin="$home/fb-locklive"; rm -rf "$fakebin"; mkdir -p "$fakebin"
548
+ clone=$(build_packed_prunable "$home" locklive)
549
+ plant_packed_refs_lock "$clone"
550
+ lsof_live_holder "$fakebin" # a live process holds the lock/.git open
551
+ before=$(head_sha "$clone")
552
+ out="$home/out-locklive"; err="$home/err-locklive"
553
+
554
+ set +e
555
+ FM_FLEET_SYNC_PACKED_REFS_LOCK_RETRIES=2 \
556
+ FM_FLEET_SYNC_PACKED_REFS_LOCK_RETRY_WAIT_SECS=0 \
557
+ FM_FLEET_SYNC_PACKED_REFS_LOCK_AGE_SECS=0 \
558
+ run_sync_guarded "$home" "$fakebin" "$out" "$err" locklive
559
+ set -e
560
+
561
+ assert_grep "is not provably stale" "$err" "live lock: guard did not explain the refusal"
562
+ assert_no_grep "removed provably-stale packed-refs lock" "$err" \
563
+ "live lock: guard force-removed a live lock"
564
+ assert_contains "$(cat "$out")" "locklive: skipped: fetch failed" "live lock: fleet-sync did not skip"
565
+ assert_present "$clone/.git/packed-refs.lock" "live lock: lock must never be removed"
566
+ [ "$(head_sha "$clone")" = "$before" ] || fail "live lock: clone was advanced despite the refusal"
567
+ pass "a live packed-refs.lock is never removed and the sync fails loudly"
568
+ }
569
+
570
+ test_live_git_cwd_in_clone_dir_blocks_removal() {
571
+ local home fakebin clone out err before
572
+ home=$(new_home)
573
+ fakebin="$home/fb-lockcwd"; rm -rf "$fakebin"; mkdir -p "$fakebin"
574
+ clone=$(build_packed_prunable "$home" lockcwd)
575
+ plant_packed_refs_lock "$clone"
576
+ # Nobody holds the lock file, but a live process holds the clone worktree as its
577
+ # cwd - the narrow race where git closed packed-refs.lock but has not yet exited.
578
+ lsof_holds_only_live_dir "$fakebin"
579
+ before=$(head_sha "$clone")
580
+ out="$home/out-lockcwd"; err="$home/err-lockcwd"
581
+
582
+ set +e
583
+ FLEET_TEST_LIVE_DIR="$clone" \
584
+ FM_FLEET_SYNC_PACKED_REFS_LOCK_RETRIES=2 \
585
+ FM_FLEET_SYNC_PACKED_REFS_LOCK_RETRY_WAIT_SECS=0 \
586
+ FM_FLEET_SYNC_PACKED_REFS_LOCK_AGE_SECS=0 \
587
+ run_sync_guarded "$home" "$fakebin" "$out" "$err" lockcwd
588
+ set -e
589
+
590
+ assert_grep "is not provably stale" "$err" "clone-cwd holder: guard did not refuse"
591
+ assert_no_grep "removed provably-stale packed-refs lock" "$err" \
592
+ "clone-cwd holder: guard removed a lock while a live process held the clone dir"
593
+ assert_present "$clone/.git/packed-refs.lock" "clone-cwd holder: lock must not be removed"
594
+ [ "$(head_sha "$clone")" = "$before" ] || fail "clone-cwd holder: clone was advanced despite the refusal"
595
+ pass "a live process holding the clone worktree dir blocks lock removal (clone-dir liveness)"
596
+ }
597
+
598
+ test_transient_packed_refs_lock_self_clears() {
599
+ local home fakebin clone out err counter
600
+ home=$(new_home)
601
+ fakebin="$home/fb-locktrans"; rm -rf "$fakebin"; mkdir -p "$fakebin"
602
+ clone=$(build_packed_prunable "$home" locktrans)
603
+ plant_packed_refs_lock "$clone"
604
+ git_transient_packed_refs_lock "$fakebin" # fail once + drop lock, then real git
605
+ counter="$home/git-fetch-count"; : > "$counter"
606
+ out="$home/out-locktrans"; err="$home/err-locktrans"
607
+
608
+ set +e
609
+ GIT_FETCH_COUNTER="$counter" \
610
+ FM_FLEET_SYNC_PACKED_REFS_LOCK_RETRIES=3 \
611
+ FM_FLEET_SYNC_PACKED_REFS_LOCK_RETRY_WAIT_SECS=0 \
612
+ run_sync_guarded "$home" "$fakebin" "$out" "$err" locktrans
613
+ set -e
614
+
615
+ assert_grep "cleared on its own" "$err" "transient lock: guard did not report the self-clear"
616
+ assert_no_grep "removed provably-stale packed-refs lock" "$err" \
617
+ "transient lock: guard force-removed a lock that only needed patience"
618
+ assert_contains "$(cat "$out")" "locktrans: synced" "transient lock: clone did not sync after self-clear"
619
+ assert_grep "recovered: packed-refs lock cleared on its own" "$out" \
620
+ "transient lock: recovery summary not emitted on stdout"
621
+ assert_absent "$clone/.git/packed-refs.lock" "transient lock: lock should be gone after self-clear"
622
+ pass "a transient packed-refs.lock that self-clears is retried without a force-remove"
623
+ }
624
+
625
+ test_non_clone_dir_never_syncs_the_enclosing_repo() {
626
+ local home before out after
627
+ home=$(build_enclosing_home nonclone)
628
+ # A worktree container, not a clone: the repo is one level BELOW it.
629
+ mkdir -p "$home/projects/not-a-clone/wt"
630
+ before=$(head_sha "$home")
631
+
632
+ out=$(run_sync "$home")
633
+ after=$(head_sha "$home")
634
+
635
+ assert_contains "$out" "not-a-clone: skipped: not a clone root" \
636
+ "a non-repo directory under projects/ must be skipped by name"
637
+ assert_not_contains "$out" "not-a-clone: synced" \
638
+ "a non-repo directory must never be reported as a synced project"
639
+ [ "$before" = "$after" ] || \
640
+ fail "fleet-sync fast-forwarded the enclosing repo ($before -> $after) under a project's label"
641
+ pass "a non-repo directory under projects/ never fast-forwards the enclosing repo"
642
+ }
643
+
644
+ test_non_clone_dir_named_directly_never_syncs_the_enclosing_repo() {
645
+ local home before out after
646
+ home=$(build_enclosing_home nonclonedirect)
647
+ mkdir -p "$home/projects/not-a-clone"
648
+ before=$(head_sha "$home")
649
+
650
+ out=$(run_sync "$home" not-a-clone)
651
+ after=$(head_sha "$home")
652
+
653
+ assert_contains "$out" "not-a-clone: skipped: not a clone root" \
654
+ "the single-project form must apply the same clone-root guard"
655
+ [ "$before" = "$after" ] || \
656
+ fail "the single-project form fast-forwarded the enclosing repo ($before -> $after)"
657
+ pass "the single-project form also refuses a directory that is not its own clone root"
658
+ }
659
+
660
+ test_symlinked_clone_still_syncs() {
661
+ local home clone out
662
+ home=$(new_home)
663
+ clone=$(build_pair "$home" sigma)
664
+ advance_origin "$home" sigma C1
665
+ # A symlinked clone dir is a real clone root; the guard compares resolved paths,
666
+ # so it must not be mistaken for a directory nested in someone else's repo.
667
+ mv "$clone" "$home/real-sigma"
668
+ ln -s "$home/real-sigma" "$clone"
669
+
670
+ out=$(run_sync "$home")
671
+
672
+ assert_contains "$out" "sigma: synced" "a symlinked clone must still fast-forward"
673
+ pass "the clone-root guard accepts a symlinked clone directory"
674
+ }
675
+
676
+ test_non_signature_fetch_failure_is_not_retried() {
677
+ local home fakebin clone out err
678
+ home=$(new_home)
679
+ fakebin="$home/fb-locknonsig"; rm -rf "$fakebin"; mkdir -p "$fakebin"
680
+ clone=$(build_pair "$home" locknonsig)
681
+ advance_origin "$home" locknonsig C1
682
+ git -C "$clone" remote set-url origin "file://$home/remotes/does-not-exist.git"
683
+ out="$home/out-locknonsig"; err="$home/err-locknonsig"
684
+
685
+ set +e
686
+ FM_FLEET_SYNC_PACKED_REFS_LOCK_RETRIES=3 \
687
+ FM_FLEET_SYNC_PACKED_REFS_LOCK_RETRY_WAIT_SECS=0 \
688
+ run_sync_guarded "$home" "$fakebin" "$out" "$err" locknonsig
689
+ set -e
690
+
691
+ assert_contains "$(cat "$out")" "locknonsig: skipped: fetch failed" "non-signature: fleet-sync did not report the fetch failure"
692
+ assert_no_grep "waiting" "$err" "non-signature: a non-lock failure was wrongly retried"
693
+ assert_no_grep "packed-refs lock" "$err" "non-signature: a non-lock failure entered the lock guard"
694
+ pass "a non-packed-refs.lock fetch failure keeps today's behavior (no retry)"
695
+ }
696
+
697
+ test_detached_clean_ancestor_recovers
698
+ test_detached_unique_commit_is_stuck_untouched
699
+ test_detached_clean_ancestor_with_diverged_local_default_is_stuck_untouched
700
+ test_dirty_is_stuck_untouched
701
+ test_non_default_branch_is_stuck_untouched
702
+ test_diverged_is_stuck_untouched
703
+ test_on_default_clean_behind_fast_forwards
704
+ test_already_current_unchanged
705
+ test_no_origin_skipped
706
+ test_local_only_skipped
707
+ test_single_project_by_bare_name_resolves
708
+ test_single_project_by_bare_name_ignores_cwd_shadow
709
+ test_single_project_by_projects_relative_name_resolves
710
+ test_single_project_by_projects_relative_name_ignores_cwd_shadow
711
+ test_single_project_unresolvable_name_still_skips
712
+ test_whole_fleet_form
713
+ test_bootstrap_relays_recovered_and_stuck
714
+ test_orphaned_stale_packed_refs_lock_recovers
715
+ test_live_packed_refs_lock_is_never_removed
716
+ test_live_git_cwd_in_clone_dir_blocks_removal
717
+ test_transient_packed_refs_lock_self_clears
718
+ test_non_signature_fetch_failure_is_not_retried
719
+ test_non_clone_dir_never_syncs_the_enclosing_repo
720
+ test_non_clone_dir_named_directly_never_syncs_the_enclosing_repo
721
+ test_symlinked_clone_still_syncs