@zerwiz/ymir 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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 +290 -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 +50 -0
- package/.agents/config/agents.yaml.example +72 -0
- package/.agents/config/crew-dispatch.json +22 -0
- package/.agents/config/cron.yaml +9 -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/config/tailscale-sync.yaml +27 -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/kaia.engram +0 -0
- package/.agents/memory/kaia.engram-shm +0 -0
- package/.agents/memory/kaia.engram-wal +0 -0
- package/.agents/memory/runes_audit.md +5 -0
- package/.agents/memory/well/episodes.jsonl +367 -0
- package/.agents/memory/well/workspace.jsonl +27 -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 +111 -0
- package/.agents/migrations/0004-hoard-and-realms.sh +104 -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 +158 -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 +673 -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 +496 -0
- package/.agents/skills/galdr-ymirsystem/assets/installation.md +571 -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 +161 -0
- package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +377 -0
- package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
- package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +129 -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 +96 -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 +175 -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 +360 -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 +70 -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 +52 -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 +34 -0
- package/.agents/skills/ymir-host/assets/desktop.md +36 -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 +454 -0
- package/CHANGELOG.md +881 -0
- package/LICENSE +1 -1
- package/NOTICE +63 -0
- package/README.md +113 -39
- 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 +65 -0
- package/RULES/05-platforms.md +67 -0
- package/RULES/06-append-only.md +58 -0
- package/RULES/07-config.md +45 -0
- package/RULES/08-delivery-gate.md +75 -0
- package/RULES/README.md +20 -0
- package/bin/README.md +107 -0
- package/bin/a2a-mcp.sh +98 -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 +233 -0
- package/bin/apodex-smoke-test.sh +92 -0
- package/bin/bifrost-bridge.sh +112 -0
- package/bin/bifrost-ingress.sh +77 -0
- package/bin/bootstrap-macos.sh +107 -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 +38 -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-guard.sh +165 -0
- package/bin/crash-sense.sh +160 -0
- package/bin/desktop-place.sh +224 -0
- package/bin/docs-guard.sh +39 -0
- package/bin/editor-place.sh +207 -0
- package/bin/eindri-acclaim.sh +34 -0
- package/bin/eindri-control.sh +28 -0
- package/bin/eindri-role.sh +83 -0
- package/bin/eindri-seen.sh +41 -0
- package/bin/eindri-send.sh +15 -0
- package/bin/eindri-start.sh +85 -0
- package/bin/eindri-watch.sh +57 -0
- package/bin/einherjar-spawn.sh +576 -0
- package/bin/eir-doctor.sh +112 -0
- package/bin/erindi-brief.sh +271 -0
- package/bin/fleet-apply.sh +68 -0
- package/bin/github-deploy.sh +81 -0
- package/bin/gjallarhorn-expose.sh +102 -0
- package/bin/gjallarhorn-notify.sh +58 -0
- package/bin/gjallarhorn-purge.sh +54 -0
- package/bin/gjallarhorn-tunnel.sh +72 -0
- package/bin/gleipnir-lock-lib.sh +284 -0
- package/bin/groa-update.sh +122 -0
- package/bin/hall-snapshot.sh +92 -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 +431 -0
- package/bin/hermes-ensure.sh +88 -0
- package/bin/hoard-lib.sh +25 -0
- package/bin/hodd.sh +78 -0
- package/bin/host-sense.sh +199 -0
- package/bin/huginn-research-worker.sh +108 -0
- package/bin/local-model-lock.sh +51 -0
- package/bin/mimir-bridge.py +226 -0
- package/bin/mimir-bridge.sh +87 -0
- package/bin/mimir-ingest.sh +131 -0
- package/bin/mimir.sh +113 -0
- package/bin/mjollnir-webhook.sh +59 -0
- package/bin/mjollnir.sh +85 -0
- package/bin/model-hardware.sh +82 -0
- package/bin/model-resolve.sh +147 -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-memory-housekeeping.sh +114 -0
- package/bin/nornir-job-observer.sh +166 -0
- package/bin/nornir-job-skillopt-sleep.sh +76 -0
- package/bin/omarchy-hook-install.sh +60 -0
- package/bin/omarchy-install.sh +148 -0
- package/bin/omarchy-plugins.sh +174 -0
- package/bin/omarchy-sense.sh +149 -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-seat.sh +63 -0
- package/bin/prereq-ensure.sh +166 -0
- package/bin/private-guard.sh +38 -0
- package/bin/project-git.sh +55 -0
- package/bin/public-guard.sh +70 -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 +91 -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 +90 -0
- package/bin/smidja-observe.sh +86 -0
- package/bin/syn-arm-pretool-check.sh +57 -0
- package/bin/syn-asset-pretool-check.sh +76 -0
- package/bin/syn-cd-pretool-check.sh +28 -0
- package/bin/syn-guard-pretool-check.sh +83 -0
- package/bin/syn-turnend-guard.sh +36 -0
- package/bin/syn-watch-arm.sh +99 -0
- package/bin/tailscale-sync.sh +190 -0
- package/bin/telegram-bot.sh +69 -0
- package/bin/toolchain.sh +74 -0
- package/bin/utgard.sh +111 -0
- package/bin/valhalla.sh +115 -0
- package/bin/valknut-load.sh +260 -0
- package/bin/vor-crew-state.sh +124 -0
- package/bin/wedge-notify.sh +47 -0
- package/bin/workspace-provision.sh +80 -0
- package/bin/workspace-rag.sh +150 -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 +641 -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-platform.sh +348 -0
- package/bin/ymir-say.sh +97 -0
- package/bin/ymir-setup-auth.sh +108 -0
- package/bin/ymir-validate.sh +173 -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 +1056 -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 +44 -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 +210 -0
- package/scripts/start.sh +167 -0
- package/scripts/stop.sh +113 -0
- package/install.sh +0 -35
|
@@ -0,0 +1,708 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Behavior tests for Cursor Agent CLI as a firstmate PRIMARY
|
|
3
|
+
# (docs/turnend-guard.md, docs/sessionstart-nudge.md,
|
|
4
|
+
# docs/supervision-protocols/cursor.md).
|
|
5
|
+
#
|
|
6
|
+
# Four layers, all hermetic over temp dirs with real processes and NO cursor
|
|
7
|
+
# installed, so CI enforces them everywhere:
|
|
8
|
+
# HOST GUARD - bin/fm-hook-host-lib.sh, and each tracked Claude-shaped hook
|
|
9
|
+
# entrypoint standing down on a Cursor-delivered payload, which
|
|
10
|
+
# is what keeps a Cursor primary from running every covered
|
|
11
|
+
# event twice.
|
|
12
|
+
# PARK - bin/fm-turnend-guard-cursor.sh, the stop-hook park: its
|
|
13
|
+
# follow-up sources, its double loop bound, its bounded repair
|
|
14
|
+
# nag, and its post-claim supersession contract.
|
|
15
|
+
# SESSION - bin/fm-sessionstart-cursor.sh, which injects the digest at
|
|
16
|
+
# sessionStart.
|
|
17
|
+
#
|
|
18
|
+
# The park runs as a child of a fake harness (a bash symlink named cursor-agent)
|
|
19
|
+
# whose pid holds the fixture home's session lock, so the real Cursor ancestry
|
|
20
|
+
# path in bin/fm-session-lock-lib.sh is exercised rather than stubbed.
|
|
21
|
+
# tests/fm-cursor-primary-live-e2e.test.sh is the opt-in guard against a real
|
|
22
|
+
# cursor-agent. Neither replaces the other.
|
|
23
|
+
# shellcheck disable=SC2016 # single quotes are deliberate: $FM_HOME expands inside the fake harness child
|
|
24
|
+
set -u
|
|
25
|
+
|
|
26
|
+
# shellcheck source=tests/lib.sh
|
|
27
|
+
. "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
|
28
|
+
|
|
29
|
+
TMP_ROOT=$(fm_test_tmproot fm-cursor-primary)
|
|
30
|
+
fm_git_identity fmtest fmtest@example.invalid
|
|
31
|
+
|
|
32
|
+
FAKEBIN=$(fm_fakebin "$TMP_ROOT/fakebin")
|
|
33
|
+
# Use a real executable whose own canonical basename is cursor-agent. A symlink
|
|
34
|
+
# to bash is not sufficient on Linux: /proc resolves it to bash, so the real
|
|
35
|
+
# Cursor ancestry classifier correctly rejects that process as an impostor.
|
|
36
|
+
CC_BIN=$(command -v cc 2>/dev/null || command -v gcc 2>/dev/null || true)
|
|
37
|
+
[ -n "$CC_BIN" ] || fail "a C compiler is required to build the fake Cursor process"
|
|
38
|
+
cat > "$TMP_ROOT/fake-cursor.c" <<'C'
|
|
39
|
+
#include <errno.h>
|
|
40
|
+
#include <string.h>
|
|
41
|
+
#include <sys/wait.h>
|
|
42
|
+
#include <unistd.h>
|
|
43
|
+
|
|
44
|
+
int main(int argc, char **argv) {
|
|
45
|
+
int status;
|
|
46
|
+
pid_t child;
|
|
47
|
+
if (argc != 3 || strcmp(argv[1], "-c") != 0) return 64;
|
|
48
|
+
child = fork();
|
|
49
|
+
if (child < 0) return 70;
|
|
50
|
+
if (child == 0) {
|
|
51
|
+
execl("/bin/bash", "bash", "-c", argv[2], (char *)0);
|
|
52
|
+
_exit(127);
|
|
53
|
+
}
|
|
54
|
+
while (waitpid(child, &status, 0) < 0) {
|
|
55
|
+
if (errno != EINTR) return 71;
|
|
56
|
+
}
|
|
57
|
+
if (WIFEXITED(status)) return WEXITSTATUS(status);
|
|
58
|
+
if (WIFSIGNALED(status)) return 128 + WTERMSIG(status);
|
|
59
|
+
return 72;
|
|
60
|
+
}
|
|
61
|
+
C
|
|
62
|
+
"$CC_BIN" -o "$FAKEBIN/cursor-agent" "$TMP_ROOT/fake-cursor.c" \
|
|
63
|
+
|| fail "could not build the fake Cursor process"
|
|
64
|
+
FAKE_CURSOR="$FAKEBIN/cursor-agent"
|
|
65
|
+
|
|
66
|
+
CURSOR_PAYLOAD='{"session_id":"sess-cursor","generation_id":"gen-1","loop_count":0,"status":"completed","hook_event_name":"stop","cursor_version":"2026.08.11-e8db854"}'
|
|
67
|
+
CLAUDE_STOP_PAYLOAD='{"session_id":"sess-claude","stop_hook_active":false}'
|
|
68
|
+
|
|
69
|
+
install_scripts() {
|
|
70
|
+
local dir=$1 f
|
|
71
|
+
mkdir -p "$dir/bin" "$dir/docs"
|
|
72
|
+
for f in fm-turnend-guard-cursor.sh fm-turnend-guard.sh fm-sessionstart-cursor.sh \
|
|
73
|
+
fm-sessionstart-run.sh fm-sessionstart-nudge.sh fm-arm-pretool-check.sh \
|
|
74
|
+
fm-cd-pretool-check.sh fm-claude-stop-autoarm.sh fm-hook-host-lib.sh \
|
|
75
|
+
fm-primary-scope-lib.sh fm-supervision-lib.sh fm-wake-lib.sh \
|
|
76
|
+
fm-session-lock-lib.sh fm-cursor-lib.sh fm-operational-input.sh \
|
|
77
|
+
fm-supervision-instructions.sh fm-harness.sh fm-lock.sh \
|
|
78
|
+
fm-gate-refuse-lib.sh; do
|
|
79
|
+
cp "$ROOT/bin/$f" "$dir/bin/$f"
|
|
80
|
+
done
|
|
81
|
+
cp "$ROOT/bin/fm-arm-command-policy.mjs" "$dir/bin/fm-arm-command-policy.mjs"
|
|
82
|
+
cp "$ROOT/bin/fm-cd-command-policy.mjs" "$dir/bin/fm-cd-command-policy.mjs"
|
|
83
|
+
cp -R "$ROOT/docs/supervision-protocols" "$dir/docs/supervision-protocols"
|
|
84
|
+
chmod +x "$dir"/bin/*.sh
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
make_primary_dir() {
|
|
88
|
+
local dir=$1
|
|
89
|
+
mkdir -p "$dir/state"
|
|
90
|
+
git init -q "$dir"
|
|
91
|
+
git -C "$dir" commit -q --allow-empty -m init
|
|
92
|
+
: > "$dir/AGENTS.md"
|
|
93
|
+
install_scripts "$dir"
|
|
94
|
+
printf '%s\n' "$dir"
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# An arm fixture standing in for bin/fm-watch-arm.sh. Real process, real output.
|
|
98
|
+
write_arm_fixture() { # <dir> <kind>
|
|
99
|
+
local dir=$1 kind=$2
|
|
100
|
+
case "$kind" in
|
|
101
|
+
actionable)
|
|
102
|
+
cat > "$dir/bin/fm-watch-arm.sh" <<'SH'
|
|
103
|
+
#!/usr/bin/env bash
|
|
104
|
+
printf '%s\n' "$$" >> "$FM_HOME/state/arm-ran"
|
|
105
|
+
printf 'watcher: started pid=%s (beacon fresh)\n' "$$"
|
|
106
|
+
printf 'stale: fixture-win needs a look\n'
|
|
107
|
+
exit 0
|
|
108
|
+
SH
|
|
109
|
+
;;
|
|
110
|
+
failed)
|
|
111
|
+
cat > "$dir/bin/fm-watch-arm.sh" <<'SH'
|
|
112
|
+
#!/usr/bin/env bash
|
|
113
|
+
printf '%s\n' "$$" >> "$FM_HOME/state/arm-ran"
|
|
114
|
+
printf 'watcher: FAILED - no live watcher with a fresh beacon\n'
|
|
115
|
+
exit 1
|
|
116
|
+
SH
|
|
117
|
+
;;
|
|
118
|
+
switchable)
|
|
119
|
+
# Slow until state/arm-fast appears, so a second invocation can be made
|
|
120
|
+
# fast WITHOUT rewriting a script the first one is still executing.
|
|
121
|
+
cat > "$dir/bin/fm-watch-arm.sh" <<'SH'
|
|
122
|
+
#!/usr/bin/env bash
|
|
123
|
+
printf '%s\n' "$$" >> "$FM_HOME/state/arm-ran"
|
|
124
|
+
if [ -e "$FM_HOME/state/arm-fast" ]; then
|
|
125
|
+
printf 'stale: fixture-win fast\n'
|
|
126
|
+
exit 0
|
|
127
|
+
fi
|
|
128
|
+
sleep 30
|
|
129
|
+
printf 'stale: fixture-win late\n'
|
|
130
|
+
exit 0
|
|
131
|
+
SH
|
|
132
|
+
;;
|
|
133
|
+
esac
|
|
134
|
+
chmod +x "$dir/bin/fm-watch-arm.sh"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# The park's child body: claim the home lock as this fake harness process, then
|
|
138
|
+
# run the adapter as its child, so the real Cursor ancestry path decides lock
|
|
139
|
+
# ownership on every platform. Keep the fake harness process alive: Linux
|
|
140
|
+
# changes the process identity when an exec reaches the adapter's shebang.
|
|
141
|
+
PARK_CHILD='
|
|
142
|
+
printf "%s\n" "$$" > "$FM_HOME/state/.lock"
|
|
143
|
+
"$FM_HOME/bin/fm-turnend-guard-cursor.sh"
|
|
144
|
+
'
|
|
145
|
+
|
|
146
|
+
# Run the park as a child of the fake cursor harness that holds the home lock.
|
|
147
|
+
# Clear PI_CODING_AGENT so a Pi host session running this suite cannot make the
|
|
148
|
+
# Cursor park stand down before the fixture under test is exercised.
|
|
149
|
+
run_park() { # <dir> [loop_count] [loop_ceiling]
|
|
150
|
+
local dir=$1 loop=${2:-0} ceiling=${3:-} payload
|
|
151
|
+
payload=$(printf '{"session_id":"sess-cursor","generation_id":"gen-%s","loop_count":%s,"status":"completed","hook_event_name":"stop","cursor_version":"2026.08.11-e8db854"}' "$loop" "$loop")
|
|
152
|
+
if [ -n "$ceiling" ]; then
|
|
153
|
+
printf '%s' "$payload" | env -u PI_CODING_AGENT FM_HOME="$dir" FM_CURSOR_PARK_POLL=1 \
|
|
154
|
+
FM_CURSOR_TURNEND_LOOP_CEILING="$ceiling" "$FAKE_CURSOR" -c "$PARK_CHILD" 2>/dev/null
|
|
155
|
+
else
|
|
156
|
+
printf '%s' "$payload" | env -u PI_CODING_AGENT FM_HOME="$dir" FM_CURSOR_PARK_POLL=1 \
|
|
157
|
+
"$FAKE_CURSOR" -c "$PARK_CHILD" 2>/dev/null
|
|
158
|
+
fi
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
run_session() { # <dir> <event> <source> [session-id]
|
|
162
|
+
local dir=$1 event=$2 source=$3 session_id=${4:-sess-cursor} payload
|
|
163
|
+
payload=$(printf '{"hook_event_name":"%s","session_id":"%s","cursor_version":"x"}' "$event" "$session_id")
|
|
164
|
+
printf '%s' "$payload" | FM_HOME="$dir" FM_SESSION_SOURCE="$source" "$FAKE_CURSOR" -c '
|
|
165
|
+
printf "%s\n" "$$" > "$FM_HOME/state/.lock"
|
|
166
|
+
"$FM_HOME/bin/fm-sessionstart-cursor.sh" --source "$FM_SESSION_SOURCE"
|
|
167
|
+
' 2>/dev/null
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
followup_of() { # <json>
|
|
171
|
+
printf '%s' "$1" | jq -r '.followup_message // empty' 2>/dev/null
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
kind_of_followup() { # <json> -> the operational kind
|
|
175
|
+
local body
|
|
176
|
+
body=$(followup_of "$1")
|
|
177
|
+
[ -n "$body" ] || return 1
|
|
178
|
+
printf '%s' "$body" | "$ROOT/bin/fm-operational-input.sh" kind
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# --- HOST GUARD --------------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
test_turnend_guard_stands_down_on_cursor_payload() {
|
|
184
|
+
local dir out status
|
|
185
|
+
dir=$(make_primary_dir "$TMP_ROOT/host-turnend")
|
|
186
|
+
: > "$dir/state/task1.meta"
|
|
187
|
+
out=$(printf '%s' "$CURSOR_PAYLOAD" | bash "$dir/bin/fm-turnend-guard.sh" 2>&1); status=$?
|
|
188
|
+
expect_code 0 "$status" "a Cursor-delivered Stop payload must not block through the Claude-settings duplicate"
|
|
189
|
+
[ -z "$out" ] || fail "duplicate entry produced output: $out"
|
|
190
|
+
out=$(printf '%s' "$CURSOR_PAYLOAD" | bash "$dir/bin/fm-turnend-guard.sh" --cursor 2>&1); status=$?
|
|
191
|
+
expect_code 2 "$status" "--cursor must let Cursor's own adapter reach the shared block decision"
|
|
192
|
+
case "$out" in *'TURN WOULD END BLIND'*) ;; *) fail "expected the shared banner, got: $out" ;; esac
|
|
193
|
+
pass "fm-turnend-guard: Cursor payload is inert without --cursor and blocks with it"
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
test_turnend_guard_still_blocks_for_claude_payload() {
|
|
197
|
+
local dir status
|
|
198
|
+
dir=$(make_primary_dir "$TMP_ROOT/host-claude")
|
|
199
|
+
: > "$dir/state/task1.meta"
|
|
200
|
+
printf '%s' "$CLAUDE_STOP_PAYLOAD" | bash "$dir/bin/fm-turnend-guard.sh" >/dev/null 2>&1
|
|
201
|
+
status=$?
|
|
202
|
+
expect_code 2 "$status" "the host guard must not disturb a genuine Claude Stop payload"
|
|
203
|
+
pass "fm-turnend-guard: a non-Cursor payload keeps blocking"
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
test_autoarm_stands_down_on_cursor_payload() {
|
|
207
|
+
local dir status
|
|
208
|
+
dir=$(make_primary_dir "$TMP_ROOT/host-autoarm")
|
|
209
|
+
: > "$dir/state/task1.meta"
|
|
210
|
+
write_arm_fixture "$dir" actionable
|
|
211
|
+
printf '%s' "$CURSOR_PAYLOAD" | FM_HOME="$dir" "$FAKE_CURSOR" -c '
|
|
212
|
+
printf "%s\n" "$$" > "$FM_HOME/state/.lock"
|
|
213
|
+
exec "$FM_HOME/bin/fm-claude-stop-autoarm.sh"
|
|
214
|
+
' >/dev/null 2>&1
|
|
215
|
+
status=$?
|
|
216
|
+
expect_code 0 "$status" "the Claude auto-arm must stay inert under Cursor"
|
|
217
|
+
[ ! -e "$dir/state/arm-ran" ] || fail "the Claude auto-arm armed under a Cursor payload; on Cursor it would run synchronously and hold the turn open for its multi-hour timeout"
|
|
218
|
+
pass "fm-claude-stop-autoarm: inert on a Cursor-delivered payload"
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
test_sessionstart_run_stands_down_on_cursor_payload() {
|
|
222
|
+
local dir out
|
|
223
|
+
dir=$(make_primary_dir "$TMP_ROOT/host-sessionstart")
|
|
224
|
+
cat > "$dir/bin/fm-session-start.sh" <<'SH'
|
|
225
|
+
#!/usr/bin/env bash
|
|
226
|
+
printf '%s\n' "$$" >> "$FM_HOME/state/digest-ran"
|
|
227
|
+
printf 'DIGEST BODY\n'
|
|
228
|
+
SH
|
|
229
|
+
chmod +x "$dir/bin/fm-session-start.sh"
|
|
230
|
+
out=$(printf '%s' "$CURSOR_PAYLOAD" | FM_HOME="$dir" bash "$dir/bin/fm-sessionstart-run.sh" 2>&1)
|
|
231
|
+
[ -z "$out" ] || fail "the run wrapper emitted a digest for the Cursor duplicate: $out"
|
|
232
|
+
[ ! -e "$dir/state/digest-ran" ] || fail "the run wrapper took the helm twice under Cursor"
|
|
233
|
+
out=$(printf '{"source":"startup","session_id":"s"}' | FM_HOME="$dir" bash "$dir/bin/fm-sessionstart-run.sh" 2>&1)
|
|
234
|
+
case "$out" in *'DIGEST BODY'*) ;; *) fail "a Claude-shaped payload must still run the digest, got: $out" ;; esac
|
|
235
|
+
pass "fm-sessionstart-run: inert on a Cursor payload, unchanged otherwise"
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
test_pretool_guards_deduplicate_and_render_cursor_deny() {
|
|
239
|
+
local dir payload out status decision
|
|
240
|
+
dir=$(make_primary_dir "$TMP_ROOT/host-pretool")
|
|
241
|
+
payload='{"tool_name":"Shell","tool_input":{"command":"bin/fm-watch-arm.sh &"},"cursor_version":"2026.08.11-e8db854"}'
|
|
242
|
+
out=$(printf '%s' "$payload" | bash "$dir/bin/fm-arm-pretool-check.sh" 2>&1); status=$?
|
|
243
|
+
expect_code 0 "$status" "the Claude-settings duplicate must allow under Cursor"
|
|
244
|
+
[ -z "$out" ] || fail "duplicate pretool entry produced output: $out"
|
|
245
|
+
|
|
246
|
+
out=$(printf '%s' "$payload" | bash "$dir/bin/fm-arm-pretool-check.sh" --cursor 2>/dev/null); status=$?
|
|
247
|
+
expect_code 0 "$status" "Cursor reads the decision object, so the deny path exits 0"
|
|
248
|
+
decision=$(printf '%s' "$out" | jq -r '.permission // empty' 2>/dev/null)
|
|
249
|
+
[ "$decision" = deny ] || fail "expected a Cursor deny object on stdout, got: $out"
|
|
250
|
+
printf '%s' "$out" | jq -e '.user_message | type == "string" and length > 0' >/dev/null 2>&1 \
|
|
251
|
+
|| fail "Cursor's deny object must carry a user_message reason, got: $out"
|
|
252
|
+
pass "fm-arm-pretool-check: Cursor duplicate allows, --cursor denies in Cursor's own shape"
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
test_cd_guard_renders_cursor_deny() {
|
|
256
|
+
local dir payload out decision
|
|
257
|
+
dir=$(make_primary_dir "$TMP_ROOT/host-cd")
|
|
258
|
+
payload='{"tool_name":"Shell","tool_input":{"command":"cd projects/example"},"cursor_version":"2026.08.11-e8db854"}'
|
|
259
|
+
out=$(printf '%s' "$payload" | FM_HOME="$dir" bash "$dir/bin/fm-cd-pretool-check.sh" --cursor 2>/dev/null)
|
|
260
|
+
decision=$(printf '%s' "$out" | jq -r '.permission // empty' 2>/dev/null)
|
|
261
|
+
[ "$decision" = deny ] || fail "expected a Cursor deny object from the cd guard, got: $out"
|
|
262
|
+
out=$(printf '%s' "$payload" | FM_HOME="$dir" bash "$dir/bin/fm-cd-pretool-check.sh" 2>&1)
|
|
263
|
+
[ -z "$out" ] || fail "the cd guard's Claude-settings duplicate produced output under Cursor: $out"
|
|
264
|
+
pass "fm-cd-pretool-check: Cursor duplicate allows, --cursor denies in Cursor's own shape"
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
# --- PARK --------------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
test_park_silent_when_nothing_in_flight() {
|
|
270
|
+
local dir out
|
|
271
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-idle")
|
|
272
|
+
write_arm_fixture "$dir" actionable
|
|
273
|
+
out=$(run_park "$dir")
|
|
274
|
+
[ -z "$out" ] || fail "the park emitted a follow-up with nothing in flight: $out"
|
|
275
|
+
[ ! -e "$dir/state/arm-ran" ] || fail "the park armed with nothing to supervise"
|
|
276
|
+
pass "cursor park: silent no-op when no supervision is needed"
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
test_park_delivers_actionable_wake_as_followup() {
|
|
280
|
+
local dir out body
|
|
281
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-wake")
|
|
282
|
+
: > "$dir/state/task1.meta"
|
|
283
|
+
write_arm_fixture "$dir" actionable
|
|
284
|
+
out=$(run_park "$dir")
|
|
285
|
+
[ -e "$dir/state/arm-ran" ] || fail "the park did not run the arm"
|
|
286
|
+
[ "$(kind_of_followup "$out")" = watcher ] \
|
|
287
|
+
|| fail "an actionable close must arrive as a watcher-kind follow-up, got: $out"
|
|
288
|
+
body=$(followup_of "$out")
|
|
289
|
+
case "$body" in *'stale: fixture-win needs a look'*) ;; *) fail "the wake reason was not carried into the follow-up: $body" ;; esac
|
|
290
|
+
case "$body" in *'fm-wake-drain.sh'*) ;; *) fail "the follow-up must tell the session to drain first: $body" ;; esac
|
|
291
|
+
pass "cursor park: an actionable close is delivered as one watcher-kind follow-up"
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
test_park_never_exits_two() {
|
|
295
|
+
local dir status
|
|
296
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-exit")
|
|
297
|
+
: > "$dir/state/task1.meta"
|
|
298
|
+
write_arm_fixture "$dir" failed
|
|
299
|
+
run_park "$dir" >/dev/null; status=$?
|
|
300
|
+
expect_code 0 "$status" "exit 2 is a silent no-op on Cursor's stop step, so the adapter must never use it"
|
|
301
|
+
pass "cursor park: always exits 0, even when supervision is genuinely down"
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
test_park_repair_nag_is_bounded() {
|
|
305
|
+
local dir out i kinds=0
|
|
306
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-nag")
|
|
307
|
+
: > "$dir/state/task1.meta"
|
|
308
|
+
write_arm_fixture "$dir" failed
|
|
309
|
+
for i in 1 2 3; do
|
|
310
|
+
out=$(run_park "$dir")
|
|
311
|
+
[ "$(kind_of_followup "$out")" = turn-end-guard ] \
|
|
312
|
+
|| fail "nag $i should be a turn-end-guard follow-up, got: $out"
|
|
313
|
+
kinds=$((kinds + 1))
|
|
314
|
+
done
|
|
315
|
+
out=$(run_park "$dir")
|
|
316
|
+
[ -z "$out" ] || fail "the repair nag must stop after its budget, got a 4th: $out"
|
|
317
|
+
[ "$kinds" -eq 3 ] || fail "expected exactly 3 bounded nags, saw $kinds"
|
|
318
|
+
pass "cursor park: the repair nag is bounded and then goes quiet"
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
test_park_repair_nag_requires_a_persisted_budget() {
|
|
322
|
+
local dir out
|
|
323
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-nag-write-failure")
|
|
324
|
+
: > "$dir/state/task1.meta"
|
|
325
|
+
mkdir "$dir/state/.turnend-cursor-blocks"
|
|
326
|
+
write_arm_fixture "$dir" failed
|
|
327
|
+
out=$(run_park "$dir")
|
|
328
|
+
[ -z "$out" ] || fail "a repair nag without a persisted budget increment must fail open: $out"
|
|
329
|
+
[ -z "$(find "$dir/state/.turnend-cursor-blocks" -mindepth 1 -print -quit 2>/dev/null)" ] \
|
|
330
|
+
|| fail "the failed budget commit left partial state"
|
|
331
|
+
pass "cursor park: a repair nag is emitted only after its budget persists"
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
test_park_nag_budget_resets_after_a_real_wake() {
|
|
335
|
+
local dir out
|
|
336
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-nag-reset")
|
|
337
|
+
: > "$dir/state/task1.meta"
|
|
338
|
+
write_arm_fixture "$dir" failed
|
|
339
|
+
run_park "$dir" >/dev/null
|
|
340
|
+
run_park "$dir" >/dev/null
|
|
341
|
+
write_arm_fixture "$dir" actionable
|
|
342
|
+
out=$(run_park "$dir")
|
|
343
|
+
[ "$(kind_of_followup "$out")" = watcher ] || fail "expected a real wake, got: $out"
|
|
344
|
+
write_arm_fixture "$dir" failed
|
|
345
|
+
out=$(run_park "$dir")
|
|
346
|
+
[ "$(kind_of_followup "$out")" = turn-end-guard ] \
|
|
347
|
+
|| fail "a productive wake must reset the nag budget, got: $out"
|
|
348
|
+
pass "cursor park: a delivered wake resets the bounded repair budget"
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
test_park_loop_ceiling_warns_once_then_goes_quiet() {
|
|
352
|
+
local dir out body
|
|
353
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-ceiling")
|
|
354
|
+
: > "$dir/state/task1.meta"
|
|
355
|
+
write_arm_fixture "$dir" actionable
|
|
356
|
+
out=$(run_park "$dir" 5 5)
|
|
357
|
+
body=$(followup_of "$out")
|
|
358
|
+
case "$body" in *'CEILING REACHED'*) ;; *) fail "at the ceiling the session must be told once, got: $out" ;; esac
|
|
359
|
+
[ ! -e "$dir/state/arm-ran" ] || fail "the park must not arm at the loop ceiling"
|
|
360
|
+
out=$(run_park "$dir" 6 5)
|
|
361
|
+
[ -z "$out" ] || fail "above the ceiling the adapter must be silent, got: $out"
|
|
362
|
+
pass "cursor park: the loop_count ceiling warns exactly once, then stops the loop"
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
test_park_stands_down_when_superseded() {
|
|
368
|
+
local dir first_out first_pid marker
|
|
369
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-supersede")
|
|
370
|
+
: > "$dir/state/task1.meta"
|
|
371
|
+
write_arm_fixture "$dir" switchable
|
|
372
|
+
marker="$dir/state/first-park-out"
|
|
373
|
+
( run_park "$dir" > "$marker" 2>/dev/null ) &
|
|
374
|
+
first_pid=$!
|
|
375
|
+
local waited=0
|
|
376
|
+
while [ ! -s "$dir/state/.cursor-park-owner" ] || [ ! -e "$dir/state/arm-ran" ]; do
|
|
377
|
+
sleep 0.2
|
|
378
|
+
waited=$((waited + 1))
|
|
379
|
+
[ "$waited" -lt 100 ] || fail "the first park never claimed ownership"
|
|
380
|
+
done
|
|
381
|
+
: > "$dir/state/arm-fast"
|
|
382
|
+
run_park "$dir" >/dev/null 2>&1
|
|
383
|
+
wait "$first_pid" 2>/dev/null || true
|
|
384
|
+
first_out=$(cat "$marker" 2>/dev/null || true)
|
|
385
|
+
[ -z "$first_out" ] || fail "the older park delivered after the newer stop claimed the baton: $first_out"
|
|
386
|
+
pass "cursor park: an older park stands down after a newer stop claim"
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
test_park_serializes_supersession_with_followup_commit() {
|
|
390
|
+
local dir first_pid first_out second_out waited budget_count
|
|
391
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-commit-race")
|
|
392
|
+
: > "$dir/state/task1.meta"
|
|
393
|
+
printf 'session=sess-cursor\ncount=1\n' > "$dir/state/.turnend-cursor-blocks"
|
|
394
|
+
write_arm_fixture "$dir" actionable
|
|
395
|
+
cat >> "$dir/bin/fm-operational-input.sh" <<'SH'
|
|
396
|
+
fm_operational_input_encode() {
|
|
397
|
+
local kind=${1-} body=${2-} result_var=${3-}
|
|
398
|
+
[ -n "$result_var" ] && fm_operational_kind_is_current "$kind" && [ -n "$body" ] || return 2
|
|
399
|
+
if ( set -C; : > "$FM_HOME/state/commit-entered" ) 2>/dev/null; then
|
|
400
|
+
while [ ! -e "$FM_HOME/state/commit-release" ]; do sleep 0.05; done
|
|
401
|
+
fi
|
|
402
|
+
printf -v "$result_var" '%s%s: %s' "$FM_OPERATIONAL_HEADER_PREFIX" "$kind" "$body"
|
|
403
|
+
}
|
|
404
|
+
SH
|
|
405
|
+
( run_park "$dir" > "$dir/state/first-out" ) &
|
|
406
|
+
first_pid=$!
|
|
407
|
+
waited=0
|
|
408
|
+
while [ ! -e "$dir/state/commit-entered" ]; do
|
|
409
|
+
sleep 0.05
|
|
410
|
+
waited=$((waited + 1))
|
|
411
|
+
[ "$waited" -lt 200 ] || fail "the first park never entered follow-up preparation"
|
|
412
|
+
done
|
|
413
|
+
write_arm_fixture "$dir" failed
|
|
414
|
+
second_out=$(run_park "$dir")
|
|
415
|
+
: > "$dir/state/commit-release"
|
|
416
|
+
wait "$first_pid" 2>/dev/null || true
|
|
417
|
+
first_out=$(cat "$dir/state/first-out" 2>/dev/null || true)
|
|
418
|
+
[ -z "$first_out" ] || fail "the older park emitted after a newer stop arrived: $first_out"
|
|
419
|
+
[ "$(kind_of_followup "$second_out")" = turn-end-guard ] \
|
|
420
|
+
|| fail "the newest park did not own the follow-up: $second_out"
|
|
421
|
+
budget_count=$(sed -n '2s/^count=//p' "$dir/state/.turnend-cursor-blocks" 2>/dev/null || true)
|
|
422
|
+
[ "$budget_count" = 2 ] \
|
|
423
|
+
|| fail "the superseded actionable park reset shared nag state: $budget_count"
|
|
424
|
+
pass "cursor park: the newest stop exclusively owns a concurrent commit"
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
test_superseded_park_does_not_consume_nag_budget() {
|
|
428
|
+
local dir first_pid second_out waited budget_count
|
|
429
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-nag-supersede")
|
|
430
|
+
: > "$dir/state/task1.meta"
|
|
431
|
+
write_arm_fixture "$dir" failed
|
|
432
|
+
cat > "$dir/bin/fm-turnend-guard.sh" <<'SH'
|
|
433
|
+
#!/usr/bin/env bash
|
|
434
|
+
if ( set -C; : > "$FM_HOME/state/first-guard-entered" ) 2>/dev/null; then
|
|
435
|
+
while [ ! -e "$FM_HOME/state/first-guard-release" ]; do sleep 0.05; done
|
|
436
|
+
fi
|
|
437
|
+
printf 'fixture supervision failure\n' >&2
|
|
438
|
+
exit 2
|
|
439
|
+
SH
|
|
440
|
+
chmod +x "$dir/bin/fm-turnend-guard.sh"
|
|
441
|
+
( run_park "$dir" > "$dir/state/first-nag-out" ) &
|
|
442
|
+
first_pid=$!
|
|
443
|
+
waited=0
|
|
444
|
+
while [ ! -e "$dir/state/first-guard-entered" ]; do
|
|
445
|
+
sleep 0.05
|
|
446
|
+
waited=$((waited + 1))
|
|
447
|
+
[ "$waited" -lt 200 ] || fail "the first park never reached the guard decision"
|
|
448
|
+
done
|
|
449
|
+
second_out=$(run_park "$dir")
|
|
450
|
+
: > "$dir/state/first-guard-release"
|
|
451
|
+
wait "$first_pid" 2>/dev/null || true
|
|
452
|
+
[ "$(kind_of_followup "$second_out")" = turn-end-guard ] \
|
|
453
|
+
|| fail "the current park did not deliver its repair nag: $second_out"
|
|
454
|
+
[ ! -s "$dir/state/first-nag-out" ] \
|
|
455
|
+
|| fail "the superseded park delivered a stale repair nag"
|
|
456
|
+
budget_count=$(sed -n '2s/^count=//p' "$dir/state/.turnend-cursor-blocks" 2>/dev/null || true)
|
|
457
|
+
[ "$budget_count" = 1 ] \
|
|
458
|
+
|| fail "the superseded park consumed the current park's nag budget: $budget_count"
|
|
459
|
+
pass "cursor park: a superseded park cannot consume repair budget"
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
test_park_inert_when_afk() {
|
|
463
|
+
local dir out
|
|
464
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-afk")
|
|
465
|
+
: > "$dir/state/task1.meta"
|
|
466
|
+
: > "$dir/state/.afk"
|
|
467
|
+
write_arm_fixture "$dir" actionable
|
|
468
|
+
out=$(run_park "$dir")
|
|
469
|
+
[ -z "$out" ] || fail "away mode owns supervision; the park must not wake the primary: $out"
|
|
470
|
+
[ ! -e "$dir/state/arm-ran" ] || fail "the park armed while the away daemon owns the watcher"
|
|
471
|
+
pass "cursor park: inert while away mode is active"
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
test_park_inert_under_pi_coding_agent() {
|
|
475
|
+
local dir out payload
|
|
476
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-pi-host")
|
|
477
|
+
: > "$dir/state/task1.meta"
|
|
478
|
+
write_arm_fixture "$dir" actionable
|
|
479
|
+
payload=$(printf '{"session_id":"sess-cursor","generation_id":"gen-0","loop_count":0,"status":"completed","hook_event_name":"stop","cursor_version":"2026.08.11-e8db854"}')
|
|
480
|
+
# No Cursor identity markers: Pi host alone must stand the park down.
|
|
481
|
+
out=$(printf '%s' "$payload" | env -u CURSOR_AGENT -u CURSOR_INVOKED_AS \
|
|
482
|
+
FM_HOME="$dir" PI_CODING_AGENT=true FM_CURSOR_PARK_POLL=1 \
|
|
483
|
+
"$FAKE_CURSOR" -c "$PARK_CHILD" 2>/dev/null)
|
|
484
|
+
[ -z "$out" ] || fail "Pi-hosted Cursor SDK must not park or wake: $out"
|
|
485
|
+
[ ! -e "$dir/state/arm-ran" ] || fail "the park armed under PI_CODING_AGENT=true"
|
|
486
|
+
pass "cursor park: inert when PI_CODING_AGENT marks a Pi host session"
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
test_park_still_parks_with_pi_leak_and_cursor_identity() {
|
|
490
|
+
local dir out body payload
|
|
491
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-pi-leak-cursor")
|
|
492
|
+
: > "$dir/state/task1.meta"
|
|
493
|
+
write_arm_fixture "$dir" actionable
|
|
494
|
+
payload=$(printf '{"session_id":"sess-cursor","generation_id":"gen-0","loop_count":0,"status":"completed","hook_event_name":"stop","cursor_version":"2026.08.11-e8db854"}')
|
|
495
|
+
# Hand-started cursor-agent may inherit PI_CODING_AGENT; Cursor identity wins.
|
|
496
|
+
out=$(printf '%s' "$payload" | env -u CURSOR_INVOKED_AS \
|
|
497
|
+
FM_HOME="$dir" PI_CODING_AGENT=true CURSOR_AGENT=1 FM_CURSOR_PARK_POLL=1 \
|
|
498
|
+
"$FAKE_CURSOR" -c "$PARK_CHILD" 2>/dev/null)
|
|
499
|
+
[ -e "$dir/state/arm-ran" ] || fail "CURSOR_AGENT must still park despite PI_CODING_AGENT leak"
|
|
500
|
+
[ "$(kind_of_followup "$out")" = watcher ] \
|
|
501
|
+
|| fail "CURSOR_AGENT park must deliver the wake despite PI leak: $out"
|
|
502
|
+
body=$(followup_of "$out")
|
|
503
|
+
case "$body" in *'stale: fixture-win needs a look'*) ;; *) fail "CURSOR_AGENT wake reason missing: $body" ;; esac
|
|
504
|
+
rm -f "$dir/state/arm-ran"
|
|
505
|
+
out=$(printf '%s' "$payload" | env -u CURSOR_AGENT \
|
|
506
|
+
FM_HOME="$dir" PI_CODING_AGENT=true CURSOR_INVOKED_AS=cursor-agent FM_CURSOR_PARK_POLL=1 \
|
|
507
|
+
"$FAKE_CURSOR" -c "$PARK_CHILD" 2>/dev/null)
|
|
508
|
+
[ -e "$dir/state/arm-ran" ] || fail "CURSOR_INVOKED_AS must still park despite PI_CODING_AGENT leak"
|
|
509
|
+
[ "$(kind_of_followup "$out")" = watcher ] \
|
|
510
|
+
|| fail "CURSOR_INVOKED_AS park must deliver the wake despite PI leak: $out"
|
|
511
|
+
pass "cursor park: parks when PI_CODING_AGENT leaks alongside Cursor identity"
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
test_park_stands_down_when_away_mode_activates_before_commit() {
|
|
515
|
+
local dir park_pid out waited budget_count
|
|
516
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-afk-transition")
|
|
517
|
+
: > "$dir/state/task1.meta"
|
|
518
|
+
printf 'session=sess-cursor\ncount=1\n' > "$dir/state/.turnend-cursor-blocks"
|
|
519
|
+
write_arm_fixture "$dir" actionable
|
|
520
|
+
cat >> "$dir/bin/fm-operational-input.sh" <<'SH'
|
|
521
|
+
fm_operational_input_encode() {
|
|
522
|
+
local kind=${1-} body=${2-} result_var=${3-}
|
|
523
|
+
[ -n "$result_var" ] && fm_operational_kind_is_current "$kind" && [ -n "$body" ] || return 2
|
|
524
|
+
: > "$FM_HOME/state/afk-commit-entered"
|
|
525
|
+
while [ ! -e "$FM_HOME/state/afk-commit-release" ]; do sleep 0.05; done
|
|
526
|
+
printf -v "$result_var" '%s%s: %s' "$FM_OPERATIONAL_HEADER_PREFIX" "$kind" "$body"
|
|
527
|
+
}
|
|
528
|
+
SH
|
|
529
|
+
( run_park "$dir" > "$dir/state/afk-transition-out" ) &
|
|
530
|
+
park_pid=$!
|
|
531
|
+
waited=0
|
|
532
|
+
while [ ! -e "$dir/state/afk-commit-entered" ]; do
|
|
533
|
+
sleep 0.05
|
|
534
|
+
waited=$((waited + 1))
|
|
535
|
+
[ "$waited" -lt 200 ] || fail "the park never reached follow-up preparation"
|
|
536
|
+
done
|
|
537
|
+
: > "$dir/state/.afk"
|
|
538
|
+
: > "$dir/state/afk-commit-release"
|
|
539
|
+
wait "$park_pid" 2>/dev/null || true
|
|
540
|
+
out=$(cat "$dir/state/afk-transition-out" 2>/dev/null || true)
|
|
541
|
+
[ -z "$out" ] || fail "the park emitted after away mode activated: $out"
|
|
542
|
+
budget_count=$(sed -n '2s/^count=//p' "$dir/state/.turnend-cursor-blocks" 2>/dev/null || true)
|
|
543
|
+
[ "$budget_count" = 1 ] || fail "the park reset nag state after away mode activated: $budget_count"
|
|
544
|
+
pass "cursor park: an away-mode transition wins before follow-up commit"
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
test_park_inert_without_session_lock() {
|
|
548
|
+
local dir out
|
|
549
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-nolock")
|
|
550
|
+
: > "$dir/state/task1.meta"
|
|
551
|
+
write_arm_fixture "$dir" actionable
|
|
552
|
+
out=$(printf '%s' "$CURSOR_PAYLOAD" | env -u PI_CODING_AGENT FM_HOME="$dir" bash "$dir/bin/fm-turnend-guard-cursor.sh" 2>/dev/null)
|
|
553
|
+
[ -z "$out" ] || fail "a session that does not hold the home lock must not arm or wake: $out"
|
|
554
|
+
[ ! -e "$dir/state/arm-ran" ] || fail "the park armed without owning the session lock"
|
|
555
|
+
pass "cursor park: inert when this session does not hold the home lock"
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
test_park_stands_down_after_session_takeover() {
|
|
559
|
+
local dir park_pid out waited budget_count
|
|
560
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-session-takeover")
|
|
561
|
+
: > "$dir/state/task1.meta"
|
|
562
|
+
printf 'session=sess-cursor\ncount=1\n' > "$dir/state/.turnend-cursor-blocks"
|
|
563
|
+
write_arm_fixture "$dir" switchable
|
|
564
|
+
( run_park "$dir" > "$dir/state/takeover-out" ) &
|
|
565
|
+
park_pid=$!
|
|
566
|
+
waited=0
|
|
567
|
+
while [ ! -e "$dir/state/arm-ran" ]; do
|
|
568
|
+
sleep 0.05
|
|
569
|
+
waited=$((waited + 1))
|
|
570
|
+
[ "$waited" -lt 200 ] || fail "the park never began polling before takeover"
|
|
571
|
+
done
|
|
572
|
+
printf '%s\n' "$$" > "$dir/state/.lock"
|
|
573
|
+
wait "$park_pid" 2>/dev/null || true
|
|
574
|
+
out=$(cat "$dir/state/takeover-out" 2>/dev/null || true)
|
|
575
|
+
[ -z "$out" ] || fail "the replaced session emitted a follow-up after takeover: $out"
|
|
576
|
+
budget_count=$(sed -n '2s/^count=//p' "$dir/state/.turnend-cursor-blocks" 2>/dev/null || true)
|
|
577
|
+
[ "$budget_count" = 1 ] || fail "the replaced session mutated nag state after takeover: $budget_count"
|
|
578
|
+
pass "cursor park: session takeover stops polling without output or state mutation"
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
test_park_inert_in_child_worktree() {
|
|
582
|
+
local base child out
|
|
583
|
+
base=$(make_primary_dir "$TMP_ROOT/park-base")
|
|
584
|
+
child="$TMP_ROOT/park-child"
|
|
585
|
+
fm_git_worktree "$base" "$child" fm/cursor-park-child
|
|
586
|
+
mkdir -p "$child/state"
|
|
587
|
+
: > "$child/AGENTS.md"
|
|
588
|
+
install_scripts "$child"
|
|
589
|
+
: > "$child/state/task1.meta"
|
|
590
|
+
write_arm_fixture "$child" actionable
|
|
591
|
+
out=$(run_park "$child")
|
|
592
|
+
[ -z "$out" ] || fail "a crewmate worktree must stay outside primary scope: $out"
|
|
593
|
+
pass "cursor park: inert inside a child crewmate worktree"
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
test_park_ignores_malformed_payload() {
|
|
597
|
+
local dir out
|
|
598
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-malformed")
|
|
599
|
+
: > "$dir/state/task1.meta"
|
|
600
|
+
write_arm_fixture "$dir" actionable
|
|
601
|
+
out=$(printf 'not json at all' | env -u PI_CODING_AGENT FM_HOME="$dir" bash "$dir/bin/fm-turnend-guard-cursor.sh" 2>/dev/null)
|
|
602
|
+
[ -z "$out" ] || fail "a malformed payload must fail open, got: $out"
|
|
603
|
+
out=$(printf '{"loop_count":"three","cursor_version":"x"}' | env -u PI_CODING_AGENT FM_HOME="$dir" bash "$dir/bin/fm-turnend-guard-cursor.sh" 2>/dev/null)
|
|
604
|
+
[ -z "$out" ] || fail "a non-numeric loop_count must fail open, got: $out"
|
|
605
|
+
pass "cursor park: malformed payloads fail open without arming"
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
# --- SESSION -----------------------------------------------------------------
|
|
609
|
+
|
|
610
|
+
install_digest_fixture() { # <dir>
|
|
611
|
+
cat > "$1/bin/fm-session-start.sh" <<'SH'
|
|
612
|
+
#!/usr/bin/env bash
|
|
613
|
+
printf '%s\n' "$*" >> "$FM_HOME/state/digest-args"
|
|
614
|
+
printf 'FIRSTMATE DIGEST "quoted" line\nsecond line\n'
|
|
615
|
+
SH
|
|
616
|
+
chmod +x "$1/bin/fm-session-start.sh"
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
test_sessionstart_emits_additional_context() {
|
|
620
|
+
local dir out ctx
|
|
621
|
+
dir=$(make_primary_dir "$TMP_ROOT/session-start")
|
|
622
|
+
install_digest_fixture "$dir"
|
|
623
|
+
out=$(run_session "$dir" sessionStart startup)
|
|
624
|
+
ctx=$(printf '%s' "$out" | jq -r '.additional_context // empty' 2>/dev/null)
|
|
625
|
+
case "$ctx" in *'FIRSTMATE DIGEST "quoted" line'*) ;; *) fail "the digest must reach model context verbatim, got: $out" ;; esac
|
|
626
|
+
case "$ctx" in *'second line'*) ;; *) fail "the digest was truncated at the first line: $ctx" ;; esac
|
|
627
|
+
grep -q -- '--source startup' "$dir/state/digest-args" \
|
|
628
|
+
|| fail "the adapter must supply --source itself; Cursor's payload has no source field"
|
|
629
|
+
pass "fm-sessionstart-cursor: sessionStart injects context"
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
test_sessionstart_silent_in_child_worktree() {
|
|
633
|
+
local base child out
|
|
634
|
+
base=$(make_primary_dir "$TMP_ROOT/session-base")
|
|
635
|
+
child="$TMP_ROOT/session-child"
|
|
636
|
+
fm_git_worktree "$base" "$child" fm/cursor-session-child
|
|
637
|
+
mkdir -p "$child/state"
|
|
638
|
+
: > "$child/AGENTS.md"
|
|
639
|
+
install_scripts "$child"
|
|
640
|
+
install_digest_fixture "$child"
|
|
641
|
+
out=$(printf '{"hook_event_name":"sessionStart","cursor_version":"x"}' \
|
|
642
|
+
| FM_HOME="$child" bash "$child/bin/fm-sessionstart-cursor.sh" --source startup 2>/dev/null)
|
|
643
|
+
[ -z "$out" ] || fail "a child worktree must never take the helm: $out"
|
|
644
|
+
pass "fm-sessionstart-cursor: silent inside a child crewmate worktree"
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
# --- registration ------------------------------------------------------------
|
|
648
|
+
|
|
649
|
+
test_tracked_registration_covers_the_primary_events() {
|
|
650
|
+
local reg
|
|
651
|
+
reg="$ROOT/.cursor/hooks.json"
|
|
652
|
+
[ -f "$reg" ] || fail "firstmate must ship a tracked project-scope .cursor/hooks.json"
|
|
653
|
+
jq -e '.hooks.stop and .hooks.sessionStart and .hooks.preToolUse' "$reg" >/dev/null 2>&1 \
|
|
654
|
+
|| fail "the registration must cover stop, sessionStart, and preToolUse"
|
|
655
|
+
jq -e '.hooks | has("preCompact") | not' "$reg" >/dev/null 2>&1 \
|
|
656
|
+
|| fail "preCompact staging is deliberately deferred to a follow-up and must stay unregistered"
|
|
657
|
+
jq -e '[.hooks.stop[] | select(.loop_limit != null and .loop_limit > 0)] | length == 1' "$reg" >/dev/null 2>&1 \
|
|
658
|
+
|| fail "the stop registration needs an explicit positive loop_limit: without it Cursor's default is unlimited"
|
|
659
|
+
jq -e '[.hooks.sessionStart[]] | all(.timeout > 120)' "$reg" >/dev/null 2>&1 \
|
|
660
|
+
|| fail "the session-open timeout must sit above bin/fm-session-start.sh's own 120s budget"
|
|
661
|
+
pass "cursor registration: covers every primary event with a bounded stop loop"
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
# The two bounds must nest, and the only honest way to prove it is to run the
|
|
665
|
+
# adapter at Cursor's own registered limit with its DEFAULT ceiling: firstmate's
|
|
666
|
+
# bound must already have stopped the loop by then, so Cursor's hard ceiling is
|
|
667
|
+
# never what silently ends supervision.
|
|
668
|
+
test_default_ceiling_bites_before_the_registered_loop_limit() {
|
|
669
|
+
local dir limit out
|
|
670
|
+
dir=$(make_primary_dir "$TMP_ROOT/park-nesting")
|
|
671
|
+
: > "$dir/state/task1.meta"
|
|
672
|
+
write_arm_fixture "$dir" actionable
|
|
673
|
+
limit=$(jq -r '.hooks.stop[0].loop_limit' "$ROOT/.cursor/hooks.json")
|
|
674
|
+
case "$limit" in ''|*[!0-9]*) fail "the stop registration needs a numeric loop_limit, got: $limit" ;; esac
|
|
675
|
+
out=$(run_park "$dir" "$((limit - 1))")
|
|
676
|
+
[ -z "$out" ] || fail "at Cursor's own limit the adapter must already be quiet from its own bound, got: $out"
|
|
677
|
+
[ ! -e "$dir/state/arm-ran" ] || fail "the adapter armed past its own default ceiling"
|
|
678
|
+
pass "cursor bounds nest: firstmate's default ceiling stops the loop before Cursor's loop_limit does"
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
test_turnend_guard_stands_down_on_cursor_payload
|
|
682
|
+
test_turnend_guard_still_blocks_for_claude_payload
|
|
683
|
+
test_autoarm_stands_down_on_cursor_payload
|
|
684
|
+
test_sessionstart_run_stands_down_on_cursor_payload
|
|
685
|
+
test_pretool_guards_deduplicate_and_render_cursor_deny
|
|
686
|
+
test_cd_guard_renders_cursor_deny
|
|
687
|
+
test_park_silent_when_nothing_in_flight
|
|
688
|
+
test_park_delivers_actionable_wake_as_followup
|
|
689
|
+
test_park_never_exits_two
|
|
690
|
+
test_park_repair_nag_is_bounded
|
|
691
|
+
test_park_repair_nag_requires_a_persisted_budget
|
|
692
|
+
test_park_nag_budget_resets_after_a_real_wake
|
|
693
|
+
test_park_loop_ceiling_warns_once_then_goes_quiet
|
|
694
|
+
test_park_stands_down_when_superseded
|
|
695
|
+
test_park_serializes_supersession_with_followup_commit
|
|
696
|
+
test_superseded_park_does_not_consume_nag_budget
|
|
697
|
+
test_park_inert_when_afk
|
|
698
|
+
test_park_inert_under_pi_coding_agent
|
|
699
|
+
test_park_still_parks_with_pi_leak_and_cursor_identity
|
|
700
|
+
test_park_stands_down_when_away_mode_activates_before_commit
|
|
701
|
+
test_park_inert_without_session_lock
|
|
702
|
+
test_park_stands_down_after_session_takeover
|
|
703
|
+
test_park_inert_in_child_worktree
|
|
704
|
+
test_park_ignores_malformed_payload
|
|
705
|
+
test_sessionstart_emits_additional_context
|
|
706
|
+
test_sessionstart_silent_in_child_worktree
|
|
707
|
+
test_tracked_registration_covers_the_primary_events
|
|
708
|
+
test_default_ceiling_bites_before_the_registered_loop_limit
|