@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.
- package/.agents/README.md +291 -0
- package/.agents/agents/bragi-marketer.md +111 -0
- package/.agents/agents/brokk.md +352 -0
- package/.agents/agents/forseti-reviewer.md +93 -0
- package/.agents/agents/frigg-consent.md +77 -0
- package/.agents/agents/groa-update.md +77 -0
- package/.agents/agents/hnoss-designer.md +69 -0
- package/.agents/agents/huginn-researcher.md +99 -0
- package/.agents/agents/jord-projects.md +78 -0
- package/.agents/agents/kvasir-scout.md +91 -0
- package/.agents/agents/mimir-planner.md +92 -0
- package/.agents/agents/muninn-stow.md +77 -0
- package/.agents/agents/saga-bearings.md +78 -0
- package/.agents/agents/sindri-developer.md +114 -0
- package/.agents/agents/snotra-documenter.md +105 -0
- package/.agents/agents/syn-recovery.md +78 -0
- package/.agents/agents/tyr-check.md +78 -0
- package/.agents/agents/urdh-hold.md +77 -0
- package/.agents/agents/volundr-smith.md +80 -0
- package/.agents/agents/vor-diagnostics.md +77 -0
- package/.agents/assets/agents/naming.md +50 -0
- package/.agents/assets/agents/registry.md +93 -0
- package/.agents/assets/agents/runtime.md +36 -0
- package/.agents/assets/agents/toon-tasks-cli.md +42 -0
- package/.agents/assets/templates/company_entity.template.md +37 -0
- package/.agents/backend/README.md +74 -0
- package/.agents/backend/backends/cmux.sh +649 -0
- package/.agents/backend/backends/herdr-eventwait.py +157 -0
- package/.agents/backend/backends/herdr-workspace-move.py +114 -0
- package/.agents/backend/backends/herdr.sh +3386 -0
- package/.agents/backend/backends/orca.sh +290 -0
- package/.agents/backend/backends/tmux.sh +357 -0
- package/.agents/backend/backends/zellij.sh +700 -0
- package/.agents/backend/fm-afk-launch.sh +654 -0
- package/.agents/backend/fm-afk-return.sh +241 -0
- package/.agents/backend/fm-afk-start.sh +172 -0
- package/.agents/backend/fm-arm-command-policy.mjs +960 -0
- package/.agents/backend/fm-arm-pretool-check.sh +200 -0
- package/.agents/backend/fm-backend-hometag-lib.sh +52 -0
- package/.agents/backend/fm-backend.sh +981 -0
- package/.agents/backend/fm-backlog-handoff.sh +909 -0
- package/.agents/backend/fm-backlog-receive.sh +187 -0
- package/.agents/backend/fm-backlog-transition-lib.sh +779 -0
- package/.agents/backend/fm-bearings-board.sh +204 -0
- package/.agents/backend/fm-bearings-snapshot.sh +546 -0
- package/.agents/backend/fm-bootstrap.sh +1568 -0
- package/.agents/backend/fm-branch-outcome.sh +203 -0
- package/.agents/backend/fm-branch-prompt.sh +101 -0
- package/.agents/backend/fm-brief.sh +452 -0
- package/.agents/backend/fm-busy-event.sh +234 -0
- package/.agents/backend/fm-busy-lib.sh +986 -0
- package/.agents/backend/fm-captain-hold.sh +1001 -0
- package/.agents/backend/fm-cd-command-policy.mjs +153 -0
- package/.agents/backend/fm-cd-pretool-check.sh +190 -0
- package/.agents/backend/fm-check-lib.sh +74 -0
- package/.agents/backend/fm-check-register.sh +42 -0
- package/.agents/backend/fm-check-unregister.sh +52 -0
- package/.agents/backend/fm-classify-lib.sh +1770 -0
- package/.agents/backend/fm-claude-stop-autoarm.sh +334 -0
- package/.agents/backend/fm-composer-lib.sh +1416 -0
- package/.agents/backend/fm-config-inherit-lib.sh +1202 -0
- package/.agents/backend/fm-config-push.sh +254 -0
- package/.agents/backend/fm-control-lib.sh +251 -0
- package/.agents/backend/fm-control.sh +878 -0
- package/.agents/backend/fm-crew-state.sh +625 -0
- package/.agents/backend/fm-cursor-lib.sh +243 -0
- package/.agents/backend/fm-decision-hold.sh +232 -0
- package/.agents/backend/fm-doc-audience-check.sh +269 -0
- package/.agents/backend/fm-dod-lib.sh +67 -0
- package/.agents/backend/fm-ensure-agents-md.sh +253 -0
- package/.agents/backend/fm-extension-launch-barrier.mjs +129 -0
- package/.agents/backend/fm-extension.mjs +2577 -0
- package/.agents/backend/fm-extension.sh +16 -0
- package/.agents/backend/fm-ff-lib.sh +420 -0
- package/.agents/backend/fm-fleet-snapshot.sh +1463 -0
- package/.agents/backend/fm-fleet-sync.sh +458 -0
- package/.agents/backend/fm-fleet-view.sh +96 -0
- package/.agents/backend/fm-gate-refuse-lib.sh +102 -0
- package/.agents/backend/fm-guard.sh +242 -0
- package/.agents/backend/fm-harness.sh +195 -0
- package/.agents/backend/fm-herdr-ci-cleanup.sh +115 -0
- package/.agents/backend/fm-herdr-lab.sh +346 -0
- package/.agents/backend/fm-herdr-session-cleanup.sh +337 -0
- package/.agents/backend/fm-home-seed.sh +984 -0
- package/.agents/backend/fm-home-summary-refresh.sh +255 -0
- package/.agents/backend/fm-hook-host-lib.sh +36 -0
- package/.agents/backend/fm-inactive-reconcile.sh +531 -0
- package/.agents/backend/fm-inbox.sh +399 -0
- package/.agents/backend/fm-install-actionlint.sh +84 -0
- package/.agents/backend/fm-install-herdr.sh +96 -0
- package/.agents/backend/fm-install-shellcheck.sh +84 -0
- package/.agents/backend/fm-install-treehouse.sh +95 -0
- package/.agents/backend/fm-kimi-turnend-hook.sh +276 -0
- package/.agents/backend/fm-lease-lib.sh +218 -0
- package/.agents/backend/fm-lease.sh +189 -0
- package/.agents/backend/fm-line-cap-lib.sh +51 -0
- package/.agents/backend/fm-lint-workflows.sh +137 -0
- package/.agents/backend/fm-lint.sh +591 -0
- package/.agents/backend/fm-lock-lib.sh +104 -0
- package/.agents/backend/fm-lock.sh +107 -0
- package/.agents/backend/fm-marker-lib.sh +12 -0
- package/.agents/backend/fm-merge-local.sh +74 -0
- package/.agents/backend/fm-merge-outcome-lib.sh +138 -0
- package/.agents/backend/fm-nm-run-lib.sh +124 -0
- package/.agents/backend/fm-on.sh +125 -0
- package/.agents/backend/fm-operational-input.sh +253 -0
- package/.agents/backend/fm-peek.sh +46 -0
- package/.agents/backend/fm-pending-reply-lib.sh +1409 -0
- package/.agents/backend/fm-pr-check.sh +135 -0
- package/.agents/backend/fm-pr-lib.sh +1016 -0
- package/.agents/backend/fm-pr-merge.sh +707 -0
- package/.agents/backend/fm-pr-poll.sh +110 -0
- package/.agents/backend/fm-primary-scope-lib.sh +33 -0
- package/.agents/backend/fm-procevent-extension-capture.pl +259 -0
- package/.agents/backend/fm-procevent-lavish.sh +628 -0
- package/.agents/backend/fm-procevent-lib.sh +892 -0
- package/.agents/backend/fm-procevent-quota.sh +290 -0
- package/.agents/backend/fm-procevent-remote-reply.sh +577 -0
- package/.agents/backend/fm-procevent-when.sh +504 -0
- package/.agents/backend/fm-procevent.sh +1466 -0
- package/.agents/backend/fm-project-mode.sh +92 -0
- package/.agents/backend/fm-project-origin-lib.sh +180 -0
- package/.agents/backend/fm-promote.sh +282 -0
- package/.agents/backend/fm-public-followup-emit.sh +260 -0
- package/.agents/backend/fm-public-followup-lib.sh +398 -0
- package/.agents/backend/fm-public-followup.sh +1330 -0
- package/.agents/backend/fm-push-transition-lib.sh +168 -0
- package/.agents/backend/fm-quota-axi-lib.sh +93 -0
- package/.agents/backend/fm-quota-choose.sh +384 -0
- package/.agents/backend/fm-remote-delta-read.sh +189 -0
- package/.agents/backend/fm-remote-doctor.sh +800 -0
- package/.agents/backend/fm-remote-entrypoint.sh +193 -0
- package/.agents/backend/fm-remote-file.sh +234 -0
- package/.agents/backend/fm-remote-home-provision.sh +259 -0
- package/.agents/backend/fm-remote-home-seed.sh +259 -0
- package/.agents/backend/fm-remote-inherit-push.sh +90 -0
- package/.agents/backend/fm-remote-inherit.sh +179 -0
- package/.agents/backend/fm-remote-job-lib.sh +1241 -0
- package/.agents/backend/fm-remote-job-reap-orphans.sh +137 -0
- package/.agents/backend/fm-remote-job-worker.sh +1124 -0
- package/.agents/backend/fm-remote-readiness-lib.sh +44 -0
- package/.agents/backend/fm-remote-secondmate-control.sh +340 -0
- package/.agents/backend/fm-review-diff.sh +158 -0
- package/.agents/backend/fm-secondmate-charter-lib.sh +44 -0
- package/.agents/backend/fm-secondmate-nudge-lib.sh +68 -0
- package/.agents/backend/fm-secondmate-parent-lib.sh +70 -0
- package/.agents/backend/fm-secondmate-reconcile.sh +371 -0
- package/.agents/backend/fm-secondmate-registry-lib.sh +311 -0
- package/.agents/backend/fm-secondmate-report.sh +88 -0
- package/.agents/backend/fm-send.sh +1059 -0
- package/.agents/backend/fm-session-lock-lib.sh +176 -0
- package/.agents/backend/fm-session-start.sh +970 -0
- package/.agents/backend/fm-sessionstart-cursor.sh +40 -0
- package/.agents/backend/fm-sessionstart-nudge.sh +45 -0
- package/.agents/backend/fm-sessionstart-run.sh +145 -0
- package/.agents/backend/fm-spawn.sh +3153 -0
- package/.agents/backend/fm-startup-memory-budget-lib.sh +224 -0
- package/.agents/backend/fm-startup-memory-budget.sh +94 -0
- package/.agents/backend/fm-startup-network.sh +642 -0
- package/.agents/backend/fm-stow-cascade.sh +251 -0
- package/.agents/backend/fm-subagent-pretool-check.sh +207 -0
- package/.agents/backend/fm-supervise-daemon.sh +1732 -0
- package/.agents/backend/fm-supervision-instructions.sh +215 -0
- package/.agents/backend/fm-supervision-lib.sh +90 -0
- package/.agents/backend/fm-supervisor-target-lib.sh +78 -0
- package/.agents/backend/fm-tangle-lib.sh +53 -0
- package/.agents/backend/fm-task-inbox-lib.sh +402 -0
- package/.agents/backend/fm-tasks-axi-lib.sh +123 -0
- package/.agents/backend/fm-teardown.sh +2922 -0
- package/.agents/backend/fm-test-isolation-proof.sh +588 -0
- package/.agents/backend/fm-test-run.sh +2191 -0
- package/.agents/backend/fm-timeout-lib.sh +141 -0
- package/.agents/backend/fm-timing-lib.sh +179 -0
- package/.agents/backend/fm-tmux-lib.sh +291 -0
- package/.agents/backend/fm-tool-update-check.sh +898 -0
- package/.agents/backend/fm-trace-context-lib.sh +227 -0
- package/.agents/backend/fm-transition-lib.sh +103 -0
- package/.agents/backend/fm-turnend-guard-cursor.sh +391 -0
- package/.agents/backend/fm-turnend-guard-grok.sh +90 -0
- package/.agents/backend/fm-turnend-guard.sh +432 -0
- package/.agents/backend/fm-update.sh +110 -0
- package/.agents/backend/fm-vendor-auth-probe.sh +190 -0
- package/.agents/backend/fm-voice-client.py +1373 -0
- package/.agents/backend/fm-voice-relay.py +1256 -0
- package/.agents/backend/fm-wake-drain.sh +592 -0
- package/.agents/backend/fm-wake-grant.sh +127 -0
- package/.agents/backend/fm-wake-lib.sh +1849 -0
- package/.agents/backend/fm-watch-arm.sh +602 -0
- package/.agents/backend/fm-watch-checkpoint.sh +109 -0
- package/.agents/backend/fm-watch.sh +1962 -0
- package/.agents/backend/fm-x-dismiss.sh +116 -0
- package/.agents/backend/fm-x-followup.sh +286 -0
- package/.agents/backend/fm-x-lib.sh +1000 -0
- package/.agents/backend/fm-x-link.sh +234 -0
- package/.agents/backend/fm-x-poll.sh +185 -0
- package/.agents/backend/fm-x-reply.sh +385 -0
- package/.agents/backend/fm_voice_frame.py +166 -0
- package/.agents/backend/fm_voice_records.py +574 -0
- package/.agents/backend/model-bridge.py +360 -0
- package/.agents/backend/opencode-go-bridge.py +30 -0
- package/.agents/bus/README.md +12 -0
- package/.agents/bus/messages.json +1 -0
- package/.agents/config/agents.machine.example.yaml +16 -0
- package/.agents/config/agents.yaml.example +72 -0
- package/.agents/config/crew-dispatch.json +22 -0
- package/.agents/config/cron.yaml +11 -0
- package/.agents/config/eindri-dispatch.json +67 -0
- package/.agents/config/eindri-harness +1 -0
- package/.agents/config/startup-memory-budget +1 -0
- package/.agents/filebrowser/.filebrowser.json +6 -0
- package/.agents/filebrowser/README.md +11 -0
- package/.agents/gateway/README.md +12 -0
- package/.agents/github/README.md +15 -0
- package/.agents/github/webhooks/.gitkeep +0 -0
- package/.agents/github/workflows/.gitkeep +0 -0
- package/.agents/harness/opencode/plugins/lib/rodd-operational-input.js +37 -0
- package/.agents/harness/opencode/plugins/package.json +4 -0
- package/.agents/harness/opencode/plugins/saga-sessionstart.js +77 -0
- package/.agents/harness/opencode/plugins/syn-cd-check.js +61 -0
- package/.agents/harness/opencode/plugins/syn-pretool-check.js +68 -0
- package/.agents/harness/opencode/plugins/syn-turnend-guard.js +105 -0
- package/.agents/harness/opencode/plugins/syn-watch-arm.js +521 -0
- package/.agents/memory/README.md +46 -0
- package/.agents/memory/runes_audit.md +5 -0
- package/.agents/migrations/0001-hodd-layout.sh +38 -0
- package/.agents/migrations/0002-a2a-mcp.sh +27 -0
- package/.agents/migrations/0003-private-data-separation.sh +121 -0
- package/.agents/migrations/0004-hoard-and-realms.sh +104 -0
- package/.agents/migrations/0005-roots-out-of-tree.sh +158 -0
- package/.agents/sandbox/Dockerfile.utgard +19 -0
- package/.agents/sandbox/README.md +14 -0
- package/.agents/sandbox/utgard.config.json +14 -0
- package/.agents/skills/README.md +59 -0
- package/.agents/skills/bragi-marketing/README.md +3 -0
- package/.agents/skills/bragi-marketing/SKILL.md +82 -0
- package/.agents/skills/eindri-homes/README.md +3 -0
- package/.agents/skills/eindri-homes/SKILL.md +291 -0
- package/.agents/skills/eindri-homes/assets/control-plane.md +122 -0
- package/.agents/skills/eindri-homes/assets/dispatch.md +80 -0
- package/.agents/skills/frigg-consent/README.md +3 -0
- package/.agents/skills/frigg-consent/SKILL.md +56 -0
- package/.agents/skills/galdr-ymirsystem/README.md +3 -0
- package/.agents/skills/galdr-ymirsystem/SKILL.md +159 -0
- package/.agents/skills/galdr-ymirsystem/assets/README.md +74 -0
- package/.agents/skills/galdr-ymirsystem/assets/brokk-distro-runtime.md +390 -0
- package/.agents/skills/galdr-ymirsystem/assets/build-method.md +113 -0
- package/.agents/skills/galdr-ymirsystem/assets/build-tool-categories.md +178 -0
- package/.agents/skills/galdr-ymirsystem/assets/eindri-orchestration.md +518 -0
- package/.agents/skills/galdr-ymirsystem/assets/eindri-profiles.md +136 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +736 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/claude-code.md +197 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/codex.md +191 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/cursor.md +193 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/opencode.md +262 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/pi.md +295 -0
- package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +539 -0
- package/.agents/skills/galdr-ymirsystem/assets/installation.md +763 -0
- package/.agents/skills/galdr-ymirsystem/assets/local-models/HOST-DATA.example.md +72 -0
- package/.agents/skills/galdr-ymirsystem/assets/local-models.md +274 -0
- package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +163 -0
- package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +415 -0
- package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
- package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +133 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/einherjar-spawn.schema.json +81 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/herdr-profile.toml +75 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +101 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/supervision-tree.yml +163 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot-guide.md +58 -0
- package/.agents/skills/galdr-ymirsystem/assets/porting-upstream-to-norse.md +301 -0
- package/.agents/skills/galdr-ymirsystem/assets/principles.md +385 -0
- package/.agents/skills/galdr-ymirsystem/assets/reference-adoption.md +99 -0
- package/.agents/skills/galdr-ymirsystem/assets/registry.md +379 -0
- package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +463 -0
- package/.agents/skills/galdr-ymirsystem/assets/runtime-components.md +553 -0
- package/.agents/skills/galdr-ymirsystem/assets/smidja.md +180 -0
- package/.agents/skills/galdr-ymirsystem/schemas/toon-schemas.md +190 -0
- package/.agents/skills/galdr-ymirsystem/scripts/README.md +45 -0
- package/.agents/skills/galdr-ymirsystem/scripts/bench-one.sh +248 -0
- package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +503 -0
- package/.agents/skills/galdr-ymirsystem/scripts/gpu-sample.sh +43 -0
- package/.agents/skills/galdr-ymirsystem/scripts/stop-all.sh +34 -0
- package/.agents/skills/galdr-ymirsystem/scripts/toon-check.py +188 -0
- package/.agents/skills/gjallarhorn-relay/README.md +3 -0
- package/.agents/skills/gjallarhorn-relay/SKILL.md +283 -0
- package/.agents/skills/groa-update/SKILL.md +90 -0
- package/.agents/skills/hamr-adapters/README.md +3 -0
- package/.agents/skills/hamr-adapters/SKILL.md +123 -0
- package/.agents/skills/hamr-adapters/references/common/control-and-recovery.md +37 -0
- package/.agents/skills/hamr-adapters/references/common/dispatch.md +32 -0
- package/.agents/skills/hamr-adapters/references/common/model-and-effort.md +42 -0
- package/.agents/skills/hamr-adapters/references/common/primary-hooks.md +40 -0
- package/.agents/skills/hamr-adapters/references/harness/claude.md +55 -0
- package/.agents/skills/hamr-adapters/references/harness/codex.md +43 -0
- package/.agents/skills/hamr-adapters/references/harness/cursor.md +75 -0
- package/.agents/skills/hamr-adapters/references/harness/grok.md +69 -0
- package/.agents/skills/hamr-adapters/references/harness/kimi.md +51 -0
- package/.agents/skills/hamr-adapters/references/harness/muse.md +70 -0
- package/.agents/skills/hamr-adapters/references/harness/opencode.md +42 -0
- package/.agents/skills/hamr-adapters/references/harness/pi.md +56 -0
- package/.agents/skills/herdr-panes/README.md +3 -0
- package/.agents/skills/herdr-panes/SKILL.md +92 -0
- package/.agents/skills/herdr-panes/assets/herdr-backend.md +346 -0
- package/.agents/skills/herdr-panes/assets/tmux-backend.md +115 -0
- package/.agents/skills/hnoss-design/DESIGN.md +19 -0
- package/.agents/skills/hnoss-design/README.md +3 -0
- package/.agents/skills/hnoss-design/SKILL.md +83 -0
- package/.agents/skills/hvild-afk/README.md +3 -0
- package/.agents/skills/hvild-afk/SKILL.md +241 -0
- package/.agents/skills/jord-projects/README.md +3 -0
- package/.agents/skills/jord-projects/SKILL.md +92 -0
- package/.agents/skills/lifecycle/SKILL.md +56 -0
- package/.agents/skills/lifecycle/smoke_test.sh +57 -0
- package/.agents/skills/lifecycle/start.sh +21 -0
- package/.agents/skills/lifecycle/status.sh +35 -0
- package/.agents/skills/lifecycle/stop.sh +16 -0
- package/.agents/skills/muninn-stow/README.md +3 -0
- package/.agents/skills/muninn-stow/SKILL.md +309 -0
- package/.agents/skills/no-mistakes/SKILL.md +379 -0
- package/.agents/skills/nornir-schedule/README.md +3 -0
- package/.agents/skills/nornir-schedule/SKILL.md +25 -0
- package/.agents/skills/nornir-schedule/assets/events.md +137 -0
- package/.agents/skills/nornir-schedule/assets/quota.md +131 -0
- package/.agents/skills/nsr-compliance/README.md +3 -0
- package/.agents/skills/nsr-compliance/SKILL.md +29 -0
- package/.agents/skills/nsr-compliance/assets/nsr/README.md +40 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/README.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/git-workflow.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/security.md +22 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/tech-stack.md +8 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/SKILL.md +71 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/config/core_four.yaml +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/validate_code.sh +29 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/verify_docs.py +66 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/generate.sh +109 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/telemetry/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/README.md +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/README.md +36 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/SKILL-template.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/README.md +25 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/SKILL.md +13 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/create_branch.sh +6 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/safe_commit.sh +9 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/sync_upstream.sh +6 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/README.md +27 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/SKILL.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/smoke_test.sh +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/start.sh +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/status.sh +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/stop.sh +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/core_four.yaml +13 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/README.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/api.md +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/compliance-wiring.md +155 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/frontend.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/README.md +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/README.md +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/template-README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/deploy.sh +49 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/development.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/production.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/staging.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/template.env.example +8 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/README.md +26 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/agent.md +7 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/developers/dev-1.md +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/linux.md +9 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/macos.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/shared.md +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/windows.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/README.md +25 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/_template.md +41 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/hosting-1.md +41 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/README.md +21 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/README.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/deployment.md +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/disaster-recovery.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/ops-tasks.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/features/README.md +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/project-skills.md +146 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/software-compliance-research.md +269 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/working-with-agents.md +260 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/AGENTS.md +38 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/ARCHITECTURE.md +36 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/BEST_PRACTICES.md +27 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/CI_CD.md +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/FEATURES.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/README.md +22 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/RULES.md +37 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/STRUCTURE.md +45 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/TECH_STACK.md +28 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scaffold-spec.md +63 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scripts/scaffold.sh +103 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scripts/validate.sh +52 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/SKILL.md +71 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/config/core_four.yaml +16 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/validate_code.sh +29 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/verify_docs.py +66 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/generate.sh +109 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/telemetry/README.md +11 -0
- package/.agents/skills/pr-ops/README.md +3 -0
- package/.agents/skills/pr-ops/SKILL.md +109 -0
- package/.agents/skills/ratatoskr-a2a/README.md +3 -0
- package/.agents/skills/ratatoskr-a2a/SKILL.md +148 -0
- package/.agents/skills/rules-check-drift/SKILL.md +81 -0
- package/.agents/skills/saga-bearings/README.md +3 -0
- package/.agents/skills/saga-bearings/SKILL.md +26 -0
- package/.agents/skills/saga-bearings/assets/bearings.md +161 -0
- package/.agents/skills/saga-bearings/assets/board-template.html +731 -0
- package/.agents/skills/saga-bearings/assets/recap.md +56 -0
- package/.agents/skills/syn-recovery/README.md +3 -0
- package/.agents/skills/syn-recovery/SKILL.md +54 -0
- package/.agents/skills/tyr-check/README.md +3 -0
- package/.agents/skills/tyr-check/SKILL.md +218 -0
- package/.agents/skills/urdh-hold/README.md +3 -0
- package/.agents/skills/urdh-hold/SKILL.md +25 -0
- package/.agents/skills/urdh-hold/assets/decisions.md +15 -0
- package/.agents/skills/urdh-hold/assets/hold.md +54 -0
- package/.agents/skills/vor-diagnostics/README.md +3 -0
- package/.agents/skills/vor-diagnostics/SKILL.md +68 -0
- package/.agents/skills/ymir-host/README.md +3 -0
- package/.agents/skills/ymir-host/SKILL.md +35 -0
- package/.agents/skills/ymir-host/assets/desktop.md +36 -0
- package/.agents/skills/ymir-host/assets/install.md +69 -0
- package/.agents/skills/ymir-host/assets/omarchy.md +297 -0
- package/.agents/skills/ymir-host/assets/runbooks.md +15 -0
- package/.agents/skills/ymir-host/assets/thjazi.md +220 -0
- package/.agents/skills/ymir-host/assets/update.md +64 -0
- package/.agents/tests/assets/board-render-harness.mjs +117 -0
- package/.agents/tests/cmux-test-safety.sh +51 -0
- package/.agents/tests/fixtures.sh +291 -0
- package/.agents/tests/fm-afk-inject-e2e.test.sh +428 -0
- package/.agents/tests/fm-afk-inject-herdr-e2e.test.sh +536 -0
- package/.agents/tests/fm-afk-launch.test.sh +961 -0
- package/.agents/tests/fm-afk-pi-herdr-return-e2e.test.sh +297 -0
- package/.agents/tests/fm-afk-return.test.sh +283 -0
- package/.agents/tests/fm-arm-pretool-check.test.sh +482 -0
- package/.agents/tests/fm-ask-user-authority.test.sh +42 -0
- package/.agents/tests/fm-backend-autodetect-smoke.test.sh +176 -0
- package/.agents/tests/fm-backend-cmux-smoke.test.sh +188 -0
- package/.agents/tests/fm-backend-cmux.test.sh +1166 -0
- package/.agents/tests/fm-backend-herdr-eventwait-smoke.test.sh +136 -0
- package/.agents/tests/fm-backend-herdr-eventwait.test.py +106 -0
- package/.agents/tests/fm-backend-herdr-focus-flash-e2e.test.sh +409 -0
- package/.agents/tests/fm-backend-herdr-launcher-workspace-e2e.test.sh +437 -0
- package/.agents/tests/fm-backend-herdr-presentation-e2e.test.sh +1424 -0
- package/.agents/tests/fm-backend-herdr-prune-safety-e2e.test.sh +180 -0
- package/.agents/tests/fm-backend-herdr-respawn-idem-e2e.test.sh +182 -0
- package/.agents/tests/fm-backend-herdr-smoke.test.sh +355 -0
- package/.agents/tests/fm-backend-herdr-workspace-per-home-e2e.test.sh +251 -0
- package/.agents/tests/fm-backend-herdr.test.sh +4606 -0
- package/.agents/tests/fm-backend-orca.test.sh +1357 -0
- package/.agents/tests/fm-backend-tmux-smoke.test.sh +173 -0
- package/.agents/tests/fm-backend-zellij-smoke.test.sh +205 -0
- package/.agents/tests/fm-backend-zellij.test.sh +1360 -0
- package/.agents/tests/fm-backend.test.sh +1142 -0
- package/.agents/tests/fm-backlog-atomicity.test.sh +2302 -0
- package/.agents/tests/fm-backlog-handoff.test.sh +1360 -0
- package/.agents/tests/fm-bearings-board-render.test.sh +135 -0
- package/.agents/tests/fm-bearings-board.test.sh +414 -0
- package/.agents/tests/fm-bearings-snapshot.test.sh +1990 -0
- package/.agents/tests/fm-bootstrap-network-parallel.test.sh +316 -0
- package/.agents/tests/fm-bootstrap.test.sh +1178 -0
- package/.agents/tests/fm-branch-supervision.test.sh +552 -0
- package/.agents/tests/fm-brief.test.sh +785 -0
- package/.agents/tests/fm-busy-adapter-wiring.test.sh +324 -0
- package/.agents/tests/fm-busy-state.test.sh +465 -0
- package/.agents/tests/fm-calm-pi-extension.test.sh +3979 -0
- package/.agents/tests/fm-captain-hold-lifecycle.test.sh +1187 -0
- package/.agents/tests/fm-cd-pretool-check.test.sh +400 -0
- package/.agents/tests/fm-check-unregister.test.sh +198 -0
- package/.agents/tests/fm-classify-corr-token.test.sh +543 -0
- package/.agents/tests/fm-classify-decision-key.test.sh +340 -0
- package/.agents/tests/fm-claude-stop-autoarm-live-e2e.test.sh +164 -0
- package/.agents/tests/fm-claude-stop-autoarm.test.sh +1190 -0
- package/.agents/tests/fm-cmux-claude-composer-live-e2e.test.sh +110 -0
- package/.agents/tests/fm-codex-continuity-live-e2e.test.sh +55 -0
- package/.agents/tests/fm-composer-ghost.test.sh +716 -0
- package/.agents/tests/fm-composer-lib.test.sh +666 -0
- package/.agents/tests/fm-composer-matrix-live-e2e.test.sh +220 -0
- package/.agents/tests/fm-control-herdr-smoke.test.sh +149 -0
- package/.agents/tests/fm-control-relaunch.test.sh +1530 -0
- package/.agents/tests/fm-control.test.sh +910 -0
- package/.agents/tests/fm-crew-state.test.sh +1610 -0
- package/.agents/tests/fm-cursor-harness.test.sh +404 -0
- package/.agents/tests/fm-cursor-primary-live-e2e.test.sh +217 -0
- package/.agents/tests/fm-cursor-primary.test.sh +708 -0
- package/.agents/tests/fm-daemon.test.sh +2740 -0
- package/.agents/tests/fm-documentation-audiences.test.sh +141 -0
- package/.agents/tests/fm-ensure-agents-md.test.sh +371 -0
- package/.agents/tests/fm-extension-binding.test.sh +2187 -0
- package/.agents/tests/fm-fleet-snapshot-view.test.sh +816 -0
- package/.agents/tests/fm-fleet-sync.test.sh +721 -0
- package/.agents/tests/fm-gate-refuse.test.sh +356 -0
- package/.agents/tests/fm-gitignore-config.test.sh +89 -0
- package/.agents/tests/fm-gotmp.test.sh +230 -0
- package/.agents/tests/fm-grok-continuity-live-e2e.test.sh +112 -0
- package/.agents/tests/fm-grok-harness.test.sh +117 -0
- package/.agents/tests/fm-grok-stop-live-e2e.test.sh +219 -0
- package/.agents/tests/fm-guard-stale-banner.test.sh +709 -0
- package/.agents/tests/fm-harness-adapter-instructions-live-e2e.test.sh +134 -0
- package/.agents/tests/fm-harness-adapter-references.test.sh +30 -0
- package/.agents/tests/fm-harness-liveness-drift-live-e2e.test.sh +151 -0
- package/.agents/tests/fm-herdr-lab.test.sh +243 -0
- package/.agents/tests/fm-herdr-session-cleanup-e2e.test.sh +146 -0
- package/.agents/tests/fm-herdr-session-cleanup.test.sh +329 -0
- package/.agents/tests/fm-herdr-submit-confirm-live-e2e.test.sh +125 -0
- package/.agents/tests/fm-herdr-version-floor-live-e2e.test.sh +144 -0
- package/.agents/tests/fm-home-summary-refresh.test.sh +971 -0
- package/.agents/tests/fm-inactive-reconcile.test.sh +485 -0
- package/.agents/tests/fm-kimi-harness.test.sh +687 -0
- package/.agents/tests/fm-lint-workflows.test.sh +532 -0
- package/.agents/tests/fm-lint.test.sh +1023 -0
- package/.agents/tests/fm-muse-harness.test.sh +932 -0
- package/.agents/tests/fm-muse-signals-live-e2e.test.sh +205 -0
- package/.agents/tests/fm-no-mistakes-required.test.sh +72 -0
- package/.agents/tests/fm-on.test.sh +507 -0
- package/.agents/tests/fm-opencode-primary-live-e2e.test.sh +357 -0
- package/.agents/tests/fm-operational-input.test.sh +160 -0
- package/.agents/tests/fm-peek-remote.test.sh +110 -0
- package/.agents/tests/fm-pending-reply.test.sh +1288 -0
- package/.agents/tests/fm-pi-branch-extension.test.sh +3182 -0
- package/.agents/tests/fm-pi-branch-live-e2e.test.sh +527 -0
- package/.agents/tests/fm-pi-primary-live-e2e.test.sh +345 -0
- package/.agents/tests/fm-pi-primary-types.test.sh +68 -0
- package/.agents/tests/fm-pi-watch-extension.test.sh +2843 -0
- package/.agents/tests/fm-pr-check-security.test.sh +2140 -0
- package/.agents/tests/fm-pr-merge.test.sh +2135 -0
- package/.agents/tests/fm-procevent-quota.test.sh +225 -0
- package/.agents/tests/fm-procevent-when.test.sh +406 -0
- package/.agents/tests/fm-procevent.test.sh +1745 -0
- package/.agents/tests/fm-project-origin.test.sh +110 -0
- package/.agents/tests/fm-public-followup.test.sh +2272 -0
- package/.agents/tests/fm-quota-array-dispatch-live-e2e.test.sh +526 -0
- package/.agents/tests/fm-quota-choose.test.sh +626 -0
- package/.agents/tests/fm-remote-backlog-handoff.test.sh +453 -0
- package/.agents/tests/fm-remote-doctor.test.sh +625 -0
- package/.agents/tests/fm-remote-entrypoint.test.sh +59 -0
- package/.agents/tests/fm-remote-job-orphan-reap.test.sh +205 -0
- package/.agents/tests/fm-remote-job.test.sh +768 -0
- package/.agents/tests/fm-remote-reply.test.sh +520 -0
- package/.agents/tests/fm-remote-secondmate-lifecycle-e2e.test.sh +1256 -0
- package/.agents/tests/fm-remote-secondmate-parent-binding.test.sh +326 -0
- package/.agents/tests/fm-remote-secondmate-trace-context.test.sh +310 -0
- package/.agents/tests/fm-remote-transport-lanes.test.sh +431 -0
- package/.agents/tests/fm-review-diff.test.sh +176 -0
- package/.agents/tests/fm-secondmate-harness.test.sh +2600 -0
- package/.agents/tests/fm-secondmate-lifecycle-e2e.test.sh +259 -0
- package/.agents/tests/fm-secondmate-liveness.test.sh +559 -0
- package/.agents/tests/fm-secondmate-reconcile.test.sh +732 -0
- package/.agents/tests/fm-secondmate-safety.test.sh +2972 -0
- package/.agents/tests/fm-secondmate-sync.test.sh +913 -0
- package/.agents/tests/fm-send-inbox-doorbell-live-e2e.test.sh +206 -0
- package/.agents/tests/fm-send-inbox.test.sh +352 -0
- package/.agents/tests/fm-send-popup-settle.test.sh +167 -0
- package/.agents/tests/fm-send-remote-delivery.test.sh +796 -0
- package/.agents/tests/fm-send-resolve-key.test.sh +551 -0
- package/.agents/tests/fm-send-secondmate-marker-herdr-e2e.test.sh +187 -0
- package/.agents/tests/fm-send-secondmate-marker.test.sh +278 -0
- package/.agents/tests/fm-send-settle.test.sh +147 -0
- package/.agents/tests/fm-send-strict.test.sh +241 -0
- package/.agents/tests/fm-session-lock-ancestry.test.sh +365 -0
- package/.agents/tests/fm-session-start.test.sh +2515 -0
- package/.agents/tests/fm-sessionstart-hook-live-e2e.test.sh +631 -0
- package/.agents/tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh +230 -0
- package/.agents/tests/fm-sessionstart-nudge.test.sh +1039 -0
- package/.agents/tests/fm-shared-captain-inheritance.test.sh +404 -0
- package/.agents/tests/fm-spawn-batch.test.sh +148 -0
- package/.agents/tests/fm-spawn-dispatch-profile.test.sh +827 -0
- package/.agents/tests/fm-spawn-pool-base-freshen.test.sh +440 -0
- package/.agents/tests/fm-spawn-worktree-settle.test.sh +147 -0
- package/.agents/tests/fm-startup-memory-budget.test.sh +333 -0
- package/.agents/tests/fm-startup-network.test.sh +712 -0
- package/.agents/tests/fm-stow-cascade.test.sh +370 -0
- package/.agents/tests/fm-subagent-pretool-check.test.sh +291 -0
- package/.agents/tests/fm-supervision-events.test.sh +156 -0
- package/.agents/tests/fm-supervision-instructions.test.sh +189 -0
- package/.agents/tests/fm-tangle-guard.test.sh +277 -0
- package/.agents/tests/fm-task-delivery.test.sh +420 -0
- package/.agents/tests/fm-task-inbox.test.sh +513 -0
- package/.agents/tests/fm-teardown-endpoint-safety.test.sh +374 -0
- package/.agents/tests/fm-teardown.test.sh +2665 -0
- package/.agents/tests/fm-test-fixture-cleanup.test.sh +172 -0
- package/.agents/tests/fm-test-fixtures.test.sh +132 -0
- package/.agents/tests/fm-test-isolation-proof.test.sh +278 -0
- package/.agents/tests/fm-test-run.test.sh +1196 -0
- package/.agents/tests/fm-tmux-agent-liveness.test.sh +354 -0
- package/.agents/tests/fm-tmux-submit-busy.test.sh +340 -0
- package/.agents/tests/fm-tool-update-check.test.sh +1044 -0
- package/.agents/tests/fm-trace-context-lib.test.sh +253 -0
- package/.agents/tests/fm-trace-context-spawn.test.sh +581 -0
- package/.agents/tests/fm-transition-lib.test.sh +54 -0
- package/.agents/tests/fm-turnend-guard.test.sh +1822 -0
- package/.agents/tests/fm-update.test.sh +304 -0
- package/.agents/tests/fm-vendor-auth-probe.test.sh +395 -0
- package/.agents/tests/fm-voice-relay.test.sh +4741 -0
- package/.agents/tests/fm-wake-daemon-lifecycle-e2e.test.sh +171 -0
- package/.agents/tests/fm-wake-drain-open-decisions-cursor.test.sh +356 -0
- package/.agents/tests/fm-wake-drain-open-decisions.test.sh +226 -0
- package/.agents/tests/fm-wake-drain-unread-status.test.sh +379 -0
- package/.agents/tests/fm-wake-queue.test.sh +1224 -0
- package/.agents/tests/fm-watch-arm.test.sh +821 -0
- package/.agents/tests/fm-watch-checkpoint.test.sh +87 -0
- package/.agents/tests/fm-watch-recovery-loop.test.sh +223 -0
- package/.agents/tests/fm-watch-triage.test.sh +3889 -0
- package/.agents/tests/fm-watcher-lock.test.sh +1133 -0
- package/.agents/tests/fm-x-mode.test.sh +3042 -0
- package/.agents/tests/gleipnir-guard-pretool-check.test.sh +51 -0
- package/.agents/tests/gleipnir-machine-lock.test.sh +130 -0
- package/.agents/tests/herdr-test-safety.sh +42 -0
- package/.agents/tests/lib.sh +315 -0
- package/.agents/tests/remote-herdr-fixture.sh +125 -0
- package/.agents/tests/secondmate-helpers.sh +193 -0
- package/.agents/tests/smoke.test.sh +93 -0
- package/.agents/tests/wake-helpers.sh +338 -0
- package/.agents/tests/zellij-test-safety.sh +60 -0
- package/.agents/tools/README.md +10 -0
- package/.agents/tools/bin/tasks-cli +267 -0
- package/.agents/tools/tasks-cli.ts +216 -0
- package/AGENTS.md +498 -0
- package/CHANGELOG.md +1913 -0
- package/LICENSE +1 -1
- package/NOTICE +63 -0
- package/README.md +114 -60
- package/RULES/01-domains.md +51 -0
- package/RULES/02-agents.md +78 -0
- package/RULES/03-houses.md +61 -0
- package/RULES/04-hoard.md +97 -0
- package/RULES/05-platforms.md +67 -0
- package/RULES/06-append-only.md +108 -0
- package/RULES/07-config.md +101 -0
- package/RULES/08-delivery-gate.md +75 -0
- package/RULES/09-electron.md +58 -0
- package/RULES/README.md +22 -0
- package/bin/README.md +107 -0
- package/bin/a2a-mcp.sh +133 -0
- package/bin/a2a-serve.py +103 -0
- package/bin/a2a-serve.sh +8 -0
- package/bin/a2a-talk.sh +50 -0
- package/bin/a2abridge-ensure.sh +108 -0
- package/bin/agent-run.sh +43 -0
- package/bin/agents-config.sh +256 -0
- package/bin/apodex-smoke-test.sh +105 -0
- package/bin/bifrost-bridge.sh +131 -0
- package/bin/bifrost-ingress.sh +77 -0
- package/bin/bootstrap-macos.sh +112 -0
- package/bin/bootstrap-windows.ps1 +112 -0
- package/bin/branch-guard.sh +64 -0
- package/bin/brokk +73 -0
- package/bin/brokk-classify-lib.sh +1770 -0
- package/bin/brokk-lease-lib.sh +222 -0
- package/bin/brokk-lease.sh +193 -0
- package/bin/brokk-lint.sh +65 -0
- package/bin/brokk-send.sh +51 -0
- package/bin/brokk-update.sh +9 -0
- package/bin/brokk-wake-grant.sh +127 -0
- package/bin/brokk-wake-lib.sh +1849 -0
- package/bin/changelog-assemble.sh +154 -0
- package/bin/changelog-guard.sh +197 -0
- package/bin/crash-sense.sh +173 -0
- package/bin/design-check.sh +80 -0
- package/bin/design-icon.sh +153 -0
- package/bin/desktop-place.sh +224 -0
- package/bin/docs-guard.sh +39 -0
- package/bin/editor-place.sh +225 -0
- package/bin/eindri-acclaim.sh +47 -0
- package/bin/eindri-control.sh +28 -0
- package/bin/eindri-role.sh +83 -0
- package/bin/eindri-seat.sh +83 -0
- package/bin/eindri-seen.sh +54 -0
- package/bin/eindri-send.sh +15 -0
- package/bin/eindri-start.sh +98 -0
- package/bin/eindri-watch.sh +57 -0
- package/bin/einherjar-spawn.sh +576 -0
- package/bin/eir-doctor.sh +158 -0
- package/bin/erindi-brief.sh +271 -0
- package/bin/feature-inventory.sh +73 -0
- package/bin/fleet-apply.sh +81 -0
- package/bin/github-deploy.sh +94 -0
- package/bin/gjallarhorn-expose.sh +136 -0
- package/bin/gjallarhorn-notify.sh +71 -0
- package/bin/gjallarhorn-purge.sh +67 -0
- package/bin/gjallarhorn-tunnel.sh +85 -0
- package/bin/gleipnir-lock-lib.sh +284 -0
- package/bin/groa-update.sh +130 -0
- package/bin/hall-snapshot.sh +111 -0
- package/bin/hamr-harness.sh +246 -0
- package/bin/heimdall-ssh-keys.sh +374 -0
- package/bin/herdr-agents.py +44 -0
- package/bin/herdr-ensure.sh +104 -0
- package/bin/herdr-run.sh +452 -0
- package/bin/hermes-ensure.sh +88 -0
- package/bin/hlidskjalf-agents.sh +172 -0
- package/bin/hlidskjalf-usage.sh +196 -0
- package/bin/hoard-guard.sh +135 -0
- package/bin/hoard-lib.sh +105 -0
- package/bin/hodd.sh +102 -0
- package/bin/host-sense.sh +199 -0
- package/bin/huginn-research-worker.sh +121 -0
- package/bin/local-model-lock.sh +70 -0
- package/bin/mimir-bridge.py +259 -0
- package/bin/mimir-bridge.sh +100 -0
- package/bin/mimir-ingest.sh +131 -0
- package/bin/mimir-reflect.sh +27 -0
- package/bin/mimir.sh +113 -0
- package/bin/mjollnir-webhook.sh +72 -0
- package/bin/mjollnir.sh +104 -0
- package/bin/model-hardware.sh +96 -0
- package/bin/model-resolve.sh +160 -0
- package/bin/models-detect.sh +178 -0
- package/bin/nornir-cron-start.sh +162 -0
- package/bin/nornir-job-daily-briefing.sh +191 -0
- package/bin/nornir-job-git-sync.sh +152 -0
- package/bin/nornir-job-hall-snapshot.sh +28 -0
- package/bin/nornir-job-memory-housekeeping.sh +114 -0
- package/bin/nornir-job-nsr-compliance.sh +54 -0
- package/bin/nornir-job-observer.sh +166 -0
- package/bin/nornir-job-skillopt-sleep.sh +76 -0
- package/bin/npm-publish.sh +136 -0
- package/bin/omarchy-hook-install.sh +60 -0
- package/bin/omarchy-install.sh +161 -0
- package/bin/omarchy-plugins.sh +187 -0
- package/bin/omarchy-sense.sh +162 -0
- package/bin/perm-guard.sh +30 -0
- package/bin/pi-agent.sh +68 -0
- package/bin/pi-ensure.sh +77 -0
- package/bin/pi-local.sh +37 -0
- package/bin/pi-model-agnostic.sh +71 -0
- package/bin/pi-seat.sh +63 -0
- package/bin/prereq-ensure.sh +166 -0
- package/bin/private-guard.sh +58 -0
- package/bin/project-git.sh +69 -0
- package/bin/public-guard.sh +71 -0
- package/bin/ratatoskr.sh +49 -0
- package/bin/realm-lib.sh +32 -0
- package/bin/repo-scrub.sh +62 -0
- package/bin/rodd-operational-input.sh +250 -0
- package/bin/runes-append.sh +198 -0
- package/bin/saga-session-start.sh +172 -0
- package/bin/saga-sessionstart-run.sh +79 -0
- package/bin/saga-wake-drain.sh +41 -0
- package/bin/secret-guard.sh +63 -0
- package/bin/sessrumnir-ensure.sh +100 -0
- package/bin/sessrumnir-sync.sh +237 -0
- package/bin/sessrumnir.sh +104 -0
- package/bin/skillopt-setup.sh +76 -0
- package/bin/skuld-branch-outcome.sh +203 -0
- package/bin/skuld-branch-prompt.sh +101 -0
- package/bin/smidja-bootstrap.sh +94 -0
- package/bin/smidja-observe.sh +86 -0
- package/bin/syn-arm-pretool-check.sh +57 -0
- package/bin/syn-asset-pretool-check.sh +89 -0
- package/bin/syn-cd-pretool-check.sh +28 -0
- package/bin/syn-guard-pretool-check.sh +83 -0
- package/bin/syn-spawn-pretool-check.sh +58 -0
- package/bin/syn-turnend-guard.sh +36 -0
- package/bin/syn-watch-arm.sh +99 -0
- package/bin/tailscale-sync.sh +203 -0
- package/bin/telegram-bot.sh +82 -0
- package/bin/toolchain.sh +74 -0
- package/bin/ui-truth-check.sh +75 -0
- package/bin/utgard.sh +111 -0
- package/bin/valhalla.sh +115 -0
- package/bin/valknut-load.sh +298 -0
- package/bin/vor-crew-state.sh +124 -0
- package/bin/wedge-notify.sh +60 -0
- package/bin/workspace-provision.sh +80 -0
- package/bin/workspace-rag.sh +163 -0
- package/bin/wyrd-db.sh +47 -0
- package/bin/yggdrasil.sh +152 -0
- package/bin/ymir-edit.sh +145 -0
- package/bin/ymir-install.sh +806 -0
- package/bin/ymir-invite.sh +93 -0
- package/bin/ymir-isolation.sh +88 -0
- package/bin/ymir-migrate.sh +61 -0
- package/bin/ymir-plan.sh +356 -0
- package/bin/ymir-platform.sh +348 -0
- package/bin/ymir-say.sh +110 -0
- package/bin/ymir-setup-auth.sh +119 -0
- package/bin/ymir-validate.sh +193 -0
- package/bin/ymir.js +74 -16
- package/docs/Architecture.md +339 -0
- package/docs/design.md +351 -0
- package/docs/installations/README.md +63 -0
- package/docs/installations/ymir-home.md +93 -0
- package/docs/integration.md +105 -0
- package/docs/lore.md +1060 -0
- package/docs/research/agent-harnesses.md +290 -0
- package/docs/runbooks/README.md +19 -0
- package/docs/runbooks/a2a.md +91 -0
- package/docs/runbooks/agent-permissions.md +99 -0
- package/docs/runbooks/agents.md +57 -0
- package/docs/runbooks/models.md +69 -0
- package/docs/runbooks/realm-onboarding.md +60 -0
- package/docs/runbooks/secrets-and-hoard.md +60 -0
- package/docs/runbooks/updates-and-migrations.md +110 -0
- package/docs/runbooks/worktrees-isolation.md +72 -0
- package/docs/session-start.md +105 -0
- package/docs/skillopt-integration.md +321 -0
- package/docs/sync-system.md +66 -0
- package/docs/workspaces.md +104 -0
- package/docs/ymir-rut.md +260 -0
- package/package.json +54 -14
- package/packaging/build.sh +74 -0
- package/packaging/macos/Ymir Installer.command +43 -0
- package/packaging/windows/ymir-setup.nsi +74 -0
- package/scripts/README.md +22 -0
- package/scripts/electron.sh +269 -0
- package/scripts/lower.sh +34 -0
- package/scripts/raise.sh +42 -0
- package/scripts/start.sh +217 -0
- package/scripts/stop.sh +113 -0
- package/install.sh +0 -35
|
@@ -0,0 +1,1166 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tests/fm-backend-cmux.test.sh - fake-cmux-CLI unit tests for the cmux
|
|
3
|
+
# session-provider adapter (bin/backends/cmux.sh), verified against the real
|
|
4
|
+
# cmux 0.64.17 binary (docs/cmux-backend.md). Mirrors
|
|
5
|
+
# tests/fm-backend-zellij.test.sh's/tests/fm-backend-herdr.test.sh's
|
|
6
|
+
# fakebin/command-log convention: a small, LOG-based, canned-response fake
|
|
7
|
+
# `cmux` + real `jq` (jq is a real required tool for this backend, not
|
|
8
|
+
# faked). The real-binary smoke test lives in
|
|
9
|
+
# tests/fm-backend-cmux-smoke.test.sh, gated on the cmux binary actually
|
|
10
|
+
# being installed and reachable.
|
|
11
|
+
set -u
|
|
12
|
+
|
|
13
|
+
# shellcheck source=tests/lib.sh
|
|
14
|
+
. "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
|
15
|
+
|
|
16
|
+
command -v jq >/dev/null 2>&1 || { echo "skip: jq not found (required by the cmux adapter)"; exit 0; }
|
|
17
|
+
|
|
18
|
+
TMP_ROOT=$(fm_test_tmproot fm-backend-cmux-tests)
|
|
19
|
+
|
|
20
|
+
# make_cmux_fakebin: a `cmux` stub that logs every invocation (one line,
|
|
21
|
+
# unit-separated args, to $FM_CMUX_LOG) and returns the canned response for
|
|
22
|
+
# that call read from $FM_CMUX_RESPONSES/<n>.out, consumed IN ORDER (call 1
|
|
23
|
+
# reads 1.out, call 2 reads 2.out, ...), mirroring
|
|
24
|
+
# tests/fm-backend-zellij.test.sh's make_zellij_fakebin. A missing response
|
|
25
|
+
# file means "succeed with empty stdout" (new-workspace/send/send-key/
|
|
26
|
+
# close-* are silent on success on the real CLI). `version` and `ping` are
|
|
27
|
+
# handled specially (not call-counted, not consuming the ordered response
|
|
28
|
+
# queue) since fm_backend_cmux_version_check/fm_backend_cmux_ping_state are
|
|
29
|
+
# called at points a test may not want to hand-count, exactly mirroring
|
|
30
|
+
# zellij's --version/list-sessions special-casing.
|
|
31
|
+
make_cmux_fakebin() { # <dir> -> echoes fakebin dir
|
|
32
|
+
local dir=$1 fb="$1/fakebin"
|
|
33
|
+
mkdir -p "$fb"
|
|
34
|
+
cat > "$fb/cmux" <<'SH'
|
|
35
|
+
#!/usr/bin/env bash
|
|
36
|
+
set -u
|
|
37
|
+
LOG="${FM_CMUX_LOG:?}"
|
|
38
|
+
RESP="${FM_CMUX_RESPONSES:?}"
|
|
39
|
+
COUNT_FILE="$RESP/.count"
|
|
40
|
+
{
|
|
41
|
+
printf 'CMUX_SOCKET_PASSWORD=%s' "${CMUX_SOCKET_PASSWORD:-}"
|
|
42
|
+
for a in "$@"; do printf '\x1f%s' "$a"; done
|
|
43
|
+
printf '\n'
|
|
44
|
+
} >> "$LOG"
|
|
45
|
+
|
|
46
|
+
if [ "${1:-}" = version ]; then
|
|
47
|
+
printf 'cmux %s (97) [abcdef1]\n' "${FM_CMUX_FAKE_VERSION:-0.64.17}"
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
50
|
+
if [ "${1:-}" = ping ]; then
|
|
51
|
+
printf '%s\n' "${FM_CMUX_FAKE_PING:-PONG}"
|
|
52
|
+
exit "${FM_CMUX_FAKE_PING_EXIT:-0}"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
next=$(( $(cat "$COUNT_FILE" 2>/dev/null || echo 0) + 1 ))
|
|
56
|
+
n=$next
|
|
57
|
+
echo "$n" > "$COUNT_FILE"
|
|
58
|
+
if [ -f "$RESP/$n.exit" ]; then
|
|
59
|
+
exit "$(cat "$RESP/$n.exit")"
|
|
60
|
+
fi
|
|
61
|
+
[ -f "$RESP/$n.out" ] && cat "$RESP/$n.out"
|
|
62
|
+
exit 0
|
|
63
|
+
SH
|
|
64
|
+
chmod +x "$fb/cmux"
|
|
65
|
+
printf '%s\n' "$fb"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
cmux_workspace_list_response() { # <dir> <n> <id1> <title1> [<id2> <title2> ...]
|
|
69
|
+
local dir=$1 n=$2 json first=1
|
|
70
|
+
shift 2
|
|
71
|
+
json='{"workspaces":['
|
|
72
|
+
while [ $# -ge 2 ]; do
|
|
73
|
+
[ "$first" -eq 1 ] || json="$json,"
|
|
74
|
+
json="$json{\"id\":\"$1\",\"title\":\"$2\"}"
|
|
75
|
+
first=0
|
|
76
|
+
shift 2
|
|
77
|
+
done
|
|
78
|
+
json="$json]}"
|
|
79
|
+
printf '%s' "$json" > "$dir/responses/$n.out"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
cmux_panes_response() { # <dir> <n> <surface_id>
|
|
83
|
+
printf '{"panes":[{"selected_surface_id":"%s","surface_ids":["%s"]}]}' "$3" "$3" > "$1/responses/$2.out"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
cmux_windows_response() { # <dir> <n> <window_id1> <count1> [<window_id2> <count2> ...]
|
|
87
|
+
local dir=$1 n=$2 json first=1
|
|
88
|
+
shift 2
|
|
89
|
+
json='['
|
|
90
|
+
while [ $# -ge 2 ]; do
|
|
91
|
+
[ "$first" -eq 1 ] || json="$json,"
|
|
92
|
+
json="$json{\"id\":\"$1\",\"workspace_count\":$2}"
|
|
93
|
+
first=0
|
|
94
|
+
shift 2
|
|
95
|
+
done
|
|
96
|
+
json="$json]"
|
|
97
|
+
printf '%s' "$json" > "$dir/responses/$n.out"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
cmux_panes_empty_response() { # <dir> <n>
|
|
101
|
+
printf '{"panes":[]}' > "$1/responses/$2.out"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
cmux_read_screen_response() { # <dir> <n> <text>
|
|
105
|
+
jq -n --arg t "$3" '{text:$t}' > "$1/responses/$2.out"
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
cmux_expected_root_hash() { # <root>
|
|
109
|
+
local root real
|
|
110
|
+
root=$1
|
|
111
|
+
real=$(cd "$root" && pwd -P) || return 1
|
|
112
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
113
|
+
printf '%s' "$real" | shasum -a 256 | awk '{print substr($1,1,8)}'
|
|
114
|
+
elif command -v sha256sum >/dev/null 2>&1; then
|
|
115
|
+
printf '%s' "$real" | sha256sum | awk '{print substr($1,1,8)}'
|
|
116
|
+
else
|
|
117
|
+
printf '%s' "$real" | cksum | awk '{printf "%08x", $1}'
|
|
118
|
+
fi
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
cmux_expected_home_label() { # [home] [root]
|
|
122
|
+
local home=${1:-$ROOT} root=${2:-$ROOT} marker id prefix
|
|
123
|
+
marker="$home/.fm-secondmate-home"
|
|
124
|
+
if [ -f "$marker" ]; then
|
|
125
|
+
id=$(tr -d '[:space:]' < "$marker" 2>/dev/null)
|
|
126
|
+
if [ -n "$id" ]; then
|
|
127
|
+
prefix="2ndmate-$id"
|
|
128
|
+
else
|
|
129
|
+
prefix="firstmate"
|
|
130
|
+
fi
|
|
131
|
+
else
|
|
132
|
+
prefix="firstmate"
|
|
133
|
+
fi
|
|
134
|
+
printf '%s-%s' "$prefix" "$(cmux_expected_root_hash "$root")"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
cmux_expected_scoped_title() { # <fm-task-label> [home] [root]
|
|
138
|
+
local label=$1 home=${2:-$ROOT} root=${3:-$ROOT} rest
|
|
139
|
+
case "$label" in
|
|
140
|
+
fm-*) rest=${label#fm-} ;;
|
|
141
|
+
*) rest=$label ;;
|
|
142
|
+
esac
|
|
143
|
+
printf 'fm-%s-%s' "$(cmux_expected_home_label "$home" "$root")" "$rest"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
cmux_assert_call_order() {
|
|
147
|
+
local log=$1 before=$2 after=$3 msg=$4 before_line after_line
|
|
148
|
+
before_line=$(grep -anF -- "$before" "$log" | head -1 | cut -d: -f1)
|
|
149
|
+
after_line=$(grep -anF -- "$after" "$log" | head -1 | cut -d: -f1)
|
|
150
|
+
[ -n "$before_line" ] || fail "$msg (missing before call: '$before')"
|
|
151
|
+
[ -n "$after_line" ] || fail "$msg (missing after call: '$after')"
|
|
152
|
+
[ "$before_line" -lt "$after_line" ] || fail "$msg"
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# --- version_check / tool_check ----------------------------------------------
|
|
156
|
+
|
|
157
|
+
test_version_check_accepts_current_version() {
|
|
158
|
+
local dir fb status
|
|
159
|
+
dir="$TMP_ROOT/version-ok"; mkdir -p "$dir/responses"
|
|
160
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
161
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" FM_CMUX_FAKE_VERSION=0.64.17 \
|
|
162
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_version_check' "$ROOT"
|
|
163
|
+
status=$?
|
|
164
|
+
expect_code 0 "$status" "version_check should accept 0.64.17 (the verified minimum)"
|
|
165
|
+
pass "fm_backend_cmux_version_check: accepts the verified minimum (0.64.17)"
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
test_version_check_accepts_newer_version() {
|
|
169
|
+
local dir fb status
|
|
170
|
+
dir="$TMP_ROOT/version-newer"; mkdir -p "$dir/responses"
|
|
171
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
172
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" FM_CMUX_FAKE_VERSION=0.70.0 \
|
|
173
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_version_check' "$ROOT"
|
|
174
|
+
status=$?
|
|
175
|
+
expect_code 0 "$status" "version_check should accept a newer minor (0.70.0)"
|
|
176
|
+
pass "fm_backend_cmux_version_check: accepts a newer version (0.70.0)"
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
test_version_check_refuses_old_version() {
|
|
180
|
+
local dir fb out status
|
|
181
|
+
dir="$TMP_ROOT/version-old"; mkdir -p "$dir/responses"
|
|
182
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
183
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" FM_CMUX_FAKE_VERSION=0.50.0 \
|
|
184
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_version_check' "$ROOT" 2>&1 )
|
|
185
|
+
status=$?
|
|
186
|
+
[ "$status" -ne 0 ] || fail "version_check should refuse 0.50.0 (below the 0.64 minimum)"
|
|
187
|
+
assert_contains "$out" "0.50.0" "version_check error did not name the rejected version"
|
|
188
|
+
pass "fm_backend_cmux_version_check: refuses an old version loudly"
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
test_version_check_refuses_missing_cmux() {
|
|
192
|
+
local dir out status
|
|
193
|
+
dir="$TMP_ROOT/version-missing"; mkdir -p "$dir/empty-fakebin"
|
|
194
|
+
# FM_BACKEND_CMUX_BUNDLE_BIN must also be overridden to a nonexistent path:
|
|
195
|
+
# this test may run on a machine (like the one that verified this adapter)
|
|
196
|
+
# where cmux really is installed at the real bundle path, which the plain
|
|
197
|
+
# PATH-emptying above would not hide.
|
|
198
|
+
out=$( PATH="$dir/empty-fakebin:/usr/bin:/bin" FM_BACKEND_CMUX_BUNDLE_BIN="$dir/no-such-cmux" \
|
|
199
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_version_check' "$ROOT" 2>&1 )
|
|
200
|
+
status=$?
|
|
201
|
+
[ "$status" -ne 0 ] || fail "version_check should refuse when cmux is not installed"
|
|
202
|
+
assert_contains "$out" "not found" "version_check did not report cmux as missing"
|
|
203
|
+
pass "fm_backend_cmux_version_check: refuses loudly when cmux is not found on PATH or at the bundle path"
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
# --- password resolution -------------------------------------------------
|
|
207
|
+
|
|
208
|
+
test_password_reads_from_config_file() {
|
|
209
|
+
local dir out
|
|
210
|
+
dir="$TMP_ROOT/password-file"; mkdir -p "$dir/config"
|
|
211
|
+
printf 'sekret-pw\n' > "$dir/config/cmux-socket-password"
|
|
212
|
+
out=$( FM_HOME="$dir" bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_password' "$ROOT" )
|
|
213
|
+
[ "$out" = "sekret-pw" ] || fail "password should be read from config/cmux-socket-password, got '$out'"
|
|
214
|
+
pass "fm_backend_cmux_password: reads the first non-empty line of config/cmux-socket-password"
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
test_password_preserves_config_file_whitespace() {
|
|
218
|
+
local dir out
|
|
219
|
+
dir="$TMP_ROOT/password-file-whitespace"; mkdir -p "$dir/config"
|
|
220
|
+
printf '\nsek ret\t pw \n' > "$dir/config/cmux-socket-password"
|
|
221
|
+
out=$( FM_HOME="$dir" bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_password' "$ROOT" )
|
|
222
|
+
[ "$out" = $'sek ret\t pw ' ] || fail "password should preserve spaces and tabs from config/cmux-socket-password, got '$out'"
|
|
223
|
+
pass "fm_backend_cmux_password: preserves spaces and tabs in config/cmux-socket-password"
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
test_password_respects_config_override() {
|
|
227
|
+
local dir home_cfg override_cfg out
|
|
228
|
+
dir="$TMP_ROOT/password-config-override"; home_cfg="$dir/home/config"; override_cfg="$dir/override-config"
|
|
229
|
+
mkdir -p "$home_cfg" "$override_cfg"
|
|
230
|
+
printf 'home-pw\n' > "$home_cfg/cmux-socket-password"
|
|
231
|
+
printf 'override-pw\n' > "$override_cfg/cmux-socket-password"
|
|
232
|
+
out=$( FM_HOME="$dir/home" FM_CONFIG_OVERRIDE="$override_cfg" bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_password' "$ROOT" )
|
|
233
|
+
[ "$out" = "override-pw" ] || fail "password should be read from FM_CONFIG_OVERRIDE, got '$out'"
|
|
234
|
+
pass "fm_backend_cmux_password: respects FM_CONFIG_OVERRIDE"
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
test_password_empty_when_config_absent() {
|
|
238
|
+
local dir out
|
|
239
|
+
dir="$TMP_ROOT/password-absent"; mkdir -p "$dir/config"
|
|
240
|
+
out=$( FM_HOME="$dir" bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_password' "$ROOT" )
|
|
241
|
+
[ -z "$out" ] || fail "password should be empty when config/cmux-socket-password is absent, got '$out'"
|
|
242
|
+
pass "fm_backend_cmux_password: empty when config/cmux-socket-password is absent"
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
test_cli_exports_password_only_when_configured() {
|
|
246
|
+
local dir fb
|
|
247
|
+
dir="$TMP_ROOT/password-export"; mkdir -p "$dir/config" "$dir/responses"
|
|
248
|
+
printf 'sekret-pw\n' > "$dir/config/cmux-socket-password"
|
|
249
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
250
|
+
PATH="$fb:$PATH" FM_HOME="$dir" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
251
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_cli ping' "$ROOT" >/dev/null
|
|
252
|
+
assert_contains "$(cat "$dir/log")" "CMUX_SOCKET_PASSWORD=sekret-pw" \
|
|
253
|
+
"fm_backend_cmux_cli did not export the configured password"
|
|
254
|
+
pass "fm_backend_cmux_cli: exports CMUX_SOCKET_PASSWORD when config/cmux-socket-password is set"
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
# --- target parsing, key normalization ---------------------------------------
|
|
258
|
+
|
|
259
|
+
test_parse_target() {
|
|
260
|
+
( . "$ROOT/bin/backends/cmux.sh"
|
|
261
|
+
fm_backend_cmux_parse_target "11111111-1111-1111-1111-111111111111:22222222-2222-2222-2222-222222222222" || exit 1
|
|
262
|
+
[ "$FM_BACKEND_CMUX_WORKSPACE" = "11111111-1111-1111-1111-111111111111" ] || { echo "workspace mismatch: $FM_BACKEND_CMUX_WORKSPACE" >&2; exit 1; }
|
|
263
|
+
[ "$FM_BACKEND_CMUX_SURFACE" = "22222222-2222-2222-2222-222222222222" ] || { echo "surface mismatch: $FM_BACKEND_CMUX_SURFACE" >&2; exit 1; }
|
|
264
|
+
) || fail "fm_backend_cmux_parse_target did not split workspace:surface correctly"
|
|
265
|
+
pass "fm_backend_cmux_parse_target: splits '<workspace_uuid>:<surface_uuid>' on the first colon"
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
test_normalize_key() {
|
|
269
|
+
( . "$ROOT/bin/backends/cmux.sh"
|
|
270
|
+
[ "$(fm_backend_cmux_normalize_key Enter)" = enter ] || { echo "Enter failed" >&2; exit 1; }
|
|
271
|
+
[ "$(fm_backend_cmux_normalize_key Escape)" = escape ] || { echo "Escape failed" >&2; exit 1; }
|
|
272
|
+
[ "$(fm_backend_cmux_normalize_key Esc)" = escape ] || { echo "Esc failed" >&2; exit 1; }
|
|
273
|
+
[ "$(fm_backend_cmux_normalize_key C-c)" = ctrl-c ] || { echo "C-c failed" >&2; exit 1; }
|
|
274
|
+
[ "$(fm_backend_cmux_normalize_key ctrl+c)" = ctrl-c ] || { echo "ctrl+c failed" >&2; exit 1; }
|
|
275
|
+
) || fail "fm_backend_cmux_normalize_key did not map firstmate's key vocabulary to cmux's verified names"
|
|
276
|
+
pass "fm_backend_cmux_normalize_key: Enter/Escape/C-c map to cmux's verified enter/escape/ctrl-c"
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
test_scoped_title_uses_primary_home_label() {
|
|
280
|
+
local dir out expected
|
|
281
|
+
dir="$TMP_ROOT/scoped-title-primary"; mkdir -p "$dir"
|
|
282
|
+
expected=$(cmux_expected_scoped_title fm-task1 "$dir")
|
|
283
|
+
out=$( FM_HOME="$dir" bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_scoped_title fm-task1' "$ROOT" )
|
|
284
|
+
[ "$out" = "$expected" ] || fail "primary scoped title should be $expected, got '$out'"
|
|
285
|
+
pass "fm_backend_cmux_scoped_title: scopes a primary task title with firstmate plus root hash"
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
test_scoped_title_uses_secondmate_home_label() {
|
|
289
|
+
local dir out expected
|
|
290
|
+
dir="$TMP_ROOT/scoped-title-secondmate"; mkdir -p "$dir"
|
|
291
|
+
printf 'sm-one\n' > "$dir/.fm-secondmate-home"
|
|
292
|
+
expected=$(cmux_expected_scoped_title fm-task1 "$dir")
|
|
293
|
+
out=$( FM_HOME="$dir" bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_scoped_title fm-task1' "$ROOT" )
|
|
294
|
+
[ "$out" = "$expected" ] || fail "secondmate scoped title should be $expected, got '$out'"
|
|
295
|
+
pass "fm_backend_cmux_scoped_title: scopes a secondmate task title with the home marker plus root hash"
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
test_scoped_title_changes_with_root_path() {
|
|
299
|
+
local dir home root_one root_two out_one out_two expected_one expected_two
|
|
300
|
+
dir="$TMP_ROOT/scoped-title-root-hash"; home="$dir/home"; root_one="$dir/root-one"; root_two="$dir/root-two"
|
|
301
|
+
mkdir -p "$home" "$root_one" "$root_two"
|
|
302
|
+
expected_one=$(cmux_expected_scoped_title fm-task1 "$home" "$root_one")
|
|
303
|
+
expected_two=$(cmux_expected_scoped_title fm-task1 "$home" "$root_two")
|
|
304
|
+
out_one=$( FM_HOME="$home" FM_ROOT_OVERRIDE="$root_one" bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_scoped_title fm-task1' "$ROOT" )
|
|
305
|
+
out_two=$( FM_HOME="$home" FM_ROOT_OVERRIDE="$root_two" bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_scoped_title fm-task1' "$ROOT" )
|
|
306
|
+
[ "$out_one" = "$expected_one" ] || fail "scoped title should include root-one hash as $expected_one, got '$out_one'"
|
|
307
|
+
[ "$out_two" = "$expected_two" ] || fail "scoped title should include root-two hash as $expected_two, got '$out_two'"
|
|
308
|
+
[ "$out_one" != "$out_two" ] || fail "scoped titles should differ for distinct FM_ROOT paths"
|
|
309
|
+
pass "fm_backend_cmux_scoped_title: includes the resolved FM_ROOT hash in the home label"
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
# --- dispatch wiring (fm-backend.sh) ------------------------------------------
|
|
313
|
+
|
|
314
|
+
test_dispatch_routes_cmux_backend() {
|
|
315
|
+
fm_backend_validate cmux 2>/dev/null || fail "fm_backend_validate should accept cmux"
|
|
316
|
+
pass "fm_backend_validate: cmux is a known backend"
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
test_dispatch_busy_state_unknown_for_cmux() {
|
|
320
|
+
# shellcheck source=/dev/null
|
|
321
|
+
. "$ROOT/bin/fm-backend.sh"
|
|
322
|
+
[ "$(fm_backend_busy_state cmux '11111111-1111-1111-1111-111111111111:22222222-2222-2222-2222-222222222222')" = unknown ] \
|
|
323
|
+
|| fail "fm_backend_busy_state should report unknown for cmux (no native agent-state primitive)"
|
|
324
|
+
pass "fm_backend_busy_state: cmux (no native primitive) always reports unknown, same as tmux/zellij/orca"
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
test_dispatch_composer_state_routes_cmux() {
|
|
328
|
+
local dir fb out target
|
|
329
|
+
dir="$TMP_ROOT/dispatch-composer"; mkdir -p "$dir/responses"
|
|
330
|
+
target="aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"
|
|
331
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
332
|
+
cmux_read_screen_response "$dir" 2 $' ╭────────────────────────╮\n │ ❯ hello captain │\n ╰──────── Composer ──────╯'
|
|
333
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
334
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
335
|
+
bash -c '. "$0/bin/fm-backend.sh"; fm_backend_composer_state cmux "$1"' "$ROOT" "$target" )
|
|
336
|
+
[ "$out" = pending ] || fail "fm_backend_composer_state should route cmux to its classifier, got '$out'"
|
|
337
|
+
pass "fm_backend_composer_state: routes cmux to the cmux composer classifier"
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
# --- ping_state / ensure_running ---------------------------------------------
|
|
341
|
+
|
|
342
|
+
test_ping_state_ok() {
|
|
343
|
+
local dir fb out
|
|
344
|
+
dir="$TMP_ROOT/ping-ok"; mkdir -p "$dir/responses"
|
|
345
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
346
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" FM_CMUX_FAKE_PING=PONG \
|
|
347
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_ping_state' "$ROOT" )
|
|
348
|
+
[ "$out" = ok ] || fail "ping_state should report ok on PONG, got '$out'"
|
|
349
|
+
pass "fm_backend_cmux_ping_state: reports 'ok' on PONG"
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
test_ping_state_denied() {
|
|
353
|
+
local dir fb out
|
|
354
|
+
dir="$TMP_ROOT/ping-denied"; mkdir -p "$dir/responses"
|
|
355
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
356
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" FM_CMUX_FAKE_PING_EXIT=1 \
|
|
357
|
+
FM_CMUX_FAKE_PING="Error: ERROR: Access denied - only processes started inside cmux can connect" \
|
|
358
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_ping_state' "$ROOT" )
|
|
359
|
+
[ "$out" = denied ] || fail "ping_state should report denied on the cmuxOnly rejection text, got '$out'"
|
|
360
|
+
pass "fm_backend_cmux_ping_state: reports 'denied' when socketControlMode=cmuxOnly rejects the connection"
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
test_ping_state_unauth() {
|
|
364
|
+
local dir fb out
|
|
365
|
+
dir="$TMP_ROOT/ping-unauth"; mkdir -p "$dir/responses"
|
|
366
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
367
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" FM_CMUX_FAKE_PING_EXIT=1 \
|
|
368
|
+
FM_CMUX_FAKE_PING="Error: ERROR: Authentication required - send auth <password> first" \
|
|
369
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_ping_state' "$ROOT" )
|
|
370
|
+
[ "$out" = unauth ] || fail "ping_state should report unauth when no password was presented, got '$out'"
|
|
371
|
+
pass "fm_backend_cmux_ping_state: reports 'unauth' when password mode rejects a missing/wrong password"
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
test_ping_state_invalid_password() {
|
|
375
|
+
local dir fb out
|
|
376
|
+
dir="$TMP_ROOT/ping-invalid-pw"; mkdir -p "$dir/responses"
|
|
377
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
378
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" FM_CMUX_FAKE_PING_EXIT=1 \
|
|
379
|
+
FM_CMUX_FAKE_PING="Error: ERROR: Invalid password" \
|
|
380
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_ping_state' "$ROOT" )
|
|
381
|
+
[ "$out" = unauth ] || fail "ping_state should report unauth on the wrong-password rejection text, got '$out'"
|
|
382
|
+
pass "fm_backend_cmux_ping_state: reports 'unauth' when password mode rejects a wrong password (Invalid password)"
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
test_ping_state_down() {
|
|
386
|
+
local dir fb out
|
|
387
|
+
dir="$TMP_ROOT/ping-down"; mkdir -p "$dir/responses"
|
|
388
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
389
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" FM_CMUX_FAKE_PING_EXIT=1 \
|
|
390
|
+
FM_CMUX_FAKE_PING="Error: Socket not found at /home/x/.local/state/cmux/cmux.sock" \
|
|
391
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_ping_state' "$ROOT" )
|
|
392
|
+
[ "$out" = down ] || fail "ping_state should report down when the socket does not exist yet, got '$out'"
|
|
393
|
+
pass "fm_backend_cmux_ping_state: reports 'down' when the app is not running yet"
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
test_ensure_running_returns_immediately_when_already_ok() {
|
|
397
|
+
local dir fb status
|
|
398
|
+
dir="$TMP_ROOT/ensure-ok"; mkdir -p "$dir/responses"
|
|
399
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
400
|
+
cat > "$fb/open" <<'SH'
|
|
401
|
+
#!/usr/bin/env bash
|
|
402
|
+
echo "open should not be called when cmux is already reachable" >&2
|
|
403
|
+
exit 1
|
|
404
|
+
SH
|
|
405
|
+
chmod +x "$fb/open"
|
|
406
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" FM_CMUX_FAKE_PING=PONG \
|
|
407
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_ensure_running' "$ROOT"
|
|
408
|
+
status=$?
|
|
409
|
+
expect_code 0 "$status" "ensure_running should succeed immediately when already reachable"
|
|
410
|
+
pass "fm_backend_cmux_ensure_running: returns immediately when cmux is already reachable"
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
test_ensure_running_fails_fast_on_denied_without_launching() {
|
|
414
|
+
local dir fb out status
|
|
415
|
+
dir="$TMP_ROOT/ensure-denied"; mkdir -p "$dir/responses"
|
|
416
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
417
|
+
cat > "$fb/open" <<'SH'
|
|
418
|
+
#!/usr/bin/env bash
|
|
419
|
+
echo "LAUNCHED" >> "${FM_CMUX_LAUNCH_MARKER:?}"
|
|
420
|
+
exit 0
|
|
421
|
+
SH
|
|
422
|
+
chmod +x "$fb/open"
|
|
423
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" FM_CMUX_FAKE_PING_EXIT=1 \
|
|
424
|
+
FM_CMUX_FAKE_PING="Error: ERROR: Access denied - only processes started inside cmux can connect" \
|
|
425
|
+
FM_CMUX_LAUNCH_MARKER="$dir/launched" \
|
|
426
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_ensure_running' "$ROOT" 2>&1 )
|
|
427
|
+
status=$?
|
|
428
|
+
[ "$status" -ne 0 ] || fail "ensure_running should refuse when the socket is denied (relaunching cannot fix a config problem)"
|
|
429
|
+
[ ! -f "$dir/launched" ] || fail "ensure_running should not attempt to launch cmux on a denied socket"
|
|
430
|
+
assert_contains "$out" "docs/cmux-backend.md" "ensure_running's denied error did not point at the setup docs"
|
|
431
|
+
assert_contains "$out" "Automation mode" "ensure_running's denied error did not name the recommended Automation mode"
|
|
432
|
+
assert_contains "$out" "Password mode" "ensure_running's denied error did not name the Password mode alternative"
|
|
433
|
+
assert_contains "$out" "Full open access" "ensure_running's denied error did not name (and caveat) Full open access"
|
|
434
|
+
pass "fm_backend_cmux_ensure_running: fails fast on a denied socket without attempting to launch, naming every viable mode"
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
test_ensure_running_fails_fast_on_unauth_without_launching() {
|
|
438
|
+
local dir fb out status
|
|
439
|
+
dir="$TMP_ROOT/ensure-unauth"; mkdir -p "$dir/responses"
|
|
440
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
441
|
+
cat > "$fb/open" <<'SH'
|
|
442
|
+
#!/usr/bin/env bash
|
|
443
|
+
echo "LAUNCHED" >> "${FM_CMUX_LAUNCH_MARKER:?}"
|
|
444
|
+
exit 0
|
|
445
|
+
SH
|
|
446
|
+
chmod +x "$fb/open"
|
|
447
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" FM_CMUX_FAKE_PING_EXIT=1 \
|
|
448
|
+
FM_CMUX_FAKE_PING="Error: ERROR: Authentication required - send auth <password> first" \
|
|
449
|
+
FM_CMUX_LAUNCH_MARKER="$dir/launched" \
|
|
450
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_ensure_running' "$ROOT" 2>&1 )
|
|
451
|
+
status=$?
|
|
452
|
+
[ "$status" -ne 0 ] || fail "ensure_running should refuse when the socket is unauthenticated (relaunching cannot fix a password problem)"
|
|
453
|
+
[ ! -f "$dir/launched" ] || fail "ensure_running should not attempt to launch cmux on an unauthenticated socket"
|
|
454
|
+
assert_contains "$out" "config/cmux-socket-password" "ensure_running's unauth error did not name the password config file"
|
|
455
|
+
assert_contains "$out" "Automation mode" "ensure_running's unauth error did not name the recommended no-password Automation mode"
|
|
456
|
+
assert_contains "$out" "docs/cmux-backend.md" "ensure_running's unauth error did not point at the setup docs"
|
|
457
|
+
pass "fm_backend_cmux_ensure_running: fails fast on an unauthenticated socket, naming the password config and the Automation mode alternative"
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
# --- create_task: duplicate refusal, id resolution ---------------------------
|
|
461
|
+
|
|
462
|
+
test_create_task_refuses_duplicate_label() {
|
|
463
|
+
local dir fb out status title
|
|
464
|
+
dir="$TMP_ROOT/dup-task"; mkdir -p "$dir/responses"
|
|
465
|
+
title=$(cmux_expected_scoped_title fm-dup1)
|
|
466
|
+
cmux_workspace_list_response "$dir" 1 "aaaaaaaa-0000-0000-0000-000000000000" "$title"
|
|
467
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
468
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
469
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_create_task fm-dup1 /tmp/proj' "$ROOT" 2>&1 )
|
|
470
|
+
status=$?
|
|
471
|
+
[ "$status" -ne 0 ] || fail "create_task should refuse an existing workspace title (cmux itself does not enforce uniqueness)"
|
|
472
|
+
assert_contains "$out" "already exists" "create_task did not report the duplicate name"
|
|
473
|
+
pass "fm_backend_cmux_create_task: refuses a duplicate workspace title (cmux's own new-workspace has no uniqueness check)"
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
test_create_task_creates_and_parses_ids() {
|
|
477
|
+
local dir fb out title
|
|
478
|
+
dir="$TMP_ROOT/create-task"; mkdir -p "$dir/responses"
|
|
479
|
+
title=$(cmux_expected_scoped_title fm-newtask)
|
|
480
|
+
# 1: workspace list --json (pre-create duplicate check) -> no match
|
|
481
|
+
printf '{"workspaces":[]}' > "$dir/responses/1.out"
|
|
482
|
+
# 2: new-workspace (silent on success)
|
|
483
|
+
# 3: workspace list --json (post-create id resolution) -> match
|
|
484
|
+
cmux_workspace_list_response "$dir" 3 "bbbbbbbb-1111-1111-1111-111111111111" "$title"
|
|
485
|
+
# 4: list-panes --json --id-format uuids -> default surface id
|
|
486
|
+
cmux_panes_response "$dir" 4 "cccccccc-2222-2222-2222-222222222222"
|
|
487
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
488
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
489
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_create_task fm-newtask /tmp/proj' "$ROOT" )
|
|
490
|
+
[ "$out" = "bbbbbbbb-1111-1111-1111-111111111111 cccccccc-2222-2222-2222-222222222222" ] \
|
|
491
|
+
|| fail "create_task should echo '<workspace_id> <surface_id>', got '$out'"
|
|
492
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''new-workspace'$'\x1f''--name'$'\x1f'"$title"$'\x1f''--cwd'$'\x1f''/tmp/proj' \
|
|
493
|
+
"create_task did not call new-workspace with the right name/cwd"
|
|
494
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''--focus'$'\x1f''false' \
|
|
495
|
+
"create_task did not pass --focus false"
|
|
496
|
+
pass "fm_backend_cmux_create_task: creates a workspace and parses workspace_id/surface_id from list responses"
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
# --- target_ready / capture ---------------------------------------------------
|
|
500
|
+
|
|
501
|
+
test_target_ready_fails_when_target_absent() {
|
|
502
|
+
local dir fb status
|
|
503
|
+
dir="$TMP_ROOT/ready-absent"; mkdir -p "$dir/responses"
|
|
504
|
+
# 1: list-panes --json --id-format uuids -> no panes at all (surface absent)
|
|
505
|
+
cmux_panes_empty_response "$dir" 1
|
|
506
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
507
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
508
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_target_ready "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT"
|
|
509
|
+
status=$?
|
|
510
|
+
[ "$status" -ne 0 ] || fail "target_ready should fail when list-panes reports the surface not found"
|
|
511
|
+
pass "fm_backend_cmux_target_ready: fails when the workspace/surface is not found (list-panes structural check)"
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
test_target_ready_checks_expected_label() {
|
|
515
|
+
local dir fb title
|
|
516
|
+
dir="$TMP_ROOT/ready-label-ok"; mkdir -p "$dir/responses"
|
|
517
|
+
title=$(cmux_expected_scoped_title fm-label)
|
|
518
|
+
cmux_workspace_list_response "$dir" 1 "aaaaaaaa-0000-0000-0000-000000000000" "$title"
|
|
519
|
+
# 2: list-panes --json --id-format uuids -> matching surface
|
|
520
|
+
cmux_panes_response "$dir" 2 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
521
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
522
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
523
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_target_ready "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" fm-label' "$ROOT"
|
|
524
|
+
expect_code 0 $? "target_ready should succeed when the workspace title matches the expected label"
|
|
525
|
+
cmux_assert_call_order "$dir/log" $'\x1f''workspace'$'\x1f''list' $'\x1f''list-panes' \
|
|
526
|
+
"target_ready did not check the label before list-panes"
|
|
527
|
+
pass "fm_backend_cmux_target_ready: verifies the workspace title against the expected label first"
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
test_target_ready_rejects_label_mismatch() {
|
|
531
|
+
local dir fb status
|
|
532
|
+
dir="$TMP_ROOT/ready-label-mismatch"; mkdir -p "$dir/responses"
|
|
533
|
+
cmux_workspace_list_response "$dir" 1 "aaaaaaaa-0000-0000-0000-000000000000" "not-the-task"
|
|
534
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
535
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
536
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_target_ready "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" fm-label' "$ROOT"
|
|
537
|
+
status=$?
|
|
538
|
+
[ "$status" -ne 0 ] || fail "target_ready should reject a workspace whose title does not match the expected label"
|
|
539
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''list-panes' \
|
|
540
|
+
"target_ready should not call list-panes after a label mismatch"
|
|
541
|
+
pass "fm_backend_cmux_target_ready: rejects a workspace id reused under a different title"
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
test_capture_trims_locally() {
|
|
545
|
+
local dir fb out
|
|
546
|
+
dir="$TMP_ROOT/capture"; mkdir -p "$dir/responses"
|
|
547
|
+
# 1: list-panes --json --id-format uuids (target_ready)
|
|
548
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
549
|
+
# 2: read-screen --scrollback --lines 200 --json (actual fetch)
|
|
550
|
+
cmux_read_screen_response "$dir" 2 $'line one\nline two\nline three\nline four'
|
|
551
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
552
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
553
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_capture "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" 2' "$ROOT" )
|
|
554
|
+
[ "$out" = $'line three\nline four' ] || fail "capture should trim to the last N lines locally, got '$out'"
|
|
555
|
+
cmux_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000' \
|
|
556
|
+
$'\x1f''--scrollback' "capture did not verify readiness before the actual read"
|
|
557
|
+
pass "fm_backend_cmux_capture: fetches generously and trims to N lines locally"
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
test_capture_fails_when_read_screen_fails_empty() {
|
|
561
|
+
local dir fb status
|
|
562
|
+
dir="$TMP_ROOT/capture-read-fail"; mkdir -p "$dir/responses"
|
|
563
|
+
# 1: list-panes --json --id-format uuids (target_ready)
|
|
564
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
565
|
+
# 2: read-screen exits nonzero with no stdout
|
|
566
|
+
printf '1' > "$dir/responses/2.exit"
|
|
567
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
568
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
569
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_capture "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" 5' "$ROOT"
|
|
570
|
+
status=$?
|
|
571
|
+
[ "$status" -ne 0 ] || fail "capture should fail when read-screen exits nonzero with no stdout"
|
|
572
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''read-screen' \
|
|
573
|
+
"capture should attempt read-screen after readiness succeeds"
|
|
574
|
+
pass "fm_backend_cmux_capture: propagates a read-screen failure even when stdout is empty"
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
test_capture_fails_when_target_not_ready() {
|
|
578
|
+
local dir fb status
|
|
579
|
+
dir="$TMP_ROOT/capture-not-ready"; mkdir -p "$dir/responses"
|
|
580
|
+
# 1: list-panes --json --id-format uuids -> no matching surface
|
|
581
|
+
cmux_panes_empty_response "$dir" 1
|
|
582
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
583
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
584
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_capture "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" 5' "$ROOT"
|
|
585
|
+
status=$?
|
|
586
|
+
[ "$status" -ne 0 ] || fail "capture should fail when the target is not ready"
|
|
587
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''--scrollback' \
|
|
588
|
+
"capture should not fetch after readiness fails"
|
|
589
|
+
pass "fm_backend_cmux_capture: fails when the target surface is absent"
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
# --- send_key / send_literal --------------------------------------------------
|
|
593
|
+
|
|
594
|
+
test_send_key_normalizes_and_targets() {
|
|
595
|
+
local dir fb
|
|
596
|
+
dir="$TMP_ROOT/sendkey"; mkdir -p "$dir/responses"
|
|
597
|
+
# 1: list-panes --json --id-format uuids (target_ready)
|
|
598
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
599
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
600
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
601
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_send_key "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" Escape' "$ROOT"
|
|
602
|
+
expect_code 0 $? "send_key should succeed"
|
|
603
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''send-key'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000'$'\x1f''--surface'$'\x1f''bbbbbbbb-1111-1111-1111-111111111111'$'\x1f''escape' \
|
|
604
|
+
"send_key did not normalize Escape to escape and target the explicit workspace/surface"
|
|
605
|
+
pass "fm_backend_cmux_send_key: normalizes the key (Escape -> escape) and targets the explicit workspace/surface"
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
test_send_key_recovers_stale_target_by_label() {
|
|
609
|
+
local dir fb title
|
|
610
|
+
dir="$TMP_ROOT/sendkey-stale-target"; mkdir -p "$dir/responses"
|
|
611
|
+
title=$(cmux_expected_scoped_title fm-label)
|
|
612
|
+
cmux_workspace_list_response "$dir" 1 "cccccccc-2222-2222-2222-222222222222" "$title"
|
|
613
|
+
cmux_workspace_list_response "$dir" 2 "cccccccc-2222-2222-2222-222222222222" "$title"
|
|
614
|
+
cmux_panes_response "$dir" 3 "dddddddd-3333-3333-3333-333333333333"
|
|
615
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
616
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
617
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_send_key "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" Enter fm-label' "$ROOT"
|
|
618
|
+
expect_code 0 $? "send_key should recover a stale cmux target when the expected label is live"
|
|
619
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''send-key'$'\x1f''--workspace'$'\x1f''cccccccc-2222-2222-2222-222222222222'$'\x1f''--surface'$'\x1f''dddddddd-3333-3333-3333-333333333333'$'\x1f''enter' \
|
|
620
|
+
"send_key did not use the refreshed cmux workspace/surface ids"
|
|
621
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''send-key'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000' \
|
|
622
|
+
"send_key should not target the stale cmux workspace id after label recovery"
|
|
623
|
+
pass "fm_backend_cmux_send_key: recovers stale workspace/surface ids by expected label"
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
test_send_literal_uses_separator_for_option_shaped_text() {
|
|
627
|
+
local dir fb
|
|
628
|
+
dir="$TMP_ROOT/sendliteral"; mkdir -p "$dir/responses"
|
|
629
|
+
# 1: list-panes --json --id-format uuids (target_ready)
|
|
630
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
631
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
632
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
633
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_send_literal "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" "--help"' "$ROOT"
|
|
634
|
+
expect_code 0 $? "send_literal should succeed"
|
|
635
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''send'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000'$'\x1f''--surface'$'\x1f''bbbbbbbb-1111-1111-1111-111111111111'$'\x1f''--'$'\x1f''--help' \
|
|
636
|
+
"send_literal did not call send with a -- separator before the literal payload"
|
|
637
|
+
pass "fm_backend_cmux_send_literal: calls send with an explicit workspace/surface and a -- separator"
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
test_send_text_line_clears_partial_input_when_enter_fails() {
|
|
641
|
+
local dir fb status log
|
|
642
|
+
dir="$TMP_ROOT/sendline-enter-failure"; mkdir -p "$dir/responses"
|
|
643
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
644
|
+
cmux_panes_response "$dir" 3 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
645
|
+
printf '1\n' > "$dir/responses/4.exit"
|
|
646
|
+
cmux_panes_response "$dir" 5 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
647
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
648
|
+
|
|
649
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
650
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_send_text_line "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" "export TRACEPARENT=carrier"' "$ROOT"
|
|
651
|
+
status=$?
|
|
652
|
+
[ "$status" -ne 0 ] || fail "send_text_line should report a failed Enter"
|
|
653
|
+
log=$(cat "$dir/log")
|
|
654
|
+
assert_contains "$log" $'\x1f''send'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000'$'\x1f''--surface'$'\x1f''bbbbbbbb-1111-1111-1111-111111111111'$'\x1f''--'$'\x1f''export TRACEPARENT=carrier' \
|
|
655
|
+
"send_text_line did not send the trace export before the simulated Enter failure"
|
|
656
|
+
assert_contains "$log" $'\x1f''send-key'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000'$'\x1f''--surface'$'\x1f''bbbbbbbb-1111-1111-1111-111111111111'$'\x1f''ctrl-c' \
|
|
657
|
+
"send_text_line did not clear the partial input after Enter failed"
|
|
658
|
+
pass "fm_backend_cmux_send_text_line: clears partial input when Enter fails"
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
test_send_text_line_reports_unsafe_input_when_cleanup_fails() {
|
|
662
|
+
local dir fb status log
|
|
663
|
+
dir="$TMP_ROOT/sendline-cleanup-failure"; mkdir -p "$dir/responses"
|
|
664
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
665
|
+
cmux_panes_response "$dir" 3 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
666
|
+
printf '1\n' > "$dir/responses/4.exit"
|
|
667
|
+
cmux_panes_response "$dir" 5 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
668
|
+
printf '1\n' > "$dir/responses/6.exit"
|
|
669
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
670
|
+
|
|
671
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
672
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_send_text_line "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" "export TRACEPARENT=carrier"' "$ROOT"
|
|
673
|
+
status=$?
|
|
674
|
+
expect_code 2 "$status" "send_text_line should distinguish uncleared input"
|
|
675
|
+
log=$(cat "$dir/log")
|
|
676
|
+
assert_contains "$log" $'\x1f''send-key'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000'$'\x1f''--surface'$'\x1f''bbbbbbbb-1111-1111-1111-111111111111'$'\x1f''ctrl-c' \
|
|
677
|
+
"send_text_line did not attempt cleanup after Enter failed"
|
|
678
|
+
pass "fm_backend_cmux_send_text_line: reports unsafe input when cleanup also fails"
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
# --- current_path: pwd-marker-probe (zellij-shape) ---------------------------
|
|
682
|
+
|
|
683
|
+
test_current_path_probes_with_marker() {
|
|
684
|
+
local dir fb out
|
|
685
|
+
# Verified real-cmux pitfall (docs/cmux-backend.md finding #2): the surface's
|
|
686
|
+
# cwd is frozen at creation time (the top-level shell's cwd), never following
|
|
687
|
+
# a foreground subshell (e.g. treehouse get) - so current_path actively
|
|
688
|
+
# prints a marked cwd line and reads only that marker from the capture.
|
|
689
|
+
dir="$TMP_ROOT/cwd"; mkdir -p "$dir/responses"
|
|
690
|
+
# 1: list-panes (current_path's own target_ready)
|
|
691
|
+
# 2: list-panes (target_ready, called by send_text_line->send_literal)
|
|
692
|
+
# 3: send (literal probe text)
|
|
693
|
+
# 4: list-panes (target_ready, called by send_text_line->send_key)
|
|
694
|
+
# 5: send-key enter
|
|
695
|
+
# 6: list-panes (target_ready, called by capture)
|
|
696
|
+
# 7: read-screen --scrollback --lines 200 --json (actual fetch)
|
|
697
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
698
|
+
cmux_panes_response "$dir" 2 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
699
|
+
cmux_panes_response "$dir" 4 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
700
|
+
cmux_panes_response "$dir" 6 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
701
|
+
cmux_read_screen_response "$dir" 7 $'/tmp/proj\n❯ printf marker\n__FM_CMUX_CWD_BEGIN__\n/home/fixture/.treehouse/fake-worktree\n__FM_CMUX_CWD_END__\n/home/fixture/.treehouse/fake-worktree ❯'
|
|
702
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
703
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
704
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_current_path "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT" )
|
|
705
|
+
[ "$out" = "/home/fixture/.treehouse/fake-worktree" ] || fail "current_path should read only the marked cwd line, got '$out'"
|
|
706
|
+
assert_contains "$(cat "$dir/log")" "__FM_CMUX_CWD_BEGIN__" "current_path did not send the cwd begin marker"
|
|
707
|
+
assert_contains "$(cat "$dir/log")" "pwd;" "current_path did not send the pwd probe"
|
|
708
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''send-key'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000'$'\x1f''--surface'$'\x1f''bbbbbbbb-1111-1111-1111-111111111111'$'\x1f''enter' \
|
|
709
|
+
"current_path did not submit the cwd probe with Enter"
|
|
710
|
+
pass "fm_backend_cmux_current_path: actively probes with marked begin/end lines (zellij-shape frozen cwd)"
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
# --- composer_state: structural border-row classification (adapted from herdr) ----
|
|
714
|
+
|
|
715
|
+
test_composer_state_bare_prompt_is_empty() {
|
|
716
|
+
local dir fb out
|
|
717
|
+
dir="$TMP_ROOT/composer-bare"; mkdir -p "$dir/responses"
|
|
718
|
+
# 1: list-panes (target_ready via capture)
|
|
719
|
+
# 2: read-screen --scrollback --lines <N> --json (composer capture)
|
|
720
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
721
|
+
cmux_read_screen_response "$dir" 2 $' ╭────────────────────────╮\n │ ❯ │\n ╰──────── Composer ──────╯\n\n Enter:send'
|
|
722
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
723
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
724
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_composer_state "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT" )
|
|
725
|
+
[ "$out" = empty ] || fail "a bare prompt glyph should read as empty, got '$out'"
|
|
726
|
+
pass "fm_backend_cmux_composer_state: a bare '❯' composer row reads empty"
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
test_composer_state_borderless_claude_prompt_is_empty() {
|
|
730
|
+
local dir fb out
|
|
731
|
+
dir="$TMP_ROOT/composer-borderless-claude"; mkdir -p "$dir/responses"
|
|
732
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
733
|
+
cmux_read_screen_response "$dir" 2 $'────────────────────────\n❯\n────────────────────────\nHaiku 4.5'
|
|
734
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
735
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
736
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_composer_state "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT" )
|
|
737
|
+
[ "$out" = empty ] || fail "a borderless Claude '❯' row bounded by horizontal rules should read empty, got '$out'"
|
|
738
|
+
pass "fm_backend_cmux_composer_state: a borderless Claude '❯' composer row reads empty"
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
test_composer_state_borderless_claude_prompt_outranks_stale_bordered_row() {
|
|
742
|
+
local dir fb out
|
|
743
|
+
dir="$TMP_ROOT/composer-borderless-claude-after-bordered"; mkdir -p "$dir/responses"
|
|
744
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
745
|
+
cmux_read_screen_response "$dir" 2 $'│ ❯ stale input │\n────────────────────────\n❯\n────────────────────────\nHaiku 4.5'
|
|
746
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
747
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
748
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_composer_state "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT" )
|
|
749
|
+
[ "$out" = empty ] || fail "a current borderless Claude row should outrank stale bordered scrollback, got '$out'"
|
|
750
|
+
pass "fm_backend_cmux_composer_state: a borderless Claude row outranks stale bordered scrollback"
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
test_composer_state_borderless_claude_nbsp_prompt_is_empty() {
|
|
754
|
+
local dir fb out
|
|
755
|
+
dir="$TMP_ROOT/composer-borderless-claude-nbsp"; mkdir -p "$dir/responses"
|
|
756
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
757
|
+
cmux_read_screen_response "$dir" 2 $'────────────────────────\n❯\302\240\n────────────────────────\nHaiku 4.5'
|
|
758
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
759
|
+
out=$( LC_ALL=C PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
760
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_composer_state "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT" )
|
|
761
|
+
[ "$out" = empty ] || fail "a borderless Claude '❯'+NBSP row bounded by horizontal rules should read empty under LC_ALL=C, got '$out'"
|
|
762
|
+
pass "fm_backend_cmux_composer_state: a borderless Claude '❯'+NBSP composer row reads empty under LC_ALL=C"
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
test_composer_state_borderless_claude_text_is_unknown_plain() {
|
|
766
|
+
# Capability degradation (the consolidated classifier's styled=0 rule): on
|
|
767
|
+
# cmux's plain-text capture, text after a bare agent glyph is unreadable -
|
|
768
|
+
# it may be the harness's own idle suggestion (claude's rotating dim hint,
|
|
769
|
+
# codex's "Use /skills ..."), which a plain read cannot tell from typed
|
|
770
|
+
# input. The verdict is `unknown` (defer, loud refusal at fm-send), never a
|
|
771
|
+
# false `pending` that would misreport an idle pane as holding unsent text.
|
|
772
|
+
# The same bytes on a styled backend (tmux/herdr/zellij) classify pending
|
|
773
|
+
# when bright and empty when ghost - pinned in tests/fm-composer-lib.test.sh.
|
|
774
|
+
local dir fb out
|
|
775
|
+
dir="$TMP_ROOT/composer-borderless-claude-text"; mkdir -p "$dir/responses"
|
|
776
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
777
|
+
cmux_read_screen_response "$dir" 2 $'────────────────────────\n❯ retain this message\n────────────────────────\nHaiku 4.5'
|
|
778
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
779
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
780
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_composer_state "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT" )
|
|
781
|
+
[ "$out" = unknown ] || fail "plain-capture text after a bare glyph must degrade to unknown, got '$out'"
|
|
782
|
+
pass "fm_backend_cmux_composer_state: plain-capture text after a bare glyph degrades to unknown (never false pending)"
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
test_composer_state_ghost_placeholder_is_empty() {
|
|
786
|
+
local dir fb out
|
|
787
|
+
dir="$TMP_ROOT/composer-ghost"; mkdir -p "$dir/responses"
|
|
788
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
789
|
+
cmux_read_screen_response "$dir" 2 $' ╭────────────────────────╮\n │ ❯ Type a message... │\n ╰──────── Composer ──────╯'
|
|
790
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
791
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
792
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_composer_state "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT" )
|
|
793
|
+
[ "$out" = empty ] || fail "the known ghost placeholder 'Type a message...' should read as empty, got '$out'"
|
|
794
|
+
pass "fm_backend_cmux_composer_state: the ghost placeholder text reads empty, not pending"
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
test_composer_state_real_text_is_pending() {
|
|
798
|
+
local dir fb out
|
|
799
|
+
dir="$TMP_ROOT/composer-pending"; mkdir -p "$dir/responses"
|
|
800
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
801
|
+
cmux_read_screen_response "$dir" 2 $' ╭────────────────────────╮\n │ ❯ hello captain │\n ╰──────── Composer ──────╯\n\n Enter:send'
|
|
802
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
803
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
804
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_composer_state "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT" )
|
|
805
|
+
[ "$out" = pending ] || fail "real unsubmitted text should read as pending, got '$out'"
|
|
806
|
+
pass "fm_backend_cmux_composer_state: real composer text reads pending"
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
# The popup-placeholder/second-Enter regression class (2026-07-03 herdr
|
|
810
|
+
# incident, docs/herdr-backend.md): a slash command's first Enter can close a
|
|
811
|
+
# completion popup and EXPAND the composer into an argument-hint placeholder
|
|
812
|
+
# rather than submitting. A raw content-diff check would misread the popup
|
|
813
|
+
# vanishing as "submitted"; the structural composer-row read must still call
|
|
814
|
+
# this pending so the caller retries a genuine second Enter.
|
|
815
|
+
test_composer_state_popup_placeholder_fill_is_pending() {
|
|
816
|
+
local dir fb out
|
|
817
|
+
dir="$TMP_ROOT/composer-popup-placeholder"; mkdir -p "$dir/responses"
|
|
818
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
819
|
+
cmux_read_screen_response "$dir" 2 $' ╭──────────────────────────────────────╮\n │ ❯ /compact compaction instructions │\n ╰──────────────── Composer ────────────╯\n\n Enter:send'
|
|
820
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
821
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
822
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_composer_state "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT" )
|
|
823
|
+
[ "$out" = pending ] || fail "a popup-close-with-placeholder-fill must still read as pending (not yet submitted), got '$out'"
|
|
824
|
+
pass "fm_backend_cmux_composer_state: a slash-command popup's argument-hint placeholder still reads pending (the incident fix)"
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
test_composer_state_unknown_on_capture_failure() {
|
|
828
|
+
local dir fb out status
|
|
829
|
+
dir="$TMP_ROOT/composer-capture-fail"; mkdir -p "$dir/responses"
|
|
830
|
+
printf '1\n' > "$dir/responses/1.exit"
|
|
831
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
832
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
833
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_composer_state "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT" )
|
|
834
|
+
status=$?
|
|
835
|
+
[ "$status" -eq 0 ] || fail "composer_state should not itself fail the caller"
|
|
836
|
+
[ "$out" = unknown ] || fail "an unreadable surface should read as unknown, got '$out'"
|
|
837
|
+
pass "fm_backend_cmux_composer_state: reports unknown when the surface cannot be captured"
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
test_composer_state_unknown_when_no_composer_row_found() {
|
|
841
|
+
local dir fb out
|
|
842
|
+
dir="$TMP_ROOT/composer-no-row"; mkdir -p "$dir/responses"
|
|
843
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
844
|
+
cmux_read_screen_response "$dir" 2 'plain-shell-prompt$ '
|
|
845
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
846
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
847
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_composer_state "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT" )
|
|
848
|
+
[ "$out" = unknown ] || fail "a capture with no recognizable composer row should read as unknown, got '$out'"
|
|
849
|
+
pass "fm_backend_cmux_composer_state: reports unknown when no border-delimited composer row is found"
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
# --- send_text_submit: structural composer-row verify-and-retry --------------
|
|
853
|
+
|
|
854
|
+
test_send_text_submit_detects_landed_send() {
|
|
855
|
+
local dir fb out
|
|
856
|
+
dir="$TMP_ROOT/submit-ok"; mkdir -p "$dir/responses"
|
|
857
|
+
# 1: list-panes (target_ready via send_literal)
|
|
858
|
+
# 2: send (literal text)
|
|
859
|
+
# 3: list-panes (target_ready via send_key Enter)
|
|
860
|
+
# 4: send-key enter
|
|
861
|
+
# 5: list-panes (target_ready via composer_state's capture)
|
|
862
|
+
# 6: read-screen --scrollback --lines N --json -> composer reads empty (submitted)
|
|
863
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
864
|
+
cmux_panes_response "$dir" 3 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
865
|
+
cmux_panes_response "$dir" 5 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
866
|
+
cmux_read_screen_response "$dir" 6 $' ╭────────────────────────╮\n │ ❯ │\n ╰──────── Composer ──────╯'
|
|
867
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
868
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
869
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_send_text_submit "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" "hello captain" 3 0.01 0.01' "$ROOT" )
|
|
870
|
+
[ "$out" = empty ] || fail "send_text_submit should report empty (submitted) once the composer row reads empty, got '$out'"
|
|
871
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''send'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000'$'\x1f''--surface'$'\x1f''bbbbbbbb-1111-1111-1111-111111111111'$'\x1f''--'$'\x1f''hello captain' \
|
|
872
|
+
"send_text_submit did not type the literal text first"
|
|
873
|
+
enter_count=$(grep -c $'\x1f''send-key'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000'$'\x1f''--surface'$'\x1f''bbbbbbbb-1111-1111-1111-111111111111'$'\x1f''enter' "$dir/log")
|
|
874
|
+
[ "$enter_count" -eq 1 ] || fail "send_text_submit should not need a second Enter for a plain message with no popup, sent $enter_count Enter(s)"
|
|
875
|
+
pass "fm_backend_cmux_send_text_submit: reports 'empty' once the composer row reads empty after one Enter"
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
test_send_text_submit_detects_swallowed_enter() {
|
|
879
|
+
local dir fb out
|
|
880
|
+
dir="$TMP_ROOT/submit-swallow"; mkdir -p "$dir/responses"
|
|
881
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
882
|
+
cmux_panes_response "$dir" 3 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
883
|
+
cmux_panes_response "$dir" 5 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
884
|
+
cmux_panes_response "$dir" 7 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
885
|
+
cmux_panes_response "$dir" 9 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
886
|
+
cmux_read_screen_response "$dir" 6 $' ╭────────────────────────╮\n │ ❯ hello captain │\n ╰──────── Composer ──────╯\n\n Enter:send'
|
|
887
|
+
cmux_read_screen_response "$dir" 10 $' ╭────────────────────────╮\n │ ❯ hello captain │\n ╰──────── Composer ──────╯\n\n Enter:send'
|
|
888
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
889
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
890
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_send_text_submit "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" "hello captain" 2 0.01 0.01' "$ROOT" )
|
|
891
|
+
[ "$out" = pending ] || fail "send_text_submit should report pending once retries are exhausted with no visible change, got '$out'"
|
|
892
|
+
pass "fm_backend_cmux_send_text_submit: reports 'pending' when the composer never clears after retried Enters (swallowed)"
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
# The regression test for the popup-placeholder/second-Enter class (mirrors
|
|
896
|
+
# herdr's 2026-07-03 incident test): Enter #1 closes the popup and fills an
|
|
897
|
+
# argument-hint placeholder (still pending); Enter #2 actually submits. The
|
|
898
|
+
# adapter must retry past the first Enter instead of declaring victory on a
|
|
899
|
+
# raw content change, and must actually issue the second Enter.
|
|
900
|
+
test_send_text_submit_popup_autocomplete_requires_second_enter() {
|
|
901
|
+
local dir fb out
|
|
902
|
+
dir="$TMP_ROOT/submit-popup-autocomplete"; mkdir -p "$dir/responses"
|
|
903
|
+
# 1: list-panes (target_ready via send_literal)
|
|
904
|
+
# 2: send "/compact"
|
|
905
|
+
# 3: list-panes (target_ready via send_key Enter #1)
|
|
906
|
+
# 4: send-key enter (#1) - closes the popup, fills the placeholder
|
|
907
|
+
# 5: list-panes (target_ready via composer_state capture)
|
|
908
|
+
# 6: composer still reads real (pending) text
|
|
909
|
+
cmux_panes_response "$dir" 1 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
910
|
+
cmux_panes_response "$dir" 3 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
911
|
+
cmux_panes_response "$dir" 5 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
912
|
+
cmux_read_screen_response "$dir" 6 $' ╭──────────────────────────────────────╮\n │ ❯ /compact compaction instructions │\n ╰──────────────── Composer ────────────╯\n\n Enter:send'
|
|
913
|
+
# 7: list-panes (target_ready via send_key Enter #2)
|
|
914
|
+
# 8: send-key enter (#2) - actually submits
|
|
915
|
+
# 9: list-panes (target_ready via composer_state capture)
|
|
916
|
+
# 10: composer now reads empty
|
|
917
|
+
cmux_panes_response "$dir" 7 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
918
|
+
cmux_panes_response "$dir" 9 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
919
|
+
cmux_read_screen_response "$dir" 10 $' ╭────────────────────────╮\n │ ❯ │\n ╰──────── Composer ──────╯'
|
|
920
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
921
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
922
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_send_text_submit "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" "/compact" 3 0.01 0.01' "$ROOT" )
|
|
923
|
+
[ "$out" = empty ] || fail "send_text_submit should eventually report empty once the SECOND Enter actually clears the composer, got '$out'"
|
|
924
|
+
enter_count=$(grep -c $'\x1f''send-key'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000'$'\x1f''--surface'$'\x1f''bbbbbbbb-1111-1111-1111-111111111111'$'\x1f''enter' "$dir/log")
|
|
925
|
+
[ "$enter_count" -eq 2 ] || fail "send_text_submit should have sent exactly 2 Enters (popup-close, then real submit), sent $enter_count"
|
|
926
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''send'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000'$'\x1f''--surface'$'\x1f''bbbbbbbb-1111-1111-1111-111111111111'$'\x1f''--'$'\x1f''/compact compaction instructions' \
|
|
927
|
+
"send_text_submit should never retype - only retry Enter"
|
|
928
|
+
pass "fm_backend_cmux_send_text_submit: retries past a popup-placeholder-fill Enter and lands the real second Enter (the incident fix)"
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
test_send_text_submit_send_failed_when_target_absent() {
|
|
932
|
+
local dir fb out
|
|
933
|
+
dir="$TMP_ROOT/submit-no-target"; mkdir -p "$dir/responses"
|
|
934
|
+
printf '1\n' > "$dir/responses/1.exit"
|
|
935
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
936
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
937
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_send_text_submit "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" "x" 2 0.01 0.01' "$ROOT" )
|
|
938
|
+
[ "$out" = send-failed ] || fail "send_text_submit should report send-failed when the target is absent, got '$out'"
|
|
939
|
+
pass "fm_backend_cmux_send_text_submit: reports 'send-failed' when the target workspace/surface is absent"
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
# --- window_of_workspace: which window holds a workspace, and its count ------
|
|
943
|
+
|
|
944
|
+
test_window_of_workspace_finds_window_and_count() {
|
|
945
|
+
local dir fb out
|
|
946
|
+
dir="$TMP_ROOT/win-of-ws"; mkdir -p "$dir/responses"
|
|
947
|
+
# 1: list-windows --json -> two windows
|
|
948
|
+
cmux_windows_response "$dir" 1 "e1111111-0000-0000-0000-000000000000" 2 "e2222222-0000-0000-0000-000000000000" 2
|
|
949
|
+
# 2: workspace list --window e1111111 -> does NOT contain the target
|
|
950
|
+
cmux_workspace_list_response "$dir" 2 "ffffffff-0000-0000-0000-000000000000" "other"
|
|
951
|
+
# 3: workspace list --window e2222222 -> contains the target
|
|
952
|
+
cmux_workspace_list_response "$dir" 3 "aaaaaaaa-0000-0000-0000-000000000000" "the-task"
|
|
953
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
954
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
955
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_window_of_workspace "aaaaaaaa-0000-0000-0000-000000000000"' "$ROOT" )
|
|
956
|
+
[ "$out" = "e2222222-0000-0000-0000-000000000000 1" ] \
|
|
957
|
+
|| fail "window_of_workspace should echo the owning window and its matched-list count, got '$out'"
|
|
958
|
+
cmux_assert_call_order "$dir/log" $'\x1f''list-windows' $'\x1f''workspace'$'\x1f''list'$'\x1f''--json'$'\x1f''--id-format'$'\x1f''uuids'$'\x1f''--window'$'\x1f''e1111111-0000-0000-0000-000000000000' \
|
|
959
|
+
"window_of_workspace did not list windows before scanning per-window workspaces"
|
|
960
|
+
pass "fm_backend_cmux_window_of_workspace: walks windows and counts the membership-confirming workspace list"
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
test_window_of_workspace_empty_when_not_found() {
|
|
964
|
+
local dir fb out
|
|
965
|
+
dir="$TMP_ROOT/win-of-ws-none"; mkdir -p "$dir/responses"
|
|
966
|
+
cmux_windows_response "$dir" 1 "e1111111-0000-0000-0000-000000000000" 1
|
|
967
|
+
cmux_workspace_list_response "$dir" 2 "ffffffff-0000-0000-0000-000000000000" "other"
|
|
968
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
969
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
970
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_window_of_workspace "aaaaaaaa-0000-0000-0000-000000000000"' "$ROOT" )
|
|
971
|
+
[ -z "$out" ] || fail "window_of_workspace should echo nothing when the workspace is not found, got '$out'"
|
|
972
|
+
pass "fm_backend_cmux_window_of_workspace: echoes nothing when no window holds the workspace"
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
# --- kill: close the task workspace, adding a sibling when it is the last one -
|
|
976
|
+
|
|
977
|
+
# The common case: the task workspace shares its window with at least one other
|
|
978
|
+
# workspace, so cmux closes it directly with no sibling dance.
|
|
979
|
+
test_kill_closes_workspace_directly_when_not_last() {
|
|
980
|
+
local dir fb
|
|
981
|
+
dir="$TMP_ROOT/kill-workspace"; mkdir -p "$dir/responses"
|
|
982
|
+
# 1: list-windows -> the owning window has 2 workspaces (target is NOT last)
|
|
983
|
+
cmux_windows_response "$dir" 1 "eeeeeeee-0000-0000-0000-000000000000" 2
|
|
984
|
+
# 2: workspace list --window eeeeeeee -> contains the target
|
|
985
|
+
cmux_workspace_list_response "$dir" 2 "aaaaaaaa-0000-0000-0000-000000000000" "the-task" "ffffffff-0000-0000-0000-000000000000" "other"
|
|
986
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
987
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
988
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_kill "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT"
|
|
989
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''close-workspace'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000' \
|
|
990
|
+
"kill did not close the task workspace"
|
|
991
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''new-workspace' \
|
|
992
|
+
"kill should not add a sibling workspace when the target is not the last one in its window"
|
|
993
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''close-surface' \
|
|
994
|
+
"kill should close the whole workspace directly"
|
|
995
|
+
pass "fm_backend_cmux_kill: closes the task workspace directly when it is not the last in its window"
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
# The selected-workspace teardown bug: cmux refuses to close the only workspace
|
|
999
|
+
# in a window (returns OK but no-ops), so kill first creates a throwaway sibling
|
|
1000
|
+
# and only then closes the target - which now succeeds.
|
|
1001
|
+
test_kill_adds_sibling_when_last_in_window() {
|
|
1002
|
+
local dir fb
|
|
1003
|
+
dir="$TMP_ROOT/kill-last-in-window"; mkdir -p "$dir/responses"
|
|
1004
|
+
cmux_windows_response "$dir" 1 "eeeeeeee-0000-0000-0000-000000000000" 2
|
|
1005
|
+
# 2: workspace list --window eeeeeeee -> contains the target
|
|
1006
|
+
cmux_workspace_list_response "$dir" 2 "aaaaaaaa-0000-0000-0000-000000000000" "the-task"
|
|
1007
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
1008
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
1009
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_kill "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT"
|
|
1010
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''new-workspace'$'\x1f''--window'$'\x1f''eeeeeeee-0000-0000-0000-000000000000'$'\x1f''--focus'$'\x1f''false' \
|
|
1011
|
+
"kill did not add a throwaway sibling in the target's own window before closing the last workspace"
|
|
1012
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''new-workspace'$'\x1f''--name' \
|
|
1013
|
+
"the throwaway sibling must stay an unnamed default workspace, never an fm- task title"
|
|
1014
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''close-workspace'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000' \
|
|
1015
|
+
"kill did not close the target workspace after adding the sibling"
|
|
1016
|
+
cmux_assert_call_order "$dir/log" $'\x1f''new-workspace'$'\x1f''--window' $'\x1f''close-workspace'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000' \
|
|
1017
|
+
"kill must add the sibling BEFORE closing the last workspace, or the close still no-ops"
|
|
1018
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''close-surface' \
|
|
1019
|
+
"kill should not call close-surface"
|
|
1020
|
+
pass "fm_backend_cmux_kill: adds a throwaway sibling then closes the target when it is the last workspace in its window"
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
test_kill_is_best_effort_when_close_workspace_fails() {
|
|
1024
|
+
local dir fb
|
|
1025
|
+
dir="$TMP_ROOT/kill-workspace-fail"; mkdir -p "$dir/responses"
|
|
1026
|
+
# 1: list-windows (not last), 2: workspace list --window, 3: close-workspace fails
|
|
1027
|
+
cmux_windows_response "$dir" 1 "eeeeeeee-0000-0000-0000-000000000000" 2
|
|
1028
|
+
cmux_workspace_list_response "$dir" 2 "aaaaaaaa-0000-0000-0000-000000000000" "the-task" "ffffffff-0000-0000-0000-000000000000" "other"
|
|
1029
|
+
printf '1\n' > "$dir/responses/3.exit"
|
|
1030
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
1031
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
1032
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_kill "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111"' "$ROOT"
|
|
1033
|
+
expect_code 0 $? "kill must stay best-effort (never fail) even when close-workspace fails"
|
|
1034
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''close-workspace'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000' \
|
|
1035
|
+
"kill should still attempt close-workspace"
|
|
1036
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''close-surface' \
|
|
1037
|
+
"kill should not call close-surface"
|
|
1038
|
+
pass "fm_backend_cmux_kill: never fails even when close-workspace fails"
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
test_kill_recovers_stale_target_by_label() {
|
|
1042
|
+
local dir fb title
|
|
1043
|
+
dir="$TMP_ROOT/kill-stale-target"; mkdir -p "$dir/responses"
|
|
1044
|
+
title=$(cmux_expected_scoped_title fm-label)
|
|
1045
|
+
# target_ready label recovery: 1 workspace list (title lookup, misses stale id),
|
|
1046
|
+
# 2 workspace list (id-for-label -> refreshed id), 3 list-panes (surface id).
|
|
1047
|
+
cmux_workspace_list_response "$dir" 1 "cccccccc-2222-2222-2222-222222222222" "$title"
|
|
1048
|
+
cmux_workspace_list_response "$dir" 2 "cccccccc-2222-2222-2222-222222222222" "$title"
|
|
1049
|
+
cmux_panes_response "$dir" 3 "dddddddd-3333-3333-3333-333333333333"
|
|
1050
|
+
# window_of_workspace on the REFRESHED id: 4 list-windows (not last), 5 workspace list --window.
|
|
1051
|
+
cmux_windows_response "$dir" 4 "eeeeeeee-0000-0000-0000-000000000000" 2
|
|
1052
|
+
cmux_workspace_list_response "$dir" 5 "cccccccc-2222-2222-2222-222222222222" "$title" "ffffffff-0000-0000-0000-000000000000" "other"
|
|
1053
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
1054
|
+
PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
1055
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_kill "aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111" "" fm-label' "$ROOT"
|
|
1056
|
+
expect_code 0 $? "kill should recover a stale cmux target when the expected label is live"
|
|
1057
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''close-workspace'$'\x1f''--workspace'$'\x1f''cccccccc-2222-2222-2222-222222222222' \
|
|
1058
|
+
"kill did not use the refreshed cmux workspace/surface ids"
|
|
1059
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''close-workspace'$'\x1f''--workspace'$'\x1f''aaaaaaaa-0000-0000-0000-000000000000' \
|
|
1060
|
+
"kill should not target the stale cmux workspace id after label recovery"
|
|
1061
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''close-surface' \
|
|
1062
|
+
"kill should not call close-surface"
|
|
1063
|
+
pass "fm_backend_cmux_kill: recovers stale workspace/surface ids by expected label"
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
# --- list_live: label-based orphan discovery ---------------------------------
|
|
1067
|
+
|
|
1068
|
+
test_list_live_filters_by_title_prefix() {
|
|
1069
|
+
local dir fb out title other_title other_root
|
|
1070
|
+
dir="$TMP_ROOT/list-live"; mkdir -p "$dir/responses"
|
|
1071
|
+
other_root="$dir/other-root"; mkdir -p "$other_root"
|
|
1072
|
+
title=$(cmux_expected_scoped_title fm-task1)
|
|
1073
|
+
other_title=$(cmux_expected_scoped_title fm-task2 "$ROOT" "$other_root")
|
|
1074
|
+
# 1: workspace list --json --id-format uuids -> one in-home task, two unrelated
|
|
1075
|
+
cmux_workspace_list_response "$dir" 1 \
|
|
1076
|
+
"aaaaaaaa-0000-0000-0000-000000000000" "$title" \
|
|
1077
|
+
"dddddddd-8888-8888-8888-888888888888" "$other_title" \
|
|
1078
|
+
"cccccccc-9999-9999-9999-999999999999" "zsh"
|
|
1079
|
+
# 2: list-panes for this home's task1 workspace
|
|
1080
|
+
cmux_panes_response "$dir" 2 "bbbbbbbb-1111-1111-1111-111111111111"
|
|
1081
|
+
fb=$(make_cmux_fakebin "$dir")
|
|
1082
|
+
out=$( PATH="$fb:$PATH" FM_CMUX_LOG="$dir/log" FM_CMUX_RESPONSES="$dir/responses" \
|
|
1083
|
+
bash -c '. "$0/bin/backends/cmux.sh"; fm_backend_cmux_list_live' "$ROOT" )
|
|
1084
|
+
[ "$out" = $'aaaaaaaa-0000-0000-0000-000000000000:bbbbbbbb-1111-1111-1111-111111111111\tfm-task1' ] \
|
|
1085
|
+
|| fail "list_live should list only the in-home task workspace with its plain label and surface id, got '$out'"
|
|
1086
|
+
pass "fm_backend_cmux_list_live: lists only this home's scoped task workspaces using plain fm-<id> labels"
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
# --- fm-spawn.sh: --secondmate refuses backend=cmux --------------------------
|
|
1090
|
+
|
|
1091
|
+
test_secondmate_spawn_refuses_cmux_backend() {
|
|
1092
|
+
local dir state data config projects out status
|
|
1093
|
+
dir="$TMP_ROOT/secondmate-refuse"; state="$dir/state"; data="$dir/data"; config="$dir/config"; projects="$dir/projects"
|
|
1094
|
+
mkdir -p "$state" "$data" "$config" "$projects"
|
|
1095
|
+
out=$( FM_STATE_OVERRIDE="$state" FM_DATA_OVERRIDE="$data" FM_CONFIG_OVERRIDE="$config" FM_PROJECTS_OVERRIDE="$projects" \
|
|
1096
|
+
"$ROOT/bin/fm-spawn.sh" sm-cmux-test --secondmate --backend cmux 2>&1 )
|
|
1097
|
+
status=$?
|
|
1098
|
+
[ "$status" -ne 0 ] || fail "fm-spawn.sh should refuse a --secondmate spawn with --backend cmux"
|
|
1099
|
+
assert_contains "$out" "does not support --secondmate" "fm-spawn.sh did not report the cmux secondmate refusal"
|
|
1100
|
+
pass "fm-spawn.sh: refuses backend=cmux for --secondmate spawns (mirrors Orca's refusal; no secondmate launch design exists yet)"
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
# shellcheck source=/dev/null
|
|
1104
|
+
. "$ROOT/bin/fm-backend.sh"
|
|
1105
|
+
|
|
1106
|
+
test_version_check_accepts_current_version
|
|
1107
|
+
test_version_check_accepts_newer_version
|
|
1108
|
+
test_version_check_refuses_old_version
|
|
1109
|
+
test_version_check_refuses_missing_cmux
|
|
1110
|
+
test_password_reads_from_config_file
|
|
1111
|
+
test_password_preserves_config_file_whitespace
|
|
1112
|
+
test_password_respects_config_override
|
|
1113
|
+
test_password_empty_when_config_absent
|
|
1114
|
+
test_cli_exports_password_only_when_configured
|
|
1115
|
+
test_parse_target
|
|
1116
|
+
test_normalize_key
|
|
1117
|
+
test_scoped_title_uses_primary_home_label
|
|
1118
|
+
test_scoped_title_uses_secondmate_home_label
|
|
1119
|
+
test_scoped_title_changes_with_root_path
|
|
1120
|
+
test_dispatch_routes_cmux_backend
|
|
1121
|
+
test_dispatch_busy_state_unknown_for_cmux
|
|
1122
|
+
test_dispatch_composer_state_routes_cmux
|
|
1123
|
+
test_ping_state_ok
|
|
1124
|
+
test_ping_state_denied
|
|
1125
|
+
test_ping_state_unauth
|
|
1126
|
+
test_ping_state_invalid_password
|
|
1127
|
+
test_ping_state_down
|
|
1128
|
+
test_ensure_running_returns_immediately_when_already_ok
|
|
1129
|
+
test_ensure_running_fails_fast_on_denied_without_launching
|
|
1130
|
+
test_ensure_running_fails_fast_on_unauth_without_launching
|
|
1131
|
+
test_create_task_refuses_duplicate_label
|
|
1132
|
+
test_create_task_creates_and_parses_ids
|
|
1133
|
+
test_target_ready_fails_when_target_absent
|
|
1134
|
+
test_target_ready_checks_expected_label
|
|
1135
|
+
test_target_ready_rejects_label_mismatch
|
|
1136
|
+
test_capture_trims_locally
|
|
1137
|
+
test_capture_fails_when_read_screen_fails_empty
|
|
1138
|
+
test_capture_fails_when_target_not_ready
|
|
1139
|
+
test_send_key_normalizes_and_targets
|
|
1140
|
+
test_send_key_recovers_stale_target_by_label
|
|
1141
|
+
test_send_literal_uses_separator_for_option_shaped_text
|
|
1142
|
+
test_send_text_line_clears_partial_input_when_enter_fails
|
|
1143
|
+
test_send_text_line_reports_unsafe_input_when_cleanup_fails
|
|
1144
|
+
test_current_path_probes_with_marker
|
|
1145
|
+
test_composer_state_bare_prompt_is_empty
|
|
1146
|
+
test_composer_state_borderless_claude_prompt_is_empty
|
|
1147
|
+
test_composer_state_borderless_claude_prompt_outranks_stale_bordered_row
|
|
1148
|
+
test_composer_state_borderless_claude_nbsp_prompt_is_empty
|
|
1149
|
+
test_composer_state_borderless_claude_text_is_unknown_plain
|
|
1150
|
+
test_composer_state_ghost_placeholder_is_empty
|
|
1151
|
+
test_composer_state_real_text_is_pending
|
|
1152
|
+
test_composer_state_popup_placeholder_fill_is_pending
|
|
1153
|
+
test_composer_state_unknown_on_capture_failure
|
|
1154
|
+
test_composer_state_unknown_when_no_composer_row_found
|
|
1155
|
+
test_send_text_submit_detects_landed_send
|
|
1156
|
+
test_send_text_submit_detects_swallowed_enter
|
|
1157
|
+
test_send_text_submit_popup_autocomplete_requires_second_enter
|
|
1158
|
+
test_send_text_submit_send_failed_when_target_absent
|
|
1159
|
+
test_window_of_workspace_finds_window_and_count
|
|
1160
|
+
test_window_of_workspace_empty_when_not_found
|
|
1161
|
+
test_kill_closes_workspace_directly_when_not_last
|
|
1162
|
+
test_kill_adds_sibling_when_last_in_window
|
|
1163
|
+
test_kill_is_best_effort_when_close_workspace_fails
|
|
1164
|
+
test_kill_recovers_stale_target_by_label
|
|
1165
|
+
test_list_live_filters_by_title_prefix
|
|
1166
|
+
test_secondmate_spawn_refuses_cmux_backend
|