@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,910 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# fm-control.sh: the agent lifecycle CONTROL plane.
|
|
3
|
+
#
|
|
4
|
+
# These tests pin the control plane's observable behavior hermetically - a
|
|
5
|
+
# stubbed session provider, no real agent - through the executable interface
|
|
6
|
+
# firstmate actually calls:
|
|
7
|
+
# 1. Adapter contract: every verified harness gets its own verified exit
|
|
8
|
+
# command and interrupt key, delivered as bytes to the endpoint.
|
|
9
|
+
# 2. Backend capability: a backend that cannot deliver the harness's
|
|
10
|
+
# interrupt key, and a backend with no recovery-grade agent-state
|
|
11
|
+
# classifier, both refuse instead of acting blind.
|
|
12
|
+
# 3. Exact-id scoping: a window label, an explicit endpoint, an unknown id,
|
|
13
|
+
# and a record bound to another task are all refused.
|
|
14
|
+
# 4. Verb allowlist: no arbitrary text, no raw keys, no resume.
|
|
15
|
+
# 5. Lifecycle states: busy interrupts first, idle does not, already-stopped
|
|
16
|
+
# is idempotent success, and an agent that does not stop fails closed.
|
|
17
|
+
# 6. Marker non-regression: a control command to a kind=secondmate task
|
|
18
|
+
# carries NO from-firstmate marker and opens no pending-reply expectation,
|
|
19
|
+
# while fm-send's marking of the same task is untouched.
|
|
20
|
+
set -u
|
|
21
|
+
|
|
22
|
+
# shellcheck source=tests/lib.sh
|
|
23
|
+
. "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
|
24
|
+
# shellcheck source=/dev/null
|
|
25
|
+
. "$ROOT/bin/fm-control-lib.sh"
|
|
26
|
+
# shellcheck source=/dev/null
|
|
27
|
+
. "$ROOT/bin/fm-marker-lib.sh"
|
|
28
|
+
|
|
29
|
+
CONTROL="$ROOT/bin/fm-control.sh"
|
|
30
|
+
SEND="$ROOT/bin/fm-send.sh"
|
|
31
|
+
# fm_test_tmproot's own cleanup trap fires when its command substitution exits,
|
|
32
|
+
# so recreate the root before resolving it and clean it up from this file's trap.
|
|
33
|
+
TMP_ROOT=$(fm_test_tmproot fm-control)
|
|
34
|
+
mkdir -p "$TMP_ROOT"
|
|
35
|
+
TMP_ROOT=$(cd "$TMP_ROOT" && pwd)
|
|
36
|
+
trap 'rm -rf "$TMP_ROOT"' EXIT
|
|
37
|
+
|
|
38
|
+
VERIFIED_HARNESSES="claude codex opencode pi pi-signed grok kimi cursor muse"
|
|
39
|
+
|
|
40
|
+
# The expectation table, written out independently of the implementation so a
|
|
41
|
+
# silent change to either side shows up here. The fourth field is the composer
|
|
42
|
+
# clear that must FOLLOW the interrupt key, empty for every adapter that leaves
|
|
43
|
+
# its composer empty on cancel.
|
|
44
|
+
verified_adapter_contract() { # <harness> -> exit command, interrupt key, repeat, clear key
|
|
45
|
+
case "$1" in
|
|
46
|
+
claude) printf '/exit\tEscape\t1\t\n' ;;
|
|
47
|
+
codex) printf '/quit\tEscape\t1\t\n' ;;
|
|
48
|
+
opencode) printf '/exit\tEscape\t2\t\n' ;;
|
|
49
|
+
pi) printf '/quit\tEscape\t1\t\n' ;;
|
|
50
|
+
pi-signed) printf '/quit\tEscape\t1\t\n' ;;
|
|
51
|
+
grok) printf '/exit\tC-c\t1\t\n' ;;
|
|
52
|
+
kimi) printf '/exit\tEscape\t1\t\n' ;;
|
|
53
|
+
cursor) printf '/exit\tEscape\t1\t\n' ;;
|
|
54
|
+
muse) printf '/exit\tEscape\t1\tC-u\n' ;;
|
|
55
|
+
*) return 1 ;;
|
|
56
|
+
esac
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# --- fake session provider --------------------------------------------------
|
|
60
|
+
#
|
|
61
|
+
# A tmux stub whose whole model is four files under $FM_FAKE_DIR:
|
|
62
|
+
# command the pane's foreground process name, which IS the agent-state
|
|
63
|
+
# classifier's input (bin/backends/tmux.sh).
|
|
64
|
+
# cwd the pane's current path.
|
|
65
|
+
# literal every `send-keys -l` payload, one per line - exactly what was
|
|
66
|
+
# typed into the composer.
|
|
67
|
+
# keys every named key send, one per line.
|
|
68
|
+
# pane optional capture-pane override, for an adapter whose busy verdict
|
|
69
|
+
# is read from the rendered tail.
|
|
70
|
+
# Two transitions make it a lifecycle model rather than a recorder: a literal
|
|
71
|
+
# that is the harness's exit command flips `command` to a shell (the agent
|
|
72
|
+
# stopped), and a literal carrying a launch brief flips it to the value in
|
|
73
|
+
# `becomes` (a new agent came up). FM_FAKE_NEVER_DIES suppresses the first, so
|
|
74
|
+
# a stubborn agent can be tested too.
|
|
75
|
+
make_tmux_stub() { # <dir> -> echoes fakebin dir
|
|
76
|
+
local dir=$1 fb="$1/fakebin"
|
|
77
|
+
mkdir -p "$fb"
|
|
78
|
+
cat > "$fb/tmux" <<'SH'
|
|
79
|
+
#!/usr/bin/env bash
|
|
80
|
+
set -u
|
|
81
|
+
D=$FM_FAKE_DIR
|
|
82
|
+
case "${1:-}" in
|
|
83
|
+
send-keys)
|
|
84
|
+
shift
|
|
85
|
+
literal=0
|
|
86
|
+
while [ $# -gt 0 ]; do
|
|
87
|
+
case "$1" in
|
|
88
|
+
-t) shift 2 ;;
|
|
89
|
+
-l) literal=1; shift ;;
|
|
90
|
+
*) break ;;
|
|
91
|
+
esac
|
|
92
|
+
done
|
|
93
|
+
payload=${1:-}
|
|
94
|
+
if [ "$literal" = 1 ]; then
|
|
95
|
+
printf '%s\n' "$payload" >> "$D/literal"
|
|
96
|
+
if [ -z "${FM_FAKE_NEVER_DIES:-}" ] \
|
|
97
|
+
&& { [ "$payload" = /exit ] || [ "$payload" = /quit ]; }; then
|
|
98
|
+
printf 'zsh' > "$D/command"
|
|
99
|
+
fi
|
|
100
|
+
case "$payload" in
|
|
101
|
+
*'encode launch-brief'*) cat "$D/becomes" > "$D/command" ;;
|
|
102
|
+
esac
|
|
103
|
+
else
|
|
104
|
+
printf '%s\n' "$payload" >> "$D/keys"
|
|
105
|
+
if [ -n "${FM_FAKE_INTERRUPT_STOPS_AGENT:-}" ] \
|
|
106
|
+
&& { [ "$payload" = Escape ] || [ "$payload" = C-c ]; }; then
|
|
107
|
+
printf 'zsh' > "$D/command"
|
|
108
|
+
fi
|
|
109
|
+
if [ "$payload" = Escape ] && [ -n "${FM_FAKE_MUSE_LOG:-}" ]; then
|
|
110
|
+
if [ -n "${FM_FAKE_MUSE_DISAPPEAR_BEFORE_ACK:-}" ]; then
|
|
111
|
+
: > "$D/muse-ack-pending"
|
|
112
|
+
else
|
|
113
|
+
printf '%s\n' '{"schema_version":1,"payload_type":"runtime.session","payload":{"kind":"run","run_id":"run-1","event":{"kind":"terminal","terminal":"cancelled","reason":null}}}' >> "$FM_FAKE_MUSE_LOG"
|
|
114
|
+
fi
|
|
115
|
+
fi
|
|
116
|
+
fi
|
|
117
|
+
exit 0 ;;
|
|
118
|
+
display-message)
|
|
119
|
+
for a in "$@"; do
|
|
120
|
+
case "$a" in
|
|
121
|
+
*cursor_y*) printf '1\n'; exit 0 ;;
|
|
122
|
+
*pane_current_command*) cat "$D/command"; printf '\n'; exit 0 ;;
|
|
123
|
+
*pane_current_path*) cat "$D/cwd"; printf '\n'; exit 0 ;;
|
|
124
|
+
esac
|
|
125
|
+
done
|
|
126
|
+
printf 'fakepane\n'; exit 0 ;;
|
|
127
|
+
capture-pane)
|
|
128
|
+
if [ -f "$D/pane" ]; then cat "$D/pane"; else printf '╭────╮\n│ │\n╰────╯\n'; fi
|
|
129
|
+
exit 0 ;;
|
|
130
|
+
list-windows)
|
|
131
|
+
if [ -f "$D/windows" ]; then cat "$D/windows"; fi
|
|
132
|
+
exit 0 ;;
|
|
133
|
+
esac
|
|
134
|
+
exit 0
|
|
135
|
+
SH
|
|
136
|
+
chmod +x "$fb/tmux"
|
|
137
|
+
cat > "$fb/sleep" <<'SH'
|
|
138
|
+
#!/usr/bin/env bash
|
|
139
|
+
if [ -n "${FM_FAKE_MUSE_DISAPPEAR_BEFORE_ACK:-}" ] \
|
|
140
|
+
&& [ -e "$FM_FAKE_DIR/muse-ack-pending" ]; then
|
|
141
|
+
rm -f "$FM_FAKE_DIR/muse-ack-pending"
|
|
142
|
+
printf 'zsh' > "$FM_FAKE_DIR/command"
|
|
143
|
+
printf '%s\n' '{"schema_version":1,"payload_type":"runtime.session","payload":{"kind":"run","run_id":"run-1","event":{"kind":"terminal","terminal":"cancelled","reason":null}}}' >> "$FM_FAKE_MUSE_LOG"
|
|
144
|
+
fi
|
|
145
|
+
exit 0
|
|
146
|
+
SH
|
|
147
|
+
chmod +x "$fb/sleep"
|
|
148
|
+
printf '%s\n' "$fb"
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# new_case <name> -> echoes a case dir holding home/, fake/, and fakebin.
|
|
152
|
+
new_case() {
|
|
153
|
+
local dir="$TMP_ROOT/$1-$RANDOM"
|
|
154
|
+
mkdir -p "$dir/home/state" "$dir/home/data" "$dir/fake"
|
|
155
|
+
: > "$dir/fake/literal"
|
|
156
|
+
: > "$dir/fake/keys"
|
|
157
|
+
printf 'zsh' > "$dir/fake/command"
|
|
158
|
+
printf 'claude' > "$dir/fake/becomes"
|
|
159
|
+
make_tmux_stub "$dir" >/dev/null
|
|
160
|
+
printf '%s\n' "$dir"
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# add_task <case-dir> <id> <harness> [kind] [backend] [window]
|
|
164
|
+
# Builds the task's worktree (a real git worktree so the relaunch checkpoint
|
|
165
|
+
# has something to account for), its brief, and its state/<id>.meta.
|
|
166
|
+
add_task() {
|
|
167
|
+
local dir=$1 id=$2 harness=$3 kind=${4:-ship} backend=${5:-tmux}
|
|
168
|
+
local window=${6:-fmses:fm-$id}
|
|
169
|
+
local home="$dir/home" proj="$dir/proj-$id" wt="$dir/wt-$id"
|
|
170
|
+
fm_git_worktree "$proj" "$wt" "task-$id"
|
|
171
|
+
mkdir -p "$home/data/$id"
|
|
172
|
+
printf '# brief for %s\n' "$id" > "$home/data/$id/brief.md"
|
|
173
|
+
{
|
|
174
|
+
echo "window=$window"
|
|
175
|
+
echo "endpoint_task_id=$id"
|
|
176
|
+
echo "worktree=$wt"
|
|
177
|
+
echo "project=$proj"
|
|
178
|
+
echo "harness=$harness"
|
|
179
|
+
echo "kind=$kind"
|
|
180
|
+
echo "mode=no-mistakes"
|
|
181
|
+
echo "yolo=off"
|
|
182
|
+
echo "model=default"
|
|
183
|
+
echo "effort=default"
|
|
184
|
+
[ "$backend" = tmux ] || echo "backend=$backend"
|
|
185
|
+
} > "$home/state/$id.meta"
|
|
186
|
+
printf '%s\n' "fm-$id" > "$dir/fake/windows"
|
|
187
|
+
printf '%s' "$wt" > "$dir/fake/cwd"
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
# run_control <case-dir> <args...>: run fm-control against the case's home with
|
|
191
|
+
# the stubbed provider on PATH. Echoes combined output; returns its exit code.
|
|
192
|
+
run_control() {
|
|
193
|
+
local dir=$1; shift
|
|
194
|
+
env PATH="$dir/fakebin:$PATH" FM_HOME="$dir/home" FM_FAKE_DIR="$dir/fake" \
|
|
195
|
+
FM_CONTROL_POLL=0.01 FM_CONTROL_SETTLE_WAIT=0.05 \
|
|
196
|
+
FM_CONTROL_EXIT_WAIT=0.05 FM_CONTROL_LAUNCH_WAIT=0.05 \
|
|
197
|
+
FM_FAKE_MUSE_LOG="${FM_FAKE_MUSE_LOG:-}" \
|
|
198
|
+
FM_FAKE_MUSE_DISAPPEAR_BEFORE_ACK="${FM_FAKE_MUSE_DISAPPEAR_BEFORE_ACK:-}" \
|
|
199
|
+
FM_FAKE_INTERRUPT_STOPS_AGENT="${FM_FAKE_INTERRUPT_STOPS_AGENT:-}" \
|
|
200
|
+
"$CONTROL" "$@" 2>&1
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
alive_as() { # <case-dir> <command-name>
|
|
204
|
+
printf '%s' "$2" > "$1/fake/command"
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
literals() { # <case-dir>
|
|
208
|
+
cat "$1/fake/literal"
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# Every named key EXCEPT Enter, which is submission mechanics shared with every
|
|
212
|
+
# text send rather than a control-plane key.
|
|
213
|
+
keys_sent() { # <case-dir>
|
|
214
|
+
grep -v '^Enter$' "$1/fake/keys" || true
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
# --- 1. adapter contract across every verified harness -----------------------
|
|
218
|
+
|
|
219
|
+
test_exit_types_each_harness_verified_command() {
|
|
220
|
+
local dir out rc harness expected key repeat clear
|
|
221
|
+
for harness in $VERIFIED_HARNESSES; do
|
|
222
|
+
dir=$(new_case "exit-$harness")
|
|
223
|
+
add_task "$dir" t1 "$harness"
|
|
224
|
+
if [ "$harness" = cursor ]; then
|
|
225
|
+
alive_as "$dir" cursor-agent
|
|
226
|
+
else
|
|
227
|
+
alive_as "$dir" "$harness"
|
|
228
|
+
fi
|
|
229
|
+
out=$(run_control "$dir" t1 exit); rc=$?
|
|
230
|
+
expect_code 0 "$rc" "exit on $harness should succeed"$'\n'"$out"
|
|
231
|
+
IFS=$'\t' read -r expected key repeat clear <<< "$(verified_adapter_contract "$harness")"
|
|
232
|
+
[ "$(literals "$dir")" = "$expected" ] \
|
|
233
|
+
|| fail "exit on $harness should type exactly '$expected', got: $(literals "$dir")"
|
|
234
|
+
assert_contains "$out" "stopped t1 harness=$harness" "exit should report the stop for $harness"
|
|
235
|
+
done
|
|
236
|
+
pass "fm-control exit: every verified harness gets its own verified exit command"
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
test_interrupt_sends_each_harness_verified_key() {
|
|
240
|
+
local dir out rc harness expected key repeat clear got want
|
|
241
|
+
for harness in $VERIFIED_HARNESSES; do
|
|
242
|
+
dir=$(new_case "int-$harness")
|
|
243
|
+
add_task "$dir" t1 "$harness"
|
|
244
|
+
if [ "$harness" = cursor ]; then
|
|
245
|
+
alive_as "$dir" cursor-agent
|
|
246
|
+
else
|
|
247
|
+
alive_as "$dir" "$harness"
|
|
248
|
+
fi
|
|
249
|
+
out=$(run_control "$dir" t1 interrupt); rc=$?
|
|
250
|
+
expect_code 0 "$rc" "interrupt on $harness should succeed"$'\n'"$out"
|
|
251
|
+
IFS=$'\t' read -r expected key repeat clear <<< "$(verified_adapter_contract "$harness")"
|
|
252
|
+
want=$(for _ in $(seq 1 "$repeat"); do printf '%s\n' "$key"; done)
|
|
253
|
+
[ -z "$clear" ] || want="$want"$'\n'"$clear"
|
|
254
|
+
got=$(keys_sent "$dir")
|
|
255
|
+
[ "$got" = "$want" ] \
|
|
256
|
+
|| fail "interrupt on $harness should send $repeat x $key${clear:+ then $clear}, got: $got"
|
|
257
|
+
[ -z "$(literals "$dir")" ] \
|
|
258
|
+
|| fail "interrupt on $harness must type no text, got: $(literals "$dir")"
|
|
259
|
+
done
|
|
260
|
+
pass "fm-control interrupt: every verified harness gets its own verified key and repeat count"
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
# A recorded harness can carry a raw launch command's basename, so the tables
|
|
264
|
+
# are reached through one prefix rule rather than an exact string match.
|
|
265
|
+
test_harness_family_resolution() {
|
|
266
|
+
local pair recorded want got
|
|
267
|
+
for pair in claude:claude claude-latest:claude codex:codex codex-cli:codex \
|
|
268
|
+
opencode:opencode grok:grok grok-2:grok kimi:kimi cursor:cursor \
|
|
269
|
+
cursor-agent:cursor muse:muse muse-bin-0.1.0:muse pi:pi \
|
|
270
|
+
pi-signed:pi-signed; do
|
|
271
|
+
recorded=${pair%%:*}
|
|
272
|
+
want=${pair#*:}
|
|
273
|
+
got=$(fm_control_harness_family "$recorded") \
|
|
274
|
+
|| fail "'$recorded' should resolve to the $want adapter"
|
|
275
|
+
[ "$got" = "$want" ] || fail "'$recorded' should resolve to $want, got '$got'"
|
|
276
|
+
done
|
|
277
|
+
fm_control_harness_family someagent \
|
|
278
|
+
&& fail "an unrecognized launch command must not be guessed into an adapter family"
|
|
279
|
+
fm_control_harness_family '' \
|
|
280
|
+
&& fail "an empty harness must not resolve to an adapter family"
|
|
281
|
+
# The signed adapter is a distinct launch profile, not a pi variant.
|
|
282
|
+
[ "$(fm_control_harness_family pi-signed)" != "$(fm_control_harness_family pi)" ] \
|
|
283
|
+
|| fail "pi-signed must not collapse into pi"
|
|
284
|
+
pass "fm-control-lib: a recorded harness resolves to its verified adapter without guessing"
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
test_prefixed_recorded_harness_reaches_each_control_verb() {
|
|
288
|
+
local dir out rc
|
|
289
|
+
dir=$(new_case prefixed-interrupt)
|
|
290
|
+
add_task "$dir" t1 grok-2
|
|
291
|
+
alive_as "$dir" grok-2
|
|
292
|
+
out=$(run_control "$dir" t1 interrupt); rc=$?
|
|
293
|
+
expect_code 0 "$rc" "interrupt should resolve a prefixed recorded harness"$'\n'"$out"
|
|
294
|
+
[ "$(keys_sent "$dir")" = C-c ] \
|
|
295
|
+
|| fail "a grok-prefixed task should receive grok's interrupt key"
|
|
296
|
+
assert_contains "$out" "harness=grok" \
|
|
297
|
+
"interrupt should report the verified adapter that supplied its mechanics"
|
|
298
|
+
|
|
299
|
+
dir=$(new_case prefixed-exit)
|
|
300
|
+
add_task "$dir" t1 grok-2
|
|
301
|
+
alive_as "$dir" grok-2
|
|
302
|
+
out=$(run_control "$dir" t1 exit); rc=$?
|
|
303
|
+
expect_code 0 "$rc" "exit should resolve a prefixed recorded harness"$'\n'"$out"
|
|
304
|
+
[ "$(literals "$dir")" = /exit ] \
|
|
305
|
+
|| fail "a grok-prefixed task should receive grok's exit command"
|
|
306
|
+
assert_contains "$out" "stopped t1 harness=grok" \
|
|
307
|
+
"exit should report the verified adapter that supplied its mechanics"
|
|
308
|
+
pass "fm-control: prefixed recorded harnesses reach interrupt and exit mechanics"
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
test_opencode_interrupts_twice_and_others_once() {
|
|
312
|
+
# The one adapter that differs, asserted through the delivered keys rather
|
|
313
|
+
# than the table, so a regression in either shows up here.
|
|
314
|
+
local dir
|
|
315
|
+
dir=$(new_case int-double)
|
|
316
|
+
add_task "$dir" t1 opencode
|
|
317
|
+
alive_as "$dir" opencode
|
|
318
|
+
run_control "$dir" t1 interrupt >/dev/null
|
|
319
|
+
[ "$(keys_sent "$dir" | wc -l | tr -d ' ')" = 2 ] \
|
|
320
|
+
|| fail "opencode should receive a double Escape"
|
|
321
|
+
dir=$(new_case int-single)
|
|
322
|
+
add_task "$dir" t1 claude
|
|
323
|
+
alive_as "$dir" claude
|
|
324
|
+
run_control "$dir" t1 interrupt >/dev/null
|
|
325
|
+
[ "$(keys_sent "$dir" | wc -l | tr -d ' ')" = 1 ] \
|
|
326
|
+
|| fail "claude should receive a single Escape"
|
|
327
|
+
pass "fm-control interrupt: opencode needs a double Escape, claude a single one"
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
test_unverified_harness_is_refused() {
|
|
331
|
+
local dir out rc
|
|
332
|
+
dir=$(new_case unverified)
|
|
333
|
+
add_task "$dir" t1 someagent
|
|
334
|
+
alive_as "$dir" someagent
|
|
335
|
+
out=$(run_control "$dir" t1 exit); rc=$?
|
|
336
|
+
expect_code 1 "$rc" "an unverified harness should refuse"
|
|
337
|
+
assert_contains "$out" "no verified control mechanics" "refusal should name the missing verification"
|
|
338
|
+
[ -z "$(literals "$dir")" ] || fail "an unverified harness must receive no bytes"
|
|
339
|
+
pass "fm-control: a harness with no verified control mechanics is refused, not guessed at"
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
# --- 2. backend capability matrix -------------------------------------------
|
|
343
|
+
|
|
344
|
+
test_backend_key_capability_matrix() {
|
|
345
|
+
local backend key
|
|
346
|
+
for backend in tmux herdr zellij cmux; do
|
|
347
|
+
# C-u is the composer clear muse's interrupt needs; every session provider
|
|
348
|
+
# but Orca normalizes it (bin/backends/*.sh).
|
|
349
|
+
for key in Escape Enter C-c C-u; do
|
|
350
|
+
fm_control_backend_supports_key "$backend" "$key" \
|
|
351
|
+
|| fail "$backend should be able to deliver $key"
|
|
352
|
+
done
|
|
353
|
+
done
|
|
354
|
+
fm_control_backend_supports_key orca Escape \
|
|
355
|
+
&& fail "orca's terminal API has no Escape and must not claim it"
|
|
356
|
+
fm_control_backend_supports_key orca C-u \
|
|
357
|
+
&& fail "orca's terminal API has no composer clear and must not claim one"
|
|
358
|
+
fm_control_backend_supports_key orca C-c || fail "orca should deliver C-c"
|
|
359
|
+
fm_control_backend_supports_key orca Enter || fail "orca should deliver Enter"
|
|
360
|
+
pass "fm-control-lib: the backend key matrix matches each adapter's real send-key surface"
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
# A verified adapter is not automatically verified for every task kind, and the
|
|
364
|
+
# check has to sit on the pre-stop side of a relaunch: muse has no primary
|
|
365
|
+
# supervision protocol, so bin/fm-spawn.sh refuses it for a secondmate, and
|
|
366
|
+
# discovering that only after the running agent was stopped would strand the
|
|
367
|
+
# secondmate with no agent at all.
|
|
368
|
+
test_harness_kind_capability() {
|
|
369
|
+
local harness
|
|
370
|
+
for harness in $VERIFIED_HARNESSES; do
|
|
371
|
+
fm_control_harness_supports_kind "$harness" ship \
|
|
372
|
+
|| fail "$harness should be able to run a ship task"
|
|
373
|
+
fm_control_harness_supports_kind "$harness" scout \
|
|
374
|
+
|| fail "$harness should be able to run a scout task"
|
|
375
|
+
done
|
|
376
|
+
fm_control_harness_supports_kind muse secondmate \
|
|
377
|
+
&& fail "muse has no primary supervision protocol and must not claim a secondmate"
|
|
378
|
+
for harness in claude codex opencode pi pi-signed grok kimi; do
|
|
379
|
+
fm_control_harness_supports_kind "$harness" secondmate \
|
|
380
|
+
|| fail "$harness should be able to run a secondmate"
|
|
381
|
+
done
|
|
382
|
+
fm_control_harness_supports_kind someagent ship \
|
|
383
|
+
&& fail "an unverified harness must not claim any kind"
|
|
384
|
+
pass "fm-control-lib: adapter capability is per task kind, not per adapter alone"
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
test_orca_refuses_an_escape_harness_interrupt() {
|
|
388
|
+
local dir out rc
|
|
389
|
+
dir=$(new_case orca-escape)
|
|
390
|
+
add_task "$dir" t1 claude ship orca "term-1"
|
|
391
|
+
# Orca records its endpoint as terminal=, which endpoint validation requires.
|
|
392
|
+
{
|
|
393
|
+
cat "$dir/home/state/t1.meta"
|
|
394
|
+
echo "terminal=term-1"
|
|
395
|
+
echo "orca_worktree_id=wt-1"
|
|
396
|
+
} > "$dir/home/state/t1.meta.new"
|
|
397
|
+
sed 's|^window=.*|window=fm-t1|' "$dir/home/state/t1.meta.new" > "$dir/home/state/t1.meta"
|
|
398
|
+
out=$(run_control "$dir" t1 interrupt); rc=$?
|
|
399
|
+
expect_code 1 "$rc" "an Escape harness on orca should refuse"
|
|
400
|
+
assert_contains "$out" "cannot deliver" "refusal should name the undeliverable key"
|
|
401
|
+
pass "fm-control interrupt: a backend that cannot deliver the harness's key refuses instead of sending another"
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
test_unverified_state_backends_refuse_stop_verbs() {
|
|
405
|
+
local dir out rc backend
|
|
406
|
+
for backend in zellij cmux; do
|
|
407
|
+
dir=$(new_case "nostate-$backend")
|
|
408
|
+
if [ "$backend" = zellij ]; then
|
|
409
|
+
add_task "$dir" t1 claude ship zellij "sess:7"
|
|
410
|
+
{
|
|
411
|
+
echo "zellij_session=sess"
|
|
412
|
+
echo "zellij_tab_id=1"
|
|
413
|
+
echo "zellij_pane_id=7"
|
|
414
|
+
} >> "$dir/home/state/t1.meta"
|
|
415
|
+
else
|
|
416
|
+
add_task "$dir" t1 claude ship cmux "ws1:surface1"
|
|
417
|
+
{
|
|
418
|
+
echo "cmux_workspace_id=ws1"
|
|
419
|
+
echo "cmux_surface_id=surface1"
|
|
420
|
+
} >> "$dir/home/state/t1.meta"
|
|
421
|
+
fi
|
|
422
|
+
out=$(run_control "$dir" t1 exit); rc=$?
|
|
423
|
+
expect_code 1 "$rc" "exit on $backend should refuse"$'\n'"$out"
|
|
424
|
+
assert_contains "$out" "no recovery-grade agent-state classifier" \
|
|
425
|
+
"the $backend refusal should name the missing stop proof"
|
|
426
|
+
[ -z "$(literals "$dir")" ] || fail "$backend must receive no exit command"
|
|
427
|
+
out=$(run_control "$dir" t1 relaunch --note x); rc=$?
|
|
428
|
+
expect_code 1 "$rc" "relaunch on $backend should refuse"$'\n'"$out"
|
|
429
|
+
assert_contains "$out" "no recovery-grade agent-state classifier" \
|
|
430
|
+
"the $backend relaunch refusal should name the missing stop proof"
|
|
431
|
+
done
|
|
432
|
+
pass "fm-control: a backend that cannot prove an agent stopped refuses exit and relaunch"
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
test_state_verified_backends_are_exactly_tmux_and_herdr() {
|
|
436
|
+
fm_control_backend_state_verified tmux || fail "tmux has a recovery-grade classifier"
|
|
437
|
+
fm_control_backend_state_verified herdr || fail "herdr has a recovery-grade classifier"
|
|
438
|
+
local backend
|
|
439
|
+
for backend in zellij orca cmux; do
|
|
440
|
+
fm_control_backend_state_verified "$backend" \
|
|
441
|
+
&& fail "$backend has no recovery-grade classifier and must not claim one"
|
|
442
|
+
done
|
|
443
|
+
pass "fm-control-lib: stop-proving verbs are gated on the backends that really classify agent state"
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
# --- 3. exact-id scoping ----------------------------------------------------
|
|
447
|
+
|
|
448
|
+
test_window_label_is_refused_with_the_exact_id() {
|
|
449
|
+
local dir out rc
|
|
450
|
+
dir=$(new_case label)
|
|
451
|
+
add_task "$dir" t1 claude
|
|
452
|
+
alive_as "$dir" claude
|
|
453
|
+
out=$(run_control "$dir" fm-t1 exit); rc=$?
|
|
454
|
+
expect_code 1 "$rc" "a window label should refuse"
|
|
455
|
+
assert_contains "$out" "pass the exact task id 't1'" "the refusal should name the exact id"
|
|
456
|
+
[ -z "$(literals "$dir")" ] || fail "a refused target must receive no bytes"
|
|
457
|
+
pass "fm-control: a legacy window label is refused and the exact task id is named"
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
test_explicit_endpoint_is_refused() {
|
|
461
|
+
local dir out rc
|
|
462
|
+
dir=$(new_case endpoint)
|
|
463
|
+
add_task "$dir" t1 claude
|
|
464
|
+
alive_as "$dir" claude
|
|
465
|
+
out=$(run_control "$dir" "fmses:fm-t1" exit); rc=$?
|
|
466
|
+
expect_code 1 "$rc" "an explicit endpoint should refuse"
|
|
467
|
+
assert_contains "$out" "exact task id only" "the refusal should name the exact-id rule"
|
|
468
|
+
[ -z "$(literals "$dir")" ] || fail "a refused target must receive no bytes"
|
|
469
|
+
pass "fm-control: an explicit backend endpoint is never a control target"
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
test_unknown_task_is_refused() {
|
|
473
|
+
local dir out rc
|
|
474
|
+
dir=$(new_case unknown)
|
|
475
|
+
add_task "$dir" t1 claude
|
|
476
|
+
out=$(run_control "$dir" t2 exit); rc=$?
|
|
477
|
+
expect_code 1 "$rc" "an unknown task should refuse"
|
|
478
|
+
assert_contains "$out" "no task 't2'" "the refusal should name the missing task"
|
|
479
|
+
pass "fm-control: an unrecorded task id is refused"
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
test_record_bound_to_another_task_is_refused() {
|
|
483
|
+
local dir out rc
|
|
484
|
+
dir=$(new_case foreign)
|
|
485
|
+
add_task "$dir" t1 claude
|
|
486
|
+
alive_as "$dir" claude
|
|
487
|
+
sed 's/^endpoint_task_id=t1$/endpoint_task_id=other/' "$dir/home/state/t1.meta" \
|
|
488
|
+
> "$dir/home/state/t1.meta.tmp"
|
|
489
|
+
mv "$dir/home/state/t1.meta.tmp" "$dir/home/state/t1.meta"
|
|
490
|
+
out=$(run_control "$dir" t1 exit); rc=$?
|
|
491
|
+
expect_code 1 "$rc" "a record bound to another task should refuse"
|
|
492
|
+
assert_contains "$out" "belongs to task other" "the refusal should name the conflicting binding"
|
|
493
|
+
[ -z "$(literals "$dir")" ] || fail "a foreign record must receive no bytes"
|
|
494
|
+
pass "fm-control: a record whose endpoint identity names another task is refused"
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
# A remotely placed secondmate's agent runs on another host, so none of the
|
|
498
|
+
# postconditions this plane verifies could be read for it here. Endpoint
|
|
499
|
+
# validation would refuse the record anyway - `window=remote:<id>` can never
|
|
500
|
+
# match a local backend's shape - but it would blame malformed metadata for a
|
|
501
|
+
# correctly configured route, so the placement is named instead. Every verb
|
|
502
|
+
# refuses, and none of them reaches a local endpoint.
|
|
503
|
+
test_remote_secondmate_is_refused_by_placement() {
|
|
504
|
+
local dir out rc verb
|
|
505
|
+
for verb in interrupt exit relaunch; do
|
|
506
|
+
dir=$(new_case "remote-$verb")
|
|
507
|
+
add_task "$dir" t1 claude secondmate
|
|
508
|
+
alive_as "$dir" claude
|
|
509
|
+
{
|
|
510
|
+
grep -v '^window=' "$dir/home/state/t1.meta"
|
|
511
|
+
echo "window=remote:t1"
|
|
512
|
+
echo "home=$dir/wt-t1"
|
|
513
|
+
echo "remote_host=example.invalid"
|
|
514
|
+
echo "remote_root=/srv/fm"
|
|
515
|
+
echo "remote_backend=herdr"
|
|
516
|
+
echo "remote_target=fm:pane-1"
|
|
517
|
+
} > "$dir/home/state/t1.meta.tmp"
|
|
518
|
+
mv "$dir/home/state/t1.meta.tmp" "$dir/home/state/t1.meta"
|
|
519
|
+
if [ "$verb" = relaunch ]; then
|
|
520
|
+
out=$(run_control "$dir" t1 "$verb" --note "x"); rc=$?
|
|
521
|
+
else
|
|
522
|
+
out=$(run_control "$dir" t1 "$verb"); rc=$?
|
|
523
|
+
fi
|
|
524
|
+
expect_code 1 "$rc" "$verb on a remotely placed secondmate should refuse"
|
|
525
|
+
assert_contains "$out" "remotely placed secondmate on example.invalid" \
|
|
526
|
+
"the $verb refusal should name the remote placement, not blame the record"
|
|
527
|
+
assert_not_contains "$out" "malformed" \
|
|
528
|
+
"a correctly configured remote route must not be reported as malformed"
|
|
529
|
+
[ -z "$(literals "$dir")" ] && [ -z "$(keys_sent "$dir")" ] \
|
|
530
|
+
|| fail "$verb on a remote secondmate must reach no local endpoint"
|
|
531
|
+
done
|
|
532
|
+
pass "fm-control: a remotely placed secondmate is refused by placement, not by a metadata complaint"
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
hold_lifecycle_lock() { # <lock-path>
|
|
536
|
+
local lifecycle_lock_path=$1
|
|
537
|
+
. "$ROOT/bin/fm-wake-lib.sh"
|
|
538
|
+
fm_lock_try_acquire "$lifecycle_lock_path" || return 1
|
|
539
|
+
sleep 30
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
test_interrupt_and_exit_lock_before_task_state_resolution() {
|
|
543
|
+
local case_dir out rc verb lifecycle_lock_path holder i
|
|
544
|
+
for verb in interrupt exit; do
|
|
545
|
+
case_dir=$(new_case "locked-$verb")
|
|
546
|
+
add_task "$case_dir" t1 claude
|
|
547
|
+
alive_as "$case_dir" claude
|
|
548
|
+
lifecycle_lock_path="$case_dir/home/state/.control-t1.lock"
|
|
549
|
+
hold_lifecycle_lock "$lifecycle_lock_path" &
|
|
550
|
+
holder=$!
|
|
551
|
+
i=0
|
|
552
|
+
while [ ! -e "$lifecycle_lock_path" ] && [ "$i" -lt 100 ]; do
|
|
553
|
+
sleep 0.1
|
|
554
|
+
i=$((i + 1))
|
|
555
|
+
done
|
|
556
|
+
[ -e "$lifecycle_lock_path" ] || fail "could not stage the lifecycle lock for $verb"
|
|
557
|
+
sed 's/^endpoint_task_id=t1$/endpoint_task_id=other/' "$case_dir/home/state/t1.meta" \
|
|
558
|
+
> "$case_dir/home/state/t1.meta.tmp"
|
|
559
|
+
mv "$case_dir/home/state/t1.meta.tmp" "$case_dir/home/state/t1.meta"
|
|
560
|
+
out=$(run_control "$case_dir" t1 "$verb"); rc=$?
|
|
561
|
+
kill "$holder" 2>/dev/null || true
|
|
562
|
+
wait "$holder" 2>/dev/null || true
|
|
563
|
+
expect_code 1 "$rc" "$verb should refuse a held lifecycle lock"
|
|
564
|
+
assert_contains "$out" "another lifecycle action is already running" \
|
|
565
|
+
"$verb should serialize before reading mutable task state"
|
|
566
|
+
[ -z "$(literals "$case_dir")" ] || fail "contended $verb must type no command"
|
|
567
|
+
[ -z "$(keys_sent "$case_dir")" ] || fail "contended $verb must send no control key"
|
|
568
|
+
done
|
|
569
|
+
pass "fm-control: interrupt and exit lock before task-state resolution"
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
# --- 4. verb allowlist ------------------------------------------------------
|
|
573
|
+
|
|
574
|
+
test_verb_allowlist_is_closed() {
|
|
575
|
+
local dir out rc
|
|
576
|
+
dir=$(new_case verbs)
|
|
577
|
+
add_task "$dir" t1 claude
|
|
578
|
+
alive_as "$dir" claude
|
|
579
|
+
out=$(run_control "$dir" t1 restart); rc=$?
|
|
580
|
+
expect_code 2 "$rc" "an unknown verb should be a usage error"
|
|
581
|
+
assert_contains "$out" "is not a control verb" "the refusal should say so"
|
|
582
|
+
assert_contains "$out" "interrupt" "the refusal should list the allowed verbs"
|
|
583
|
+
out=$(run_control "$dir" t1 --key); rc=$?
|
|
584
|
+
expect_code 2 "$rc" "a raw key is not a control verb"
|
|
585
|
+
out=$(run_control "$dir" t1 clear); rc=$?
|
|
586
|
+
expect_code 2 "$rc" "clear is not a control verb"
|
|
587
|
+
out=$(run_control "$dir" t1 "please stop what you are doing"); rc=$?
|
|
588
|
+
expect_code 2 "$rc" "arbitrary text is not a control verb"
|
|
589
|
+
[ -z "$(literals "$dir")" ] || fail "a refused verb must send nothing"
|
|
590
|
+
[ -z "$(keys_sent "$dir")" ] || fail "a refused verb must send no keys"
|
|
591
|
+
pass "fm-control: the verb list is closed - no raw keys, arbitrary text, or clear verb"
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
test_resume_is_refused_with_its_reason() {
|
|
595
|
+
local dir out rc
|
|
596
|
+
dir=$(new_case resume)
|
|
597
|
+
add_task "$dir" t1 claude
|
|
598
|
+
out=$(run_control "$dir" t1 resume); rc=$?
|
|
599
|
+
expect_code 2 "$rc" "resume should be refused"
|
|
600
|
+
assert_contains "$out" "not deterministic across the verified adapters" \
|
|
601
|
+
"the refusal should explain why resume is excluded"
|
|
602
|
+
assert_contains "$out" "relaunch" "the refusal should point at the deterministic alternative"
|
|
603
|
+
pass "fm-control: resume is refused with the determinism reason and the alternative"
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
test_relaunch_only_flags_are_rejected_on_other_verbs() {
|
|
607
|
+
local dir out rc
|
|
608
|
+
dir=$(new_case flags)
|
|
609
|
+
add_task "$dir" t1 claude
|
|
610
|
+
alive_as "$dir" claude
|
|
611
|
+
out=$(run_control "$dir" t1 exit --harness codex); rc=$?
|
|
612
|
+
expect_code 1 "$rc" "--harness should not apply to exit"
|
|
613
|
+
assert_contains "$out" "apply to 'relaunch' only" "the refusal should scope the flags"
|
|
614
|
+
pass "fm-control: profile and note flags belong to relaunch only"
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
# --- 5. lifecycle states ----------------------------------------------------
|
|
618
|
+
|
|
619
|
+
test_already_stopped_exit_is_idempotent() {
|
|
620
|
+
local dir out rc
|
|
621
|
+
dir=$(new_case idempotent)
|
|
622
|
+
add_task "$dir" t1 claude
|
|
623
|
+
alive_as "$dir" zsh
|
|
624
|
+
out=$(run_control "$dir" t1 exit); rc=$?
|
|
625
|
+
expect_code 0 "$rc" "exiting an already-stopped agent should succeed"
|
|
626
|
+
assert_contains "$out" "already-stopped t1" "the outcome should say it was already stopped"
|
|
627
|
+
[ -z "$(literals "$dir")" ] || fail "an already-stopped agent must not be sent an exit command"
|
|
628
|
+
pass "fm-control exit: an already-stopped agent is idempotent success with no bytes sent"
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
test_missing_endpoint_refuses() {
|
|
632
|
+
local dir out rc
|
|
633
|
+
dir=$(new_case gone)
|
|
634
|
+
add_task "$dir" t1 claude
|
|
635
|
+
: > "$dir/fake/windows"
|
|
636
|
+
out=$(run_control "$dir" t1 exit); rc=$?
|
|
637
|
+
expect_code 1 "$rc" "a missing endpoint should refuse"
|
|
638
|
+
assert_contains "$out" "recorded endpoint is gone" "the refusal should name the missing endpoint"
|
|
639
|
+
pass "fm-control exit: a vanished endpoint refuses instead of silently succeeding"
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
test_interrupt_refuses_when_no_agent_runs() {
|
|
643
|
+
local dir out rc
|
|
644
|
+
dir=$(new_case nointerrupt)
|
|
645
|
+
add_task "$dir" t1 claude
|
|
646
|
+
alive_as "$dir" zsh
|
|
647
|
+
out=$(run_control "$dir" t1 interrupt); rc=$?
|
|
648
|
+
expect_code 1 "$rc" "interrupting a stopped agent should refuse"
|
|
649
|
+
assert_contains "$out" "nothing to interrupt" "the refusal should say there is no agent"
|
|
650
|
+
[ -z "$(keys_sent "$dir")" ] || fail "no key should reach a stopped agent"
|
|
651
|
+
pass "fm-control interrupt: refuses when no agent is running rather than keying a shell"
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
test_ambiguous_endpoint_refuses() {
|
|
655
|
+
local dir out rc
|
|
656
|
+
dir=$(new_case ambiguous)
|
|
657
|
+
add_task "$dir" t1 claude
|
|
658
|
+
alive_as "$dir" some-unrelated-process
|
|
659
|
+
out=$(run_control "$dir" t1 exit); rc=$?
|
|
660
|
+
expect_code 1 "$rc" "an unattributed endpoint should refuse"
|
|
661
|
+
assert_contains "$out" "positively classified" "the refusal should name the missing attribution"
|
|
662
|
+
[ -z "$(literals "$dir")" ] || fail "an unattributed endpoint must receive no bytes"
|
|
663
|
+
pass "fm-control exit: an endpoint whose process cannot be attributed refuses"
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
test_busy_agent_is_interrupted_before_the_exit_command() {
|
|
667
|
+
local dir out rc
|
|
668
|
+
dir=$(new_case busy)
|
|
669
|
+
add_task "$dir" t1 claude
|
|
670
|
+
alive_as "$dir" claude
|
|
671
|
+
# Arm the semantic busy contract and record a busy turn, exactly as the
|
|
672
|
+
# harness's own lifecycle hook would.
|
|
673
|
+
gen=$("$ROOT/bin/fm-busy-event.sh" arm "$dir/home/state" t1)
|
|
674
|
+
printf 'busy_gen=%s\n' "$gen" >> "$dir/home/state/t1.meta"
|
|
675
|
+
out=$(run_control "$dir" t1 exit); rc=$?
|
|
676
|
+
expect_code 0 "$rc" "exiting a busy agent should succeed"$'\n'"$out"
|
|
677
|
+
[ "$(keys_sent "$dir")" = "Escape" ] \
|
|
678
|
+
|| fail "a busy agent should be interrupted once before its exit command, got: $(keys_sent "$dir")"
|
|
679
|
+
[ "$(literals "$dir")" = "/exit" ] || fail "the exit command should follow the interrupt"
|
|
680
|
+
pass "fm-control exit: a busy agent receives interrupt delivery before the exit command"
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
test_idle_agent_is_not_interrupted() {
|
|
684
|
+
local dir out rc gen
|
|
685
|
+
dir=$(new_case idle)
|
|
686
|
+
add_task "$dir" t1 claude
|
|
687
|
+
alive_as "$dir" claude
|
|
688
|
+
gen=$("$ROOT/bin/fm-busy-event.sh" arm "$dir/home/state" t1 --state idle --source fm-spawn --event seed)
|
|
689
|
+
printf 'busy_gen=%s\n' "$gen" >> "$dir/home/state/t1.meta"
|
|
690
|
+
out=$(run_control "$dir" t1 exit); rc=$?
|
|
691
|
+
expect_code 0 "$rc" "exiting an idle agent should succeed"$'\n'"$out"
|
|
692
|
+
[ -z "$(keys_sent "$dir")" ] \
|
|
693
|
+
|| fail "an idle agent needs no interrupt, got keys: $(keys_sent "$dir")"
|
|
694
|
+
[ "$(literals "$dir")" = "/exit" ] || fail "the exit command should still be sent"
|
|
695
|
+
pass "fm-control exit: an idle agent goes straight to its exit command"
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
test_interrupt_without_acknowledgement_preserves_busy_state() {
|
|
699
|
+
local dir gen before after out rc
|
|
700
|
+
dir=$(new_case unconfirmed)
|
|
701
|
+
add_task "$dir" t1 claude
|
|
702
|
+
alive_as "$dir" claude
|
|
703
|
+
gen=$("$ROOT/bin/fm-busy-event.sh" arm "$dir/home/state" t1)
|
|
704
|
+
printf 'busy_gen=%s\n' "$gen" >> "$dir/home/state/t1.meta"
|
|
705
|
+
before=$(cat "$dir/home/state/t1.busy-state")
|
|
706
|
+
out=$(run_control "$dir" t1 interrupt); rc=$?
|
|
707
|
+
expect_code 0 "$rc" "an interrupt without acknowledgement should still deliver"$'\n'"$out"
|
|
708
|
+
after=$(cat "$dir/home/state/t1.busy-state")
|
|
709
|
+
[ "$after" = "$before" ] || fail "an unconfirmed interrupt must preserve adapter-owned busy state"
|
|
710
|
+
assert_contains "$out" "verified=agent-alive cancel=unconfirmed" \
|
|
711
|
+
"the result should distinguish delivery proof from unconfirmed cancellation"
|
|
712
|
+
assert_not_contains "$out" "cancel=confirmed" \
|
|
713
|
+
"an adapter without acknowledgement must not report cancellation"
|
|
714
|
+
pass "fm-control interrupt: unconfirmed delivery preserves observed busy state"
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
test_muse_interrupt_confirms_adapter_acknowledgement() {
|
|
718
|
+
local dir root log out rc
|
|
719
|
+
dir=$(new_case confirmed)
|
|
720
|
+
add_task "$dir" t1 muse
|
|
721
|
+
alive_as "$dir" muse
|
|
722
|
+
root="$dir/muse-sessions"
|
|
723
|
+
log="$root/2026/08/08/session-1/session.jsonl"
|
|
724
|
+
mkdir -p "$(dirname "$log")"
|
|
725
|
+
printf '%s\n' \
|
|
726
|
+
"{\"schema_version\":1,\"payload_type\":\"runtime.session.metadata\",\"payload\":{\"kind\":\"metadata\",\"record\":{\"workspace_root\":\"$dir/wt-t1\"}}}" \
|
|
727
|
+
'{"schema_version":1,"payload_type":"runtime.session","payload":{"kind":"run","run_id":"run-1","event":{"kind":"started","prompt":"work"}}}' > "$log"
|
|
728
|
+
printf 'sessions_root=%s\nworkspace_root=%s\nbinding_id=test\n' \
|
|
729
|
+
"$root" "$dir/wt-t1" > "$dir/home/state/t1.muse-session"
|
|
730
|
+
out=$(FM_FAKE_MUSE_LOG="$log" run_control "$dir" t1 interrupt); rc=$?
|
|
731
|
+
expect_code 0 "$rc" "muse interrupt should observe its adapter acknowledgement"$'\n'"$out"
|
|
732
|
+
assert_contains "$out" "verified=agent-alive cancel=confirmed" \
|
|
733
|
+
"the result should report muse's cancelled terminal acknowledgement"
|
|
734
|
+
pass "fm-control interrupt: muse confirms cancellation from its session log"
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
test_interrupt_revalidates_agent_after_acknowledgement_wait() {
|
|
738
|
+
local dir root log out rc
|
|
739
|
+
dir=$(new_case ack-race)
|
|
740
|
+
add_task "$dir" t1 muse
|
|
741
|
+
alive_as "$dir" muse
|
|
742
|
+
root="$dir/muse-sessions"
|
|
743
|
+
log="$root/2026/08/08/session-1/session.jsonl"
|
|
744
|
+
mkdir -p "$(dirname "$log")"
|
|
745
|
+
printf '%s\n' \
|
|
746
|
+
"{\"schema_version\":1,\"payload_type\":\"runtime.session.metadata\",\"payload\":{\"kind\":\"metadata\",\"record\":{\"workspace_root\":\"$dir/wt-t1\"}}}" \
|
|
747
|
+
'{"schema_version":1,"payload_type":"runtime.session","payload":{"kind":"run","run_id":"run-1","event":{"kind":"started","prompt":"work"}}}' > "$log"
|
|
748
|
+
printf 'sessions_root=%s\nworkspace_root=%s\nbinding_id=test\n' \
|
|
749
|
+
"$root" "$dir/wt-t1" > "$dir/home/state/t1.muse-session"
|
|
750
|
+
out=$(FM_FAKE_MUSE_LOG="$log" FM_FAKE_MUSE_DISAPPEAR_BEFORE_ACK=1 \
|
|
751
|
+
run_control "$dir" t1 interrupt); rc=$?
|
|
752
|
+
expect_code 1 "$rc" "interrupt should fail when the agent stops during acknowledgement polling"
|
|
753
|
+
assert_contains "$out" "agent is 'dead' after its interrupt key" \
|
|
754
|
+
"the final postcondition should observe the agent after acknowledgement polling"
|
|
755
|
+
assert_not_contains "$out" "interrupt-delivered" \
|
|
756
|
+
"a stale pre-wait liveness proof must not be published"
|
|
757
|
+
pass "fm-control interrupt: postconditions are revalidated after acknowledgement polling"
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
test_exit_accepts_agent_stopped_by_busy_interrupt() {
|
|
761
|
+
local dir out rc gen
|
|
762
|
+
dir=$(new_case interrupt-stops)
|
|
763
|
+
add_task "$dir" t1 claude
|
|
764
|
+
alive_as "$dir" claude
|
|
765
|
+
gen=$("$ROOT/bin/fm-busy-event.sh" arm "$dir/home/state" t1)
|
|
766
|
+
printf 'busy_gen=%s\n' "$gen" >> "$dir/home/state/t1.meta"
|
|
767
|
+
out=$(FM_FAKE_INTERRUPT_STOPS_AGENT=1 run_control "$dir" t1 exit); rc=$?
|
|
768
|
+
expect_code 0 "$rc" "exit should accept a busy agent stopped by interrupt"$'\n'"$out"
|
|
769
|
+
assert_contains "$out" "stopped t1 harness=claude" \
|
|
770
|
+
"the authoritative gone-state should complete exit successfully"
|
|
771
|
+
[ "$(keys_sent "$dir")" = Escape ] \
|
|
772
|
+
|| fail "exit should deliver the busy agent's interrupt sequence"
|
|
773
|
+
[ -z "$(literals "$dir")" ] \
|
|
774
|
+
|| fail "exit should not type a command after interrupt already stopped the agent"
|
|
775
|
+
[ ! -e "$dir/home/state/t1.busy-gen" ] && [ ! -e "$dir/home/state/t1.busy-state" ] \
|
|
776
|
+
|| fail "exit should retire busy wiring for an agent stopped by interrupt"
|
|
777
|
+
pass "fm-control exit: an interrupt-stopped agent satisfies the gone-state postcondition"
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
test_agent_that_does_not_stop_fails_closed() {
|
|
781
|
+
local dir out rc gen
|
|
782
|
+
dir=$(new_case stubborn)
|
|
783
|
+
add_task "$dir" t1 claude
|
|
784
|
+
alive_as "$dir" claude
|
|
785
|
+
gen=$("$ROOT/bin/fm-busy-event.sh" arm "$dir/home/state" t1)
|
|
786
|
+
printf 'busy_gen=%s\n' "$gen" >> "$dir/home/state/t1.meta"
|
|
787
|
+
out=$(env FM_FAKE_NEVER_DIES=1 PATH="$dir/fakebin:$PATH" FM_HOME="$dir/home" \
|
|
788
|
+
FM_FAKE_DIR="$dir/fake" FM_CONTROL_POLL=0.01 FM_CONTROL_EXIT_WAIT=0.05 \
|
|
789
|
+
"$CONTROL" t1 exit 2>&1); rc=$?
|
|
790
|
+
expect_code 1 "$rc" "an agent that ignores its exit command should fail closed"
|
|
791
|
+
assert_contains "$out" "did not stop" "the failure should say the agent did not stop"
|
|
792
|
+
assert_contains "$out" "exit-delivered t1 interrupt=delivered verified=agent-alive cancel=unconfirmed exit-command=delivered agent-state=alive exit=unconfirmed" \
|
|
793
|
+
"the failure should distinguish delivered lifecycle input from the unconfirmed exit"
|
|
794
|
+
assert_not_contains "$out" "nothing was changed" \
|
|
795
|
+
"the failure must not deny the lifecycle input that was delivered"
|
|
796
|
+
[ "$(keys_sent "$dir")" = Escape ] \
|
|
797
|
+
|| fail "a stubborn busy agent should receive its interrupt sequence"
|
|
798
|
+
[ "$(literals "$dir")" = /exit ] \
|
|
799
|
+
|| fail "a stubborn busy agent should receive its exit command"
|
|
800
|
+
pass "fm-control exit: a stubborn agent reports delivered input and an unconfirmed exit"
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
test_grok_interrupt_without_acknowledgement_reports_unconfirmed() {
|
|
804
|
+
local dir out rc
|
|
805
|
+
dir=$(new_case nosettle)
|
|
806
|
+
add_task "$dir" t1 grok
|
|
807
|
+
alive_as "$dir" grok
|
|
808
|
+
printf '╭────╮\n│ │\n╰────╯\n Ctrl+c:cancel\n' > "$dir/fake/pane"
|
|
809
|
+
out=$(run_control "$dir" t1 interrupt); rc=$?
|
|
810
|
+
expect_code 0 "$rc" "grok interrupt delivery should not depend on inferred cancellation"$'\n'"$out"
|
|
811
|
+
assert_contains "$out" "verified=agent-alive cancel=unconfirmed" \
|
|
812
|
+
"a rendered busy hint is not a cancellation acknowledgement"
|
|
813
|
+
pass "fm-control interrupt: grok reports delivery without claiming cancellation"
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
test_grok_idle_footer_does_not_confirm_cancellation() {
|
|
817
|
+
local dir out rc
|
|
818
|
+
dir=$(new_case settles)
|
|
819
|
+
add_task "$dir" t1 grok
|
|
820
|
+
alive_as "$dir" grok
|
|
821
|
+
printf '╭────╮\n│ │\n╰────╯\n Shift+Tab:mode │ Ctrl+.:shortcuts\n' > "$dir/fake/pane"
|
|
822
|
+
out=$(run_control "$dir" t1 interrupt); rc=$?
|
|
823
|
+
expect_code 0 "$rc" "grok interrupt delivery should succeed"$'\n'"$out"
|
|
824
|
+
assert_contains "$out" "verified=agent-alive cancel=unconfirmed" \
|
|
825
|
+
"an idle footer is not an explicit cancellation acknowledgement"
|
|
826
|
+
[ "$(keys_sent "$dir")" = "C-c" ] || fail "grok should receive C-c, got: $(keys_sent "$dir")"
|
|
827
|
+
pass "fm-control interrupt: grok's idle footer does not confirm cancellation"
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
# --- 6. marker non-regression -----------------------------------------------
|
|
831
|
+
|
|
832
|
+
test_secondmate_control_command_carries_no_marker() {
|
|
833
|
+
local dir out rc typed home
|
|
834
|
+
dir=$(new_case sm-marker)
|
|
835
|
+
home="$dir/home"
|
|
836
|
+
add_task "$dir" domain claude secondmate
|
|
837
|
+
# A secondmate's worktree IS its home; give it the marker its records need.
|
|
838
|
+
printf '%s\n' domain > "$dir/wt-domain/.fm-secondmate-home"
|
|
839
|
+
alive_as "$dir" claude
|
|
840
|
+
out=$(run_control "$dir" domain exit); rc=$?
|
|
841
|
+
expect_code 0 "$rc" "exiting a secondmate's agent should succeed"$'\n'"$out"
|
|
842
|
+
typed=$(literals "$dir")
|
|
843
|
+
[ "$typed" = "/exit" ] \
|
|
844
|
+
|| fail "a secondmate control command must be the bare exit command, got: $typed"
|
|
845
|
+
case "$typed" in
|
|
846
|
+
*"$FM_FROMFIRST_MARK"*) fail "a control command must never carry the from-firstmate marker" ;;
|
|
847
|
+
esac
|
|
848
|
+
case "$typed" in
|
|
849
|
+
*corr=*) fail "a control command must never carry a pending-reply correlation id" ;;
|
|
850
|
+
esac
|
|
851
|
+
[ -z "$(find "$home/state/pending-replies" -type f 2>/dev/null | head -n 1)" ] \
|
|
852
|
+
|| fail "a control command must not open a pending-reply expectation"
|
|
853
|
+
pass "fm-control: a lifecycle command to a secondmate is unmarked and opens no reply expectation"
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
test_fm_send_still_marks_the_same_secondmate_task() {
|
|
857
|
+
local dir log out rc
|
|
858
|
+
dir=$(new_case sm-send)
|
|
859
|
+
add_task "$dir" domain claude secondmate
|
|
860
|
+
log="$dir/fake/sendlog"
|
|
861
|
+
: > "$log"
|
|
862
|
+
out=$(env PATH="$dir/fakebin:$PATH" FM_HOME="$dir/home" FM_FAKE_DIR="$dir/fake" \
|
|
863
|
+
FM_SEND_SETTLE=0 FM_ROOT_OVERRIDE="$dir/home" \
|
|
864
|
+
"$SEND" domain "audit the build" 2>&1); rc=$?
|
|
865
|
+
expect_code 0 "$rc" "fm-send to a secondmate should still succeed"$'\n'"$out"
|
|
866
|
+
# The marked steer rides fm-send's durable inbox plane; only the doorbell is
|
|
867
|
+
# typed, so the marker is asserted on the recorded body.
|
|
868
|
+
case "$(bash -c '. "$1"; fm_task_inbox_body "$2"' _ "$ROOT/bin/fm-task-inbox-lib.sh" \
|
|
869
|
+
"$dir/home/state/domain.inbox/001.msg")" in
|
|
870
|
+
"$FM_FROMFIRST_MARK"*) : ;;
|
|
871
|
+
*) fail "fm-send must still mark a kind=secondmate target: $(literals "$dir")" ;;
|
|
872
|
+
esac
|
|
873
|
+
pass "fm-control's arrival leaves fm-send's from-firstmate marking untouched"
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
test_exit_types_each_harness_verified_command
|
|
877
|
+
test_interrupt_sends_each_harness_verified_key
|
|
878
|
+
test_opencode_interrupts_twice_and_others_once
|
|
879
|
+
test_unverified_harness_is_refused
|
|
880
|
+
test_harness_family_resolution
|
|
881
|
+
test_prefixed_recorded_harness_reaches_each_control_verb
|
|
882
|
+
test_backend_key_capability_matrix
|
|
883
|
+
test_harness_kind_capability
|
|
884
|
+
test_orca_refuses_an_escape_harness_interrupt
|
|
885
|
+
test_unverified_state_backends_refuse_stop_verbs
|
|
886
|
+
test_state_verified_backends_are_exactly_tmux_and_herdr
|
|
887
|
+
test_window_label_is_refused_with_the_exact_id
|
|
888
|
+
test_explicit_endpoint_is_refused
|
|
889
|
+
test_unknown_task_is_refused
|
|
890
|
+
test_record_bound_to_another_task_is_refused
|
|
891
|
+
test_remote_secondmate_is_refused_by_placement
|
|
892
|
+
test_interrupt_and_exit_lock_before_task_state_resolution
|
|
893
|
+
test_verb_allowlist_is_closed
|
|
894
|
+
test_resume_is_refused_with_its_reason
|
|
895
|
+
test_relaunch_only_flags_are_rejected_on_other_verbs
|
|
896
|
+
test_already_stopped_exit_is_idempotent
|
|
897
|
+
test_missing_endpoint_refuses
|
|
898
|
+
test_interrupt_refuses_when_no_agent_runs
|
|
899
|
+
test_ambiguous_endpoint_refuses
|
|
900
|
+
test_busy_agent_is_interrupted_before_the_exit_command
|
|
901
|
+
test_idle_agent_is_not_interrupted
|
|
902
|
+
test_interrupt_without_acknowledgement_preserves_busy_state
|
|
903
|
+
test_muse_interrupt_confirms_adapter_acknowledgement
|
|
904
|
+
test_interrupt_revalidates_agent_after_acknowledgement_wait
|
|
905
|
+
test_exit_accepts_agent_stopped_by_busy_interrupt
|
|
906
|
+
test_agent_that_does_not_stop_fails_closed
|
|
907
|
+
test_grok_interrupt_without_acknowledgement_reports_unconfirmed
|
|
908
|
+
test_grok_idle_footer_does_not_confirm_cancellation
|
|
909
|
+
test_secondmate_control_command_carries_no_marker
|
|
910
|
+
test_fm_send_still_marks_the_same_secondmate_task
|