@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,716 @@
1
+ #!/usr/bin/env bash
2
+ # Ghost-text robustness (incident composer-robust; task afk-herdr-false-pending).
3
+ #
4
+ # A harness fills an otherwise-empty composer with de-emphasised ghost text that a
5
+ # plain pane capture cannot tell apart from human input, so the composer reader
6
+ # saw an idle pane as holding pending input. Two rendering styles are covered by
7
+ # the one shared ANSI-aware owner (fm_composer_strip_ghost, bin/fm-composer-lib.sh,
8
+ # reached here through the fm_tmux_strip_ghost thin adapter):
9
+ # - DIM/FAINT (SGR 2): claude's rotating prompt suggestion, codex's idle tip.
10
+ # - a dark/muted TRUECOLOR foreground: grok's placeholder/hint text.
11
+ # These tests pin:
12
+ # 1. fm_tmux_strip_ghost drops dim/faint AND dark-truecolor runs, keeping
13
+ # normal-intensity, brightly-coloured text.
14
+ # 2. fm_pane_input_pending reads a ghost-only composer (either style) as NOT
15
+ # pending, while still treating real (normal/bright) text as pending.
16
+ # 3. The tmux reader structurally scans every row of a multi-row composer.
17
+ # 4. The human/LLM-facing capture path (fm-peek.sh) stays PLAIN - no escape codes
18
+ # ever reach firstmate's context.
19
+ set -u
20
+
21
+ # shellcheck source=tests/lib.sh
22
+ . "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
23
+
24
+ LIB="$ROOT/bin/fm-tmux-lib.sh"
25
+ PEEK="$ROOT/bin/fm-peek.sh"
26
+
27
+ # shellcheck source=/dev/null
28
+ . "$LIB"
29
+
30
+ TMP_ROOT=$(fm_test_tmproot fm-ghost-tests)
31
+
32
+ # ESC byte for building styled fixtures and asserting escape-free output.
33
+ ESC=$(printf '\033')
34
+
35
+ # A fake tmux that serves a styled composer line for the dim-aware reader and an
36
+ # escape-free line for the plain (peek) path. capture-pane returns the styled
37
+ # fixture verbatim WITH -e (mirrors `tmux capture-pane -e`), and the same content
38
+ # with SGR sequences stripped WITHOUT -e (mirrors a plain capture). cursor_y comes
39
+ # from FM_FAKE_CY. The fake deliberately returns the complete fixture for every
40
+ # capture, which exercises the structural scan while preserving the historical
41
+ # single-row fallback fixtures.
42
+ make_fake_tmux() { # <dir>
43
+ local dir=$1 fb="$1/fakebin"
44
+ mkdir -p "$fb"
45
+ cat > "$fb/tmux" <<'SH'
46
+ #!/usr/bin/env bash
47
+ set -u
48
+ case "${1:-}" in
49
+ display-message)
50
+ for a in "$@"; do case "$a" in *cursor_y*) printf '%s\n' "${FM_FAKE_CY:-0}"; exit 0 ;; esac; done
51
+ printf 'fakepane\n'; exit 0 ;;
52
+ capture-pane)
53
+ has_e=0
54
+ start= end= prev=
55
+ for a in "$@"; do
56
+ [ "$a" = "-e" ] && has_e=1
57
+ case "$prev" in
58
+ -S) start=$a ;;
59
+ -E) end=$a ;;
60
+ esac
61
+ prev=$a
62
+ done
63
+ f="${FM_FAKE_STYLED:-/dev/null}"
64
+ if [ -n "${FM_FAKE_ROW:-}" ] \
65
+ && [ "$start" = "${FM_FAKE_CY:-0}" ] \
66
+ && [ "$end" = "${FM_FAKE_CY:-0}" ]; then
67
+ f=$FM_FAKE_ROW
68
+ fi
69
+ if [ "$has_e" = 1 ]; then
70
+ cat "$f" 2>/dev/null
71
+ else
72
+ # Plain capture: drop SGR sequences, as real `tmux capture-pane -p` does.
73
+ LC_ALL=C awk '{gsub(/\033\[[0-9;]*m/, ""); print}' "$f" 2>/dev/null
74
+ fi
75
+ exit 0 ;;
76
+ list-windows) exit 0 ;;
77
+ esac
78
+ exit 1
79
+ SH
80
+ chmod +x "$fb/tmux"
81
+ printf '%s\n' "$fb"
82
+ }
83
+
84
+ # --- fm_tmux_strip_ghost (pure) ---------------------------------------------
85
+
86
+ test_strip_ghost_drops_dim_keeps_normal() {
87
+ local out
88
+ # Dim run between ESC[2m and ESC[0m is dropped; the prompt glyph survives.
89
+ out=$(printf '\xe2\x9d\xaf \033[2mWhat is the largest country by area?\033[0m\n' | fm_tmux_strip_ghost)
90
+ [ "$out" = "$(printf '\xe2\x9d\xaf ')" ] || fail "dim run not dropped: '$out'"
91
+ # Normal-intensity text is kept verbatim (no styling at all).
92
+ out=$(printf '\xe2\x9d\xaf real human text\n' | fm_tmux_strip_ghost)
93
+ [ "$out" = "$(printf '\xe2\x9d\xaf real human text')" ] || fail "normal text changed: '$out'"
94
+ # Bold (SGR 1) is normal-intensity, NOT dim - must be kept.
95
+ out=$(printf '\033[1mbold typed\033[0m\n' | fm_tmux_strip_ghost)
96
+ [ "$out" = "bold typed" ] || fail "bold text wrongly dropped: '$out'"
97
+ pass "fm_tmux_strip_ghost drops dim/faint runs, keeps normal and bold text"
98
+ }
99
+
100
+ test_strip_ghost_handles_combined_and_boundary_codes() {
101
+ local out
102
+ # Dim combined with a color in one sequence (ESC[2;37m) is still a dim run.
103
+ out=$(printf '\xe2\x9d\xaf \033[2;37mpredicted\033[0m\n' | fm_tmux_strip_ghost)
104
+ [ "$out" = "$(printf '\xe2\x9d\xaf ')" ] || fail "combined dim+color not dropped: '$out'"
105
+ # ESC[22m (normal intensity) ends a dim run mid-line; the tail is kept.
106
+ out=$(printf '\033[2mghost\033[22mREALTAIL\n' | fm_tmux_strip_ghost)
107
+ [ "$out" = "REALTAIL" ] || fail "ESC[22m did not end the dim run: '$out'"
108
+ # ESC[0;2m (reset then dim) reads as dim (left-to-right within the sequence).
109
+ out=$(printf 'keep\033[0;2mdrop\033[0m\n' | fm_tmux_strip_ghost)
110
+ [ "$out" = "keep" ] || fail "reset-then-dim not treated as dim: '$out'"
111
+ pass "fm_tmux_strip_ghost handles combined SGR, ESC[22m, and reset-then-dim"
112
+ }
113
+
114
+ test_strip_ghost_keeps_colored_text_with_2_payloads() {
115
+ local out
116
+ # These pin that the awk's truecolor/256-color `2` payload SELECTOR is not
117
+ # mistaken for the SGR-2 dim attribute. The truecolor foregrounds use a BRIGHT
118
+ # colour (grok's real-input RGB 224,222,244, luminance ~225), because a DARK
119
+ # truecolor foreground is now itself a ghost signal (grok's placeholder) and is
120
+ # covered by test_strip_ghost_drops_dark_truecolor_ghost below.
121
+ out=$(printf '\033[38;5;2mgreen typed\033[0m\n' | fm_tmux_strip_ghost)
122
+ [ "$out" = "green typed" ] || fail "8-bit color payload 2 was treated as dim: '$out'"
123
+ out=$(printf '\033[38;2;224;222;244mtruecolor typed\033[0m\n' | fm_tmux_strip_ghost)
124
+ [ "$out" = "truecolor typed" ] || fail "bright truecolor payload 2 was treated as dim/ghost: '$out'"
125
+ out=$(printf '\033[48;2;4;5;6mbackground typed\033[0m\n' | fm_tmux_strip_ghost)
126
+ [ "$out" = "background typed" ] || fail "background truecolor payload was treated as dim: '$out'"
127
+ out=$(printf '\033[58;5;2munderline-color typed\033[0m\n' | fm_tmux_strip_ghost)
128
+ [ "$out" = "underline-color typed" ] || fail "underline color payload 2 was treated as dim: '$out'"
129
+ out=$(printf '\033[38:2::224:222:244mcolon truecolor typed\033[0m\n' | fm_tmux_strip_ghost)
130
+ [ "$out" = "colon truecolor typed" ] || fail "bright colon truecolor payload 2 was treated as dim/ghost: '$out'"
131
+ out=$(printf '\033[58::5::2mcolon underline typed\033[0m\n' | fm_tmux_strip_ghost)
132
+ [ "$out" = "colon underline typed" ] || fail "colon underline SGR leaked or dimmed text: '$out'"
133
+ out=$(printf '\033[4:2mnot dim underline\033[0m\n' | fm_tmux_strip_ghost)
134
+ [ "$out" = "not dim underline" ] || fail "colon subparameter 2 was treated as dim: '$out'"
135
+ pass "fm_tmux_strip_ghost keeps bright colored text with 2 payloads"
136
+ }
137
+
138
+ # --- Dark truecolor foreground is ghost (grok placeholder), dropped ----------
139
+
140
+ test_strip_ghost_drops_dark_truecolor_ghost() {
141
+ local out
142
+ # grok renders its placeholder/hint text with a dark, muted truecolor
143
+ # foreground (empirically 38;2;50;47;70 .. 38;2;110;106;134, luminance ~51..110,
144
+ # verified live against grok 0.2.93; the pristine "Type a message..." placeholder
145
+ # was this shape in grok 0.2.82). The shared owner drops it while keeping the
146
+ # bright prompt glyph, so an idle grok composer never reads as pending.
147
+ out=$(printf '\xe2\x9d\xaf \033[38;2;50;47;70mType a message...\033[0m\n' | fm_tmux_strip_ghost)
148
+ [ "$out" = "$(printf '\xe2\x9d\xaf ')" ] || fail "dark truecolor ghost not dropped: '$out'"
149
+ out=$(printf '\033[38;2;110;106;134mplaceholder hint text\033[39m\n' | fm_tmux_strip_ghost)
150
+ [ -z "$out" ] || fail "dark truecolor hint not dropped: '$out'"
151
+ # The colon form drops too.
152
+ out=$(printf '\xe2\x9d\xaf \033[38:2::86:82:110mmuted\033[0m\n' | fm_tmux_strip_ghost)
153
+ [ "$out" = "$(printf '\xe2\x9d\xaf ')" ] || fail "dark colon-truecolor ghost not dropped: '$out'"
154
+ pass "fm_tmux_strip_ghost drops a dark/muted truecolor foreground (grok placeholder)"
155
+ }
156
+
157
+ # --- muse's composer sits closest to the ghost threshold ---------------------
158
+
159
+ # These are muse 0.1.0-R708.1's real captured composer rows. Its prompt glyph
160
+ # `⟩` is truecolor 38;2;90;160;255 (luminance ~149.9) and its typed text is
161
+ # 38;2;204;211;219 (~209.8), so the glyph clears the 128 default by the
162
+ # narrowest margin in the fleet - roughly a fifth of grok's real-input margin.
163
+ # Both must survive stripping: dropping the glyph would empty an idle composer's
164
+ # plain row, and dropping the typed text would read a pending pane as empty and
165
+ # make it an injection target.
166
+ test_strip_ghost_keeps_muse_composer_colors() {
167
+ local out glyph
168
+ glyph=$(printf '\xe2\x9f\xa9')
169
+ out=$(printf '\033[0m\033[38;2;90;160;255m\xe2\x9f\xa9 \033[39m\n' | fm_tmux_strip_ghost)
170
+ [ "$out" = "$(printf '%s ' "$glyph")" ] \
171
+ || fail "muse's idle composer glyph was stripped as ghost text: '$out'"
172
+ # The submitted-prompt row carries a background colour too; an SGR 48 payload
173
+ # must not be luminance-tested as if it were the foreground.
174
+ out=$(printf '\033[38;2;90;160;255m\033[48;2;38;56;84m\xe2\x9f\xa9 \033[38;2;204;211;219mhello from firstmate\033[39m\n' | fm_tmux_strip_ghost)
175
+ [ "$out" = "$(printf '%s hello from firstmate' "$glyph")" ] \
176
+ || fail "muse's typed text or background-coloured glyph row was stripped: '$out'"
177
+ # The restored prompt muse puts back into the composer after an Escape
178
+ # interrupt is real bright text and must stay visible as pending input.
179
+ out=$(printf '\033[0m\033[38;2;90;160;255m\xe2\x9f\xa9 \033[38;2;204;211;219msecond turn to interrupt\033[39m\n' | fm_tmux_strip_ghost)
180
+ [ "$out" = "$(printf '%s second turn to interrupt' "$glyph")" ] \
181
+ || fail "muse's restored post-interrupt prompt was stripped as ghost text: '$out'"
182
+ pass "fm_tmux_strip_ghost keeps muse's near-threshold glyph and its typed text"
183
+ }
184
+
185
+ # --- fm_pane_input_pending: dim ghost is not pending ------------------------
186
+
187
+ test_dim_ghost_only_composer_is_not_pending() {
188
+ local dir fb capture
189
+ dir="$TMP_ROOT/ghost-only"; mkdir -p "$dir"
190
+ fb=$(make_fake_tmux "$dir")
191
+ capture="$dir/styled.txt"
192
+ # The exact rendering claude emits: a normal prompt glyph + a DIM predicted prompt.
193
+ printf '\xe2\x9d\xaf \033[2mWhat is the largest country by area?\033[0m\n' > "$capture"
194
+ if PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=0 \
195
+ fm_pane_input_pending "fakepane"; then
196
+ fail "dim ghost-only composer falsely read as pending"
197
+ fi
198
+ pass "fm_pane_input_pending: a dim ghost-only composer is NOT pending"
199
+ }
200
+
201
+ test_dim_ghost_inside_bordered_composer_is_not_pending() {
202
+ local dir fb capture
203
+ dir="$TMP_ROOT/ghost-bordered"; mkdir -p "$dir"
204
+ fb=$(make_fake_tmux "$dir")
205
+ capture="$dir/styled.txt"
206
+ # Bordered composer (claude box) holding only dim ghost text.
207
+ printf '╭─────────────────────────────────────╮\n│ \033[2mtry the other approach instead\033[0m │\n╰─────────────────────────────────────╯\n' > "$capture"
208
+ if PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
209
+ fm_pane_input_pending "fakepane"; then
210
+ fail "dim ghost in a bordered composer falsely read as pending"
211
+ fi
212
+ pass "fm_pane_input_pending: dim ghost inside a bordered composer is NOT pending"
213
+ }
214
+
215
+ test_normal_text_still_pending() {
216
+ local dir fb capture
217
+ dir="$TMP_ROOT/real-text"; mkdir -p "$dir"
218
+ fb=$(make_fake_tmux "$dir")
219
+ capture="$dir/styled.txt"
220
+ # Real human text, normal intensity - must still read as pending.
221
+ printf '\xe2\x9d\xaf fix findings 1 and 3, skip 2\n' > "$capture"
222
+ PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=0 \
223
+ fm_pane_input_pending "fakepane" \
224
+ || fail "real typed text was not detected as pending"
225
+ pass "fm_pane_input_pending: normal-intensity typed text is still pending"
226
+ }
227
+
228
+ test_colored_text_with_2_payload_still_pending() {
229
+ local dir fb capture
230
+ dir="$TMP_ROOT/colored-text"; mkdir -p "$dir"
231
+ fb=$(make_fake_tmux "$dir")
232
+ capture="$dir/styled.txt"
233
+ printf '\xe2\x9d\xaf \033[38;5;2mgreen typed\033[0m\n' > "$capture"
234
+ PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=0 \
235
+ fm_pane_input_pending "fakepane" \
236
+ || fail "8-bit colored typed text was not detected as pending"
237
+ printf '\xe2\x9d\xaf \033[38;2;224;222;244mtruecolor typed\033[0m\n' > "$capture"
238
+ PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=0 \
239
+ fm_pane_input_pending "fakepane" \
240
+ || fail "bright truecolor typed text was not detected as pending"
241
+ printf '\xe2\x9d\xaf \033[58;5;2munderline-color typed\033[0m\n' > "$capture"
242
+ PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=0 \
243
+ fm_pane_input_pending "fakepane" \
244
+ || fail "underline-colored typed text was not detected as pending"
245
+ printf '\xe2\x9d\xaf \033[58::5::2mcolon underline typed\033[0m\n' > "$capture"
246
+ PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=0 \
247
+ fm_pane_input_pending "fakepane" \
248
+ || fail "colon underline typed text was not detected as pending"
249
+ pass "fm_pane_input_pending: bright colored text with 2 payloads is still pending"
250
+ }
251
+
252
+ test_dark_truecolor_ghost_only_composer_is_not_pending() {
253
+ local dir fb capture
254
+ dir="$TMP_ROOT/grok-ghost"; mkdir -p "$dir"
255
+ fb=$(make_fake_tmux "$dir")
256
+ capture="$dir/styled.txt"
257
+ # A grok-style pristine composer: bright prompt glyph + a dark/muted truecolor
258
+ # placeholder. It must read NOT pending (the grok TRUECOLOR gap, now covered by
259
+ # the same ANSI-aware owner as claude's dim ghost).
260
+ printf '\xe2\x9d\xaf \033[38;2;50;47;70mType a message...\033[0m\n' > "$capture"
261
+ if PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=0 \
262
+ fm_pane_input_pending "fakepane"; then
263
+ fail "dark truecolor ghost-only composer falsely read as pending"
264
+ fi
265
+ pass "fm_pane_input_pending: a dark truecolor ghost-only composer (grok placeholder) is NOT pending"
266
+ }
267
+
268
+ test_dark_truecolor_bare_shell_prompt_is_unknown() {
269
+ local dir fb capture out prompt
270
+ dir="$TMP_ROOT/dark-shell-prompt"; mkdir -p "$dir"
271
+ fb=$(make_fake_tmux "$dir")
272
+ capture="$dir/styled.txt"
273
+ for prompt in '$' 'user@host $'; do
274
+ printf '\033[38;2;50;47;70m%s\033[0m\n' "$prompt" > "$capture"
275
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=0 \
276
+ fm_tmux_composer_state "fakepane")
277
+ [ "$out" = unknown ] \
278
+ || fail "dark truecolor bare shell prompt '$prompt' must read unknown, got '$out'"
279
+ done
280
+ pass "fm_tmux_composer_state: dark truecolor shell prompts read unknown"
281
+ }
282
+
283
+ test_real_text_with_trailing_ghost_is_pending() {
284
+ local dir fb capture
285
+ dir="$TMP_ROOT/mixed"; mkdir -p "$dir"
286
+ fb=$(make_fake_tmux "$dir")
287
+ capture="$dir/styled.txt"
288
+ # A human typed "deploy" and claude appended a dim ghost completion. The real
289
+ # text must win - the composer is pending.
290
+ printf '\xe2\x9d\xaf deploy\033[2m the staging environment now\033[0m\n' > "$capture"
291
+ PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=0 \
292
+ fm_pane_input_pending "fakepane" \
293
+ || fail "real text with a trailing ghost completion was not detected as pending"
294
+ pass "fm_pane_input_pending: real text plus a trailing ghost run is still pending"
295
+ }
296
+
297
+ # --- fm_tmux_composer_state: structural multi-row box scan ------------------
298
+
299
+ test_two_row_composer_reads_text_above_empty_cursor_row() {
300
+ local dir fb capture out
301
+ dir="$TMP_ROOT/two-row"; mkdir -p "$dir"
302
+ fb=$(make_fake_tmux "$dir")
303
+ capture="$dir/styled.txt"
304
+ cat > "$capture" <<'EOF'
305
+ ╭────────────────────────────────────────────────────╮
306
+ │ > Read the brief at /tmp/brief.md and follow it. │
307
+ │ │
308
+ ╰────────────────────────────────────────────────────╯
309
+ EOF
310
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=2 \
311
+ fm_tmux_composer_state "fakepane")
312
+ [ "$out" = pending ] \
313
+ || fail "two-row composer text above the empty cursor row should be pending, got '$out'"
314
+ pass "fm_tmux_composer_state: text above an empty cursor row is pending"
315
+ }
316
+
317
+ test_wrapped_composer_reads_all_content_rows() {
318
+ local dir fb capture out
319
+ dir="$TMP_ROOT/wrapped"; mkdir -p "$dir"
320
+ fb=$(make_fake_tmux "$dir")
321
+ capture="$dir/styled.txt"
322
+ cat > "$capture" <<'EOF'
323
+ ╭────────────────────────────────────────────╮
324
+ │ > This deliberately long instruction wraps │
325
+ │ across a second composer content row and │
326
+ │ across a third composer content row too. │
327
+ │ │
328
+ ╰────────────────────────────────────────────╯
329
+ EOF
330
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=4 \
331
+ fm_tmux_composer_state "fakepane")
332
+ [ "$out" = pending ] \
333
+ || fail "a three-row wrapped composer should be pending, got '$out'"
334
+ pass "fm_tmux_composer_state: a message wrapped across three rows is pending"
335
+ }
336
+
337
+ test_proven_box_bottom_border_cursor_classifies_content() {
338
+ local dir fb capture out
339
+ dir="$TMP_ROOT/bottom-border-ghost"; mkdir -p "$dir"
340
+ fb=$(make_fake_tmux "$dir")
341
+ capture="$dir/styled.txt"
342
+ printf '╭────────────────────────╮\n│ ❯ \033[38;2;50;47;70mType a message...\033[0m │\n╰──────── Grok 4.5 ──────╯\n' > "$capture"
343
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=2 \
344
+ fm_tmux_composer_state "fakepane")
345
+ [ "$out" = empty ] \
346
+ || fail "a cursor on a proven titled box bottom must classify its content, got '$out'"
347
+ pass "fm_tmux_composer_state: a proven titled box tolerates a bottom-border cursor"
348
+ }
349
+
350
+ test_pi_identity_requires_readable_busy_state() (
351
+ local out
352
+ # Keep the mocks in this subshell so they cannot affect later tests. Defining
353
+ # functions directly inside a command substitution does not parse in Bash 3.2.
354
+ # shellcheck disable=SC2329 # Mock invoked indirectly by the sourced adapter.
355
+ tmux() {
356
+ local arg
357
+ for arg in "$@"; do
358
+ case "$arg" in
359
+ *pane_tty*) printf '\n'; return 0 ;;
360
+ *pane_current_command*) printf 'pi\n'; return 0 ;;
361
+ esac
362
+ done
363
+ return 1
364
+ }
365
+ # shellcheck disable=SC2329 # Mock invoked indirectly by the sourced adapter.
366
+ fm_pane_busy_state() { printf 'unknown'; }
367
+ if out=$(fm_tmux_composer_identity fakepane); then
368
+ fail "a live Pi process with unreadable busy state must not produce identity, got '$out'"
369
+ fi
370
+ pass "fm_tmux_composer_identity: unknown busy state cannot become idle identity"
371
+ )
372
+
373
+ test_bordered_busy_signatures_are_pending() {
374
+ local dir fb capture out signature
375
+ dir="$TMP_ROOT/bordered-busy-signatures"; mkdir -p "$dir"
376
+ fb=$(make_fake_tmux "$dir")
377
+ capture="$dir/styled.txt"
378
+ for signature in 'Working...' 'Ctrl+c:cancel'; do
379
+ printf '╭────────────────────╮\n│ %-18s │\n╰────────────────────╯\n' "$signature" > "$capture"
380
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
381
+ fm_tmux_composer_state "fakepane")
382
+ [ "$out" = pending ] \
383
+ || fail "typed bordered busy signature '$signature' should be pending, got '$out'"
384
+ done
385
+ pass "fm_tmux_composer_state: typed Pi and Grok busy signatures inside a box are pending"
386
+ }
387
+
388
+ test_non_bordered_busy_footer_is_unknown_strict() {
389
+ # STRICT divergence (captain decision blank-row-injection-posture): a bare
390
+ # busy-footer row under the cursor is not a composer container, so it no
391
+ # longer reads `empty` the way the old allow-busy compatibility fallback
392
+ # did. Its one load-bearing consumer - submit confirmation on a harness
393
+ # whose mid-turn screen hides the composer (pi) - moved to the submit
394
+ # core's baseline-idle turn-started conversion (fm_tmux_submit_core), which
395
+ # requires an idle-to-busy transition across our own Enter instead of
396
+ # trusting any busy-looking row.
397
+ local dir fb capture out
398
+ dir="$TMP_ROOT/non-bordered-busy"; mkdir -p "$dir"
399
+ fb=$(make_fake_tmux "$dir")
400
+ capture="$dir/styled.txt"
401
+ printf 'Working...\n' > "$capture"
402
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=0 \
403
+ fm_tmux_composer_state "fakepane")
404
+ [ "$out" = unknown ] \
405
+ || fail "a non-bordered busy footer must read unknown under the strict rule, got '$out'"
406
+ pass "fm_tmux_composer_state: a bare busy-footer row reads unknown (strict container-proof rule)"
407
+ }
408
+
409
+ test_clipped_bordered_box_is_unknown() {
410
+ local dir fb capture out
411
+ dir="$TMP_ROOT/clipped-box"; mkdir -p "$dir"
412
+ fb=$(make_fake_tmux "$dir")
413
+ capture="$dir/styled.txt"
414
+ printf '╭────────────────────╮\n│ > │\n' > "$capture"
415
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
416
+ fm_tmux_composer_state "fakepane")
417
+ [ "$out" = unknown ] \
418
+ || fail "a bordered box with no readable bottom border should be unknown, got '$out'"
419
+ pass "fm_tmux_composer_state: an unbounded bordered box fails closed as unknown"
420
+ }
421
+
422
+ test_asymmetric_composer_edges_are_unknown() {
423
+ local dir fb capture out row
424
+ dir="$TMP_ROOT/asymmetric-box"; mkdir -p "$dir"
425
+ fb=$(make_fake_tmux "$dir")
426
+ capture="$dir/styled.txt"
427
+ for row in '│ > text' 'text │' '│ > text ┃' '──────' '+----' 'text +'; do
428
+ printf '%s\n' "$row" > "$capture"
429
+ out=$(PATH="$fb:$PATH" LC_ALL=C FM_FAKE_STYLED="$capture" FM_FAKE_CY=0 \
430
+ fm_tmux_composer_state "fakepane")
431
+ [ "$out" = unknown ] \
432
+ || fail "unbounded composer-edge row '$row' should be unknown, got '$out'"
433
+ done
434
+ pass "fm_tmux_composer_state: clipped and asymmetric composer edges fail closed"
435
+ }
436
+
437
+ test_mismatched_box_families_are_unknown() {
438
+ local dir fb capture out
439
+ dir="$TMP_ROOT/mismatched-box"; mkdir -p "$dir"
440
+ fb=$(make_fake_tmux "$dir")
441
+ capture="$dir/styled.txt"
442
+ printf '╭────────╮\n┃ > ┃\n╰────────╯\n' > "$capture"
443
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
444
+ fm_tmux_composer_state "fakepane")
445
+ [ "$out" = unknown ] \
446
+ || fail "a box with mismatched border families should be unknown, got '$out'"
447
+ pass "fm_tmux_composer_state: inconsistent box geometry fails closed"
448
+ }
449
+
450
+ test_misaligned_box_is_unknown() {
451
+ local dir fb capture out fixture
452
+ dir="$TMP_ROOT/misaligned-box"; mkdir -p "$dir"
453
+ fb=$(make_fake_tmux "$dir")
454
+ capture="$dir/styled.txt"
455
+ for fixture in offset width; do
456
+ case "$fixture" in
457
+ offset) printf ' ╭────╮\n │ │\n╰────╯\n' > "$capture" ;;
458
+ width) printf '╭────╮\n│ │\n╰────╯\n' > "$capture" ;;
459
+ esac
460
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
461
+ fm_tmux_composer_state "fakepane")
462
+ [ "$out" = unknown ] \
463
+ || fail "a box with inconsistent $fixture geometry should be unknown, got '$out'"
464
+ done
465
+ pass "fm_tmux_composer_state: misaligned box bounds fail closed"
466
+ }
467
+
468
+ test_unproved_empty_geometry_fails_closed() {
469
+ local dir fb capture out fixture expected
470
+ dir="$TMP_ROOT/unproved-empty-geometry"; mkdir -p "$dir"
471
+ fb=$(make_fake_tmux "$dir")
472
+ capture="$dir/styled.txt"
473
+ for fixture in ghost idle malformed-top; do
474
+ case "$fixture" in
475
+ ghost)
476
+ expected=unknown
477
+ printf '╭────────────╮\n│ \033[2mghost\033[0m │\n╰────────────╯\n' > "$capture"
478
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
479
+ fm_tmux_composer_state "fakepane")
480
+ ;;
481
+ idle)
482
+ expected=pending-unproven
483
+ printf '╭────────────╮\n│ idle hint │\n╰────────────╯\n' > "$capture"
484
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
485
+ FM_COMPOSER_IDLE_RE='^idle hint$' fm_tmux_composer_state "fakepane")
486
+ ;;
487
+ malformed-top)
488
+ expected=unknown
489
+ printf '╭────x───────╮\n│ │\n╰────────────╯\n' > "$capture"
490
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
491
+ fm_tmux_composer_state "fakepane")
492
+ ;;
493
+ esac
494
+ [ "$out" = "$expected" ] \
495
+ || fail "unproved geometry '$fixture' should be $expected, got '$out'"
496
+ done
497
+ pass "fm_tmux_composer_state: unproved ghost and malformed geometry stay unknown while styled placeholder-like text stays pending-unproven"
498
+ }
499
+
500
+ test_differing_widths_use_asymmetric_verdicts() {
501
+ local dir fb capture out
502
+ dir="$TMP_ROOT/differing-widths"; mkdir -p "$dir"
503
+ fb=$(make_fake_tmux "$dir")
504
+ capture="$dir/styled.txt"
505
+ printf '╭──────────╮\n│ > text │\n╰──────────╯\n' > "$capture"
506
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
507
+ fm_tmux_composer_state "fakepane")
508
+ [ "$out" = pending-unproven ] \
509
+ || fail "text in a differing-width box should be pending-unproven, got '$out'"
510
+ printf '╭──────────╮\n│ │\n╰──────────╯\n' > "$capture"
511
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
512
+ fm_tmux_composer_state "fakepane")
513
+ [ "$out" = unknown ] \
514
+ || fail "an empty differing-width box should be unknown, never empty, got '$out'"
515
+ pass "fm_tmux_composer_state: differing widths prefer pending or unknown, never empty"
516
+ }
517
+
518
+ test_wide_composer_text_is_pending() {
519
+ local dir fb capture out text
520
+ dir="$TMP_ROOT/wide-text"; mkdir -p "$dir"
521
+ fb=$(make_fake_tmux "$dir")
522
+ capture="$dir/styled.txt"
523
+ for text in '修复登录问题' 'fix the bug 🔧'; do
524
+ printf '╭────────────────────╮\n│ > %s │\n╰────────────────────╯\n' "$text" > "$capture"
525
+ out=$(PATH="$fb:$PATH" LC_ALL=C FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
526
+ fm_tmux_composer_state "fakepane")
527
+ [ "$out" = pending-unproven ] \
528
+ || fail "wide composer text '$text' should be pending-unproven, got '$out'"
529
+ done
530
+ pass "fm_tmux_composer_state: emoji and CJK text remain pending under the C locale"
531
+ }
532
+
533
+ test_all_tmux_harness_composers_share_classification() {
534
+ local dir fb capture out harness
535
+ dir="$TMP_ROOT/all-harness-composers"; mkdir -p "$dir"
536
+ fb=$(make_fake_tmux "$dir")
537
+ capture="$dir/styled.txt"
538
+ for harness in claude codex opencode pi pi-signed grok; do
539
+ case "$harness" in
540
+ claude) printf '╭────────────╮\n│ ❯ \033[2mtry\033[0m │\n╰────────────╯\n' > "$capture" ;;
541
+ codex) printf '╭────────────╮\n│ › \033[2mtip\033[0m │\n╰────────────╯\n' > "$capture" ;;
542
+ opencode) printf '╭────────────╮\n│ > │\n╰────────────╯\n' > "$capture" ;;
543
+ pi|pi-signed) printf '╭────────────╮\n│ │\n╰────────────╯\n' > "$capture" ;;
544
+ grok) printf '╭────────────╮\n│ ❯ \033[38;2;50;47;70mType\033[0m │\n╰────────────╯\n' > "$capture" ;;
545
+ esac
546
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
547
+ fm_tmux_composer_state "fakepane")
548
+ [ "$out" = empty ] \
549
+ || fail "$harness aligned idle composer should be empty, got '$out'"
550
+ case "$harness" in
551
+ claude|grok) printf '╭────────────╮\n│ ❯ fix │\n╰────────────╯\n' > "$capture" ;;
552
+ codex) printf '╭────────────╮\n│ › fix │\n╰────────────╯\n' > "$capture" ;;
553
+ opencode|pi|pi-signed) printf '╭────────────╮\n│ > fix │\n╰────────────╯\n' > "$capture" ;;
554
+ esac
555
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
556
+ fm_tmux_composer_state "fakepane")
557
+ [ "$out" = pending ] \
558
+ || fail "$harness composer with text should be pending, got '$out'"
559
+ done
560
+ pass "fm_tmux_composer_state: all tmux harnesses share empty and pending classification"
561
+ }
562
+
563
+ test_unrecognized_state_defers_input_guard() {
564
+ (
565
+ # shellcheck disable=SC2329
566
+ fm_tmux_composer_state() { printf 'future-state'; }
567
+ fm_pane_input_pending "fakepane"
568
+ ) || fail "an unrecognized composer state should defer the input guard"
569
+ pass "fm_pane_input_pending: unrecognized states defer by default"
570
+ }
571
+
572
+ test_single_capture_leaves_no_fallback_race() {
573
+ # The old reader captured twice (a full-pane scan, then a separate
574
+ # cursor-row band capture), so a pane redraw between the two could hand the
575
+ # verdict a row the scan never saw. The consolidated reader classifies ONE
576
+ # capture (bin/fm-composer-lib.sh, fm_composer_classify_screen), so the
577
+ # race is structurally gone: a divergent band-capture row (served via
578
+ # FM_FAKE_ROW, which only a band capture would read) must have no effect on
579
+ # the verdict.
580
+ local dir fb capture row_capture out
581
+ dir="$TMP_ROOT/fallback-race"; mkdir -p "$dir"
582
+ fb=$(make_fake_tmux "$dir")
583
+ capture="$dir/styled.txt"
584
+ row_capture="$dir/row.txt"
585
+ printf '› deploy staging\n' > "$capture"
586
+ printf '│ > │\n' > "$row_capture"
587
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_ROW="$row_capture" FM_FAKE_CY=0 \
588
+ fm_tmux_composer_state "fakepane")
589
+ [ "$out" = pending ] \
590
+ || fail "the verdict must come from the one full capture (agent glyph + typed text = pending), got '$out'"
591
+ pass "fm_tmux_composer_state: one capture feeds the classifier; no band-capture race remains"
592
+ }
593
+
594
+ test_absent_tmux_identity_keeps_enclosed_bare_verdict() {
595
+ local dir fb capture out nbsp
596
+ dir="$TMP_ROOT/absent-identity"; mkdir -p "$dir"
597
+ fb=$(make_fake_tmux "$dir")
598
+ capture="$dir/styled.txt"
599
+ nbsp=$(printf '\302\240')
600
+ printf '────────────────────────\n❯%s\n────────────────────────\n' "$nbsp" > "$capture"
601
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=1 \
602
+ fm_tmux_composer_state "fakepane")
603
+ [ "$out" = empty ] \
604
+ || fail "an enclosed Claude glyph must keep its bare empty verdict when the Pi-only probe is absent, got '$out'"
605
+ pass "fm_tmux_composer_state: absent Pi identity preserves Claude's enclosed bare verdict"
606
+ }
607
+
608
+ test_legitimate_empty_routes_remain_empty() {
609
+ local dir fb capture out fixture cursor
610
+ dir="$TMP_ROOT/legitimate-empty"; mkdir -p "$dir"
611
+ fb=$(make_fake_tmux "$dir")
612
+ capture="$dir/styled.txt"
613
+ # A blank pane is deliberately absent here: under the strict container-proof
614
+ # rule (captain decision blank-row-injection-posture) a blank cursor row is
615
+ # unknown, pinned by tests/fm-daemon.test.sh and tests/fm-composer-lib.test.sh.
616
+ for fixture in bordered double-bordered agent-prompt; do
617
+ case "$fixture" in
618
+ bordered) printf '╭────╮\n│ │\n╰────╯\n' > "$capture"; cursor=1 ;;
619
+ double-bordered) printf '╔════╗\n║ ║\n╚════╝\n' > "$capture"; cursor=1 ;;
620
+ agent-prompt) printf '›\n' > "$capture"; cursor=0 ;;
621
+ esac
622
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY="$cursor" \
623
+ fm_tmux_composer_state "fakepane")
624
+ [ "$out" = empty ] \
625
+ || fail "legitimate empty route '$fixture' should remain empty, got '$out'"
626
+ done
627
+ pass "fm_tmux_composer_state: only proven structural and non-bordered empty routes stay empty"
628
+ }
629
+
630
+ test_non_bordered_composer_uses_compatibility_fallback() {
631
+ local dir fb capture out
632
+ dir="$TMP_ROOT/non-bordered-fallback"; mkdir -p "$dir"
633
+ fb=$(make_fake_tmux "$dir")
634
+ capture="$dir/styled.txt"
635
+ printf '› deploy staging\n' > "$capture"
636
+ out=$(PATH="$fb:$PATH" FM_FAKE_STYLED="$capture" FM_FAKE_CY=0 \
637
+ fm_tmux_composer_state "fakepane")
638
+ [ "$out" = pending ] \
639
+ || fail "a non-bordered composer should retain cursor-row classification, got '$out'"
640
+ pass "fm_tmux_composer_state: panes without bordered structure retain compatibility fallback"
641
+ }
642
+
643
+ test_non_bordered_interior_edges_are_pending() {
644
+ local dir fb capture out row
645
+ dir="$TMP_ROOT/non-bordered-interior-edges"; mkdir -p "$dir"
646
+ fb=$(make_fake_tmux "$dir")
647
+ capture="$dir/styled.txt"
648
+ for row in '› cat file | grep x' '› explain │ this glyph'; do
649
+ printf '%s\n' "$row" > "$capture"
650
+ out=$(PATH="$fb:$PATH" LC_ALL=C FM_FAKE_STYLED="$capture" FM_FAKE_CY=0 \
651
+ fm_tmux_composer_state "fakepane")
652
+ [ "$out" = pending ] \
653
+ || fail "non-bordered interior edge row '$row' should be pending, got '$out'"
654
+ done
655
+ pass "fm_tmux_composer_state: interior edge glyphs retain non-bordered fallback"
656
+ }
657
+
658
+ # --- fm-peek.sh stays escape-free (LLM-facing path) -------------------------
659
+
660
+ test_peek_output_is_escape_free() {
661
+ local dir fb capture home out
662
+ dir="$TMP_ROOT/peek"; mkdir -p "$dir"
663
+ fb=$(make_fake_tmux "$dir")
664
+ capture="$dir/styled.txt"
665
+ # A pane full of styling, including dim ghost text. The plain peek path must
666
+ # surface NONE of these escape codes into firstmate's context.
667
+ printf 'normal output line\n\xe2\x9d\xaf \033[2mpredicted next prompt\033[0m\n' > "$capture"
668
+ # Empty FM_HOME so fm-guard.sh finds no in-flight task and stays silent.
669
+ home="$dir/home"; mkdir -p "$home/state"
670
+ # Pass an explicit session:window so resolution needs no metadata.
671
+ out=$(PATH="$fb:$PATH" FM_HOME="$home" FM_FAKE_STYLED="$capture" \
672
+ "$PEEK" "sess:win" 2>/dev/null)
673
+ case "$out" in
674
+ *"$ESC"*) fail "fm-peek surfaced ANSI escape codes into LLM-facing output" ;;
675
+ esac
676
+ # And it should still carry the real content.
677
+ case "$out" in
678
+ *"predicted next prompt"*) : ;;
679
+ *) fail "fm-peek dropped pane content (expected the ghost text body as plain text)" ;;
680
+ esac
681
+ pass "fm-peek output is escape-free (no raw -e bytes reach firstmate context)"
682
+ }
683
+
684
+ test_strip_ghost_drops_dim_keeps_normal
685
+ test_strip_ghost_handles_combined_and_boundary_codes
686
+ test_strip_ghost_keeps_colored_text_with_2_payloads
687
+ test_strip_ghost_drops_dark_truecolor_ghost
688
+ test_strip_ghost_keeps_muse_composer_colors
689
+ test_dim_ghost_only_composer_is_not_pending
690
+ test_dim_ghost_inside_bordered_composer_is_not_pending
691
+ test_normal_text_still_pending
692
+ test_colored_text_with_2_payload_still_pending
693
+ test_dark_truecolor_ghost_only_composer_is_not_pending
694
+ test_dark_truecolor_bare_shell_prompt_is_unknown
695
+ test_real_text_with_trailing_ghost_is_pending
696
+ test_two_row_composer_reads_text_above_empty_cursor_row
697
+ test_wrapped_composer_reads_all_content_rows
698
+ test_proven_box_bottom_border_cursor_classifies_content
699
+ test_pi_identity_requires_readable_busy_state
700
+ test_bordered_busy_signatures_are_pending
701
+ test_non_bordered_busy_footer_is_unknown_strict
702
+ test_clipped_bordered_box_is_unknown
703
+ test_asymmetric_composer_edges_are_unknown
704
+ test_mismatched_box_families_are_unknown
705
+ test_misaligned_box_is_unknown
706
+ test_unproved_empty_geometry_fails_closed
707
+ test_differing_widths_use_asymmetric_verdicts
708
+ test_wide_composer_text_is_pending
709
+ test_all_tmux_harness_composers_share_classification
710
+ test_unrecognized_state_defers_input_guard
711
+ test_single_capture_leaves_no_fallback_race
712
+ test_absent_tmux_identity_keeps_enclosed_bare_verdict
713
+ test_legitimate_empty_routes_remain_empty
714
+ test_non_bordered_composer_uses_compatibility_fallback
715
+ test_non_bordered_interior_edges_are_pending
716
+ test_peek_output_is_escape_free