@zerwiz/ymir 0.1.4 → 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 -59
- 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,961 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tests/fm-afk-launch.test.sh - the script-owned, backend-aware away-daemon
|
|
3
|
+
# launch (bin/fm-afk-launch.sh) and the away-mode stale-artifact lifecycle fixes
|
|
4
|
+
# (bin/fm-afk-start.sh). Two layers:
|
|
5
|
+
#
|
|
6
|
+
# UNIT (always run, no backend): the session-scoped stale-artifact clear on a
|
|
7
|
+
# fresh entry vs a refresh, and the correct-ordered stop (daemon SIGTERM'd
|
|
8
|
+
# while state/.afk is still present, .afk cleared last).
|
|
9
|
+
#
|
|
10
|
+
# E2E TOPOLOGY (per backend, skipped when its tool is absent): the anti-
|
|
11
|
+
# regression for the pane split/shrink - entering AND exiting away mode leaves
|
|
12
|
+
# the captain's active tab topology UNCHANGED, because the daemon lands in a
|
|
13
|
+
# NON-VISIBLE separate terminal (a herdr dedicated workspace, a detached tmux
|
|
14
|
+
# session), never a split of the captain's pane. The herdr path runs on a
|
|
15
|
+
# throwaway, NEVER-default HERDR_SESSION and asserts the default session is
|
|
16
|
+
# byte-identical via the fm-herdr-lab.sh fleet-state tripwire; the tmux path
|
|
17
|
+
# uses uniquely-named throwaway sessions killed by exact name. A harmless
|
|
18
|
+
# sleeper replaces the real daemon (FM_AFK_LAUNCH_ENTRY) so the test observes
|
|
19
|
+
# only the terminal lifecycle.
|
|
20
|
+
set -u
|
|
21
|
+
|
|
22
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
23
|
+
LAUNCH="$ROOT/bin/fm-afk-launch.sh"
|
|
24
|
+
START="$ROOT/bin/fm-afk-start.sh"
|
|
25
|
+
|
|
26
|
+
FAILED=0
|
|
27
|
+
fail() { printf 'not ok - %s\n' "$1" >&2; FAILED=1; }
|
|
28
|
+
pass() { printf 'ok - %s\n' "$1"; }
|
|
29
|
+
|
|
30
|
+
SLEEPER=$(mktemp "${TMPDIR:-/tmp}/fm-afk-sleeper.XXXXXX")
|
|
31
|
+
printf '#!/usr/bin/env bash\nexec sleep 600\n' > "$SLEEPER"
|
|
32
|
+
chmod +x "$SLEEPER"
|
|
33
|
+
TRACK_TMUX_SESSIONS=""
|
|
34
|
+
GLOBAL_CLEANUP() {
|
|
35
|
+
rm -f "$SLEEPER" 2>/dev/null || true
|
|
36
|
+
local s
|
|
37
|
+
for s in $TRACK_TMUX_SESSIONS; do
|
|
38
|
+
tmux kill-session -t "$s" 2>/dev/null || true
|
|
39
|
+
done
|
|
40
|
+
}
|
|
41
|
+
trap GLOBAL_CLEANUP EXIT
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# UNIT 1: fm_afk_clear_stale_artifacts removes exactly the three stale artifacts.
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
unit_clear_stale() {
|
|
47
|
+
local st
|
|
48
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-clear.XXXXXX")
|
|
49
|
+
mkdir -p "$st/state"
|
|
50
|
+
: > "$st/state/.subsuper-escalations"
|
|
51
|
+
: > "$st/state/.subsuper-escalations.since"
|
|
52
|
+
: > "$st/state/.subsuper-inject-wedged"
|
|
53
|
+
: > "$st/state/.wake-queue" # durable queue must be untouched
|
|
54
|
+
# Source fm-afk-start.sh inside a child bash (it sets `set -eu` and would
|
|
55
|
+
# otherwise leak that into this test shell) and call the clear helper.
|
|
56
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" \
|
|
57
|
+
bash -c '. "$1"; fm_afk_clear_stale_artifacts "$2"' _ "$START" "$st/state"
|
|
58
|
+
if [ ! -e "$st/state/.subsuper-escalations" ] \
|
|
59
|
+
&& [ ! -e "$st/state/.subsuper-escalations.since" ] \
|
|
60
|
+
&& [ ! -e "$st/state/.subsuper-inject-wedged" ]; then
|
|
61
|
+
pass "clear-stale: removes escalations buffer, sidecar, and wedge marker"
|
|
62
|
+
else
|
|
63
|
+
fail "clear-stale: stale artifacts survived"
|
|
64
|
+
fi
|
|
65
|
+
if [ -e "$st/state/.wake-queue" ]; then
|
|
66
|
+
pass "clear-stale: leaves the durable wake-queue intact (no pending work dropped)"
|
|
67
|
+
else
|
|
68
|
+
fail "clear-stale: removed the durable wake-queue"
|
|
69
|
+
fi
|
|
70
|
+
rm -rf "$st"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
unit_relative_paths_are_absolute_before_daemon_launch() {
|
|
74
|
+
local root home state out status linked_home
|
|
75
|
+
root=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-relative-home.XXXXXX")
|
|
76
|
+
mkdir -p "$root/home/state" "$root/cdpath/home/state"
|
|
77
|
+
home=$(cd "$root/home" && pwd -P)
|
|
78
|
+
state="$home/state"
|
|
79
|
+
out=$(
|
|
80
|
+
cd "$root" || exit 1
|
|
81
|
+
CDPATH="$root/cdpath" FM_HOME=home FM_STATE_OVERRIDE=home/state \
|
|
82
|
+
bash -c '. "$1"; printf "%s\n%s\n" "$FM_HOME" "$FM_AFK_LAUNCH_STATE"' _ "$LAUNCH"
|
|
83
|
+
)
|
|
84
|
+
if [ "$out" = "$home"$'\n'"$state" ]; then
|
|
85
|
+
pass "launcher paths: relative home and state ignore CDPATH before daemon command construction"
|
|
86
|
+
else
|
|
87
|
+
fail "launcher paths: relative home or state remained cwd-dependent ($out)"
|
|
88
|
+
fi
|
|
89
|
+
linked_home="$root/home-link"
|
|
90
|
+
ln -s "$root/home" "$linked_home"
|
|
91
|
+
out=$(FM_HOME="$linked_home" FM_STATE_OVERRIDE="$linked_home/state" \
|
|
92
|
+
bash -c '. "$1"; printf "%s\n%s\n" "$FM_HOME" "$FM_AFK_LAUNCH_STATE"' _ "$LAUNCH")
|
|
93
|
+
if [ "$out" = "$linked_home"$'\n'"$linked_home/state" ]; then
|
|
94
|
+
pass "launcher paths: absolute symlink spellings are preserved"
|
|
95
|
+
else
|
|
96
|
+
fail "launcher paths: absolute symlink spelling changed ($out)"
|
|
97
|
+
fi
|
|
98
|
+
out=$(
|
|
99
|
+
cd "$root" || exit 1
|
|
100
|
+
FM_HOME=missing-home "$LAUNCH" help 2>&1
|
|
101
|
+
)
|
|
102
|
+
status=$?
|
|
103
|
+
if [ "$status" -ne 0 ] && printf '%s\n' "$out" | grep -F "FM_HOME directory cannot be resolved: missing-home" >/dev/null; then
|
|
104
|
+
pass "launcher paths: unresolved relative FM_HOME fails loudly"
|
|
105
|
+
else
|
|
106
|
+
fail "launcher paths: unresolved relative FM_HOME did not name the bad input ($out)"
|
|
107
|
+
fi
|
|
108
|
+
out=$(
|
|
109
|
+
cd "$root" || exit 1
|
|
110
|
+
FM_HOME=home FM_STATE_OVERRIDE=missing-state "$LAUNCH" help 2>&1
|
|
111
|
+
)
|
|
112
|
+
status=$?
|
|
113
|
+
if [ "$status" -ne 0 ] && printf '%s\n' "$out" | grep -F "FM_STATE_OVERRIDE directory cannot be resolved: missing-state" >/dev/null; then
|
|
114
|
+
pass "launcher paths: unresolved relative FM_STATE_OVERRIDE fails loudly"
|
|
115
|
+
else
|
|
116
|
+
fail "launcher paths: unresolved relative FM_STATE_OVERRIDE did not name the bad input ($out)"
|
|
117
|
+
fi
|
|
118
|
+
rm -rf "$root"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
# UNIT 2: a FRESH entry clears; a REFRESH (daemon already alive) preserves the
|
|
123
|
+
# current session's buffered escalations.
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
unit_fresh_vs_refresh() {
|
|
126
|
+
local st sleep_pid lock
|
|
127
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-refresh.XXXXXX")
|
|
128
|
+
mkdir -p "$st/state"
|
|
129
|
+
: > "$st/state/.subsuper-escalations"
|
|
130
|
+
: > "$st/state/.subsuper-inject-wedged"
|
|
131
|
+
# A live "daemon": a real process whose identity the lock records, so
|
|
132
|
+
# daemon_lock_held_by_live_daemon returns true (a refresh).
|
|
133
|
+
sleep 600 &
|
|
134
|
+
sleep_pid=$!
|
|
135
|
+
lock="$st/state/.supervise-daemon.lock"
|
|
136
|
+
mkdir -p "$lock"
|
|
137
|
+
printf '%s' "$sleep_pid" > "$lock/pid"
|
|
138
|
+
( . "$ROOT/bin/fm-wake-lib.sh"; fm_pid_identity "$sleep_pid" > "$lock/pid-identity" 2>/dev/null ) || true
|
|
139
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" "$START" >/dev/null 2>&1
|
|
140
|
+
if [ -e "$st/state/.subsuper-escalations" ] && [ -e "$st/state/.subsuper-inject-wedged" ]; then
|
|
141
|
+
pass "refresh: daemon already alive - stale artifacts preserved (current session's buffer kept)"
|
|
142
|
+
else
|
|
143
|
+
fail "refresh: incorrectly cleared the current session's buffered escalations"
|
|
144
|
+
fi
|
|
145
|
+
kill "$sleep_pid" 2>/dev/null || true
|
|
146
|
+
wait "$sleep_pid" 2>/dev/null || true
|
|
147
|
+
rm -rf "$st"
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# ---------------------------------------------------------------------------
|
|
151
|
+
# UNIT 3: exit ordering - fm_afk_launch_stop SIGTERMs the daemon WHILE .afk is
|
|
152
|
+
# still present (so its flush is not a no-op), and clears .afk last.
|
|
153
|
+
# ---------------------------------------------------------------------------
|
|
154
|
+
unit_stop_ordering() {
|
|
155
|
+
local st lock marker daemon_pid
|
|
156
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-stop.XXXXXX")
|
|
157
|
+
mkdir -p "$st/state"
|
|
158
|
+
date '+%s' > "$st/state/.afk"
|
|
159
|
+
marker="$st/afk-at-term"
|
|
160
|
+
# A fake daemon: on SIGTERM, record whether .afk was still present, then exit.
|
|
161
|
+
bash -c '
|
|
162
|
+
trap "if [ -f \"$1/state/.afk\" ]; then echo present > \"$2\"; else echo absent > \"$2\"; fi; exit 0" TERM
|
|
163
|
+
while :; do sleep 0.2; done
|
|
164
|
+
' _ "$st" "$marker" &
|
|
165
|
+
daemon_pid=$!
|
|
166
|
+
lock="$st/state/.supervise-daemon.lock"
|
|
167
|
+
mkdir -p "$lock"
|
|
168
|
+
printf '%s' "$daemon_pid" > "$lock/pid"
|
|
169
|
+
( . "$ROOT/bin/fm-wake-lib.sh"; fm_pid_identity "$daemon_pid" > "$lock/pid-identity" 2>/dev/null ) || true
|
|
170
|
+
printf 'none\t-\tnative\n' > "$st/state/.afk-daemon-terminal"
|
|
171
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" "$LAUNCH" stop >/dev/null 2>&1
|
|
172
|
+
# shellcheck disable=SC2031 # The background daemon writes this shared file; no shell variable is reassigned.
|
|
173
|
+
if [ "$(cat "$marker" 2>/dev/null || echo missing)" = present ]; then
|
|
174
|
+
pass "stop-ordering: daemon SIGTERM'd while .afk still present (flush is not a no-op)"
|
|
175
|
+
else
|
|
176
|
+
fail "stop-ordering: .afk was already cleared when the daemon got SIGTERM"
|
|
177
|
+
fi
|
|
178
|
+
if [ ! -e "$st/state/.afk" ]; then
|
|
179
|
+
pass "stop-ordering: .afk cleared last"
|
|
180
|
+
else
|
|
181
|
+
fail "stop-ordering: .afk not cleared"
|
|
182
|
+
fi
|
|
183
|
+
if [ ! -e "$st/state/.afk-daemon-terminal" ]; then
|
|
184
|
+
pass "stop-ordering: daemon-terminal record removed"
|
|
185
|
+
else
|
|
186
|
+
fail "stop-ordering: record not removed"
|
|
187
|
+
fi
|
|
188
|
+
kill "$daemon_pid" 2>/dev/null || true
|
|
189
|
+
wait "$daemon_pid" 2>/dev/null || true
|
|
190
|
+
rm -rf "$st"
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
unit_stop_rejects_reused_pid() {
|
|
194
|
+
local st lock sleeper_pid
|
|
195
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-pid-reuse.XXXXXX")
|
|
196
|
+
mkdir -p "$st/state"
|
|
197
|
+
date '+%s' > "$st/state/.afk"
|
|
198
|
+
sleep 600 &
|
|
199
|
+
sleeper_pid=$!
|
|
200
|
+
lock="$st/state/.supervise-daemon.lock"
|
|
201
|
+
mkdir -p "$lock"
|
|
202
|
+
printf '%s' "$sleeper_pid" > "$lock/pid"
|
|
203
|
+
printf 'different-process-identity' > "$lock/pid-identity"
|
|
204
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" "$LAUNCH" stop >/dev/null 2>&1
|
|
205
|
+
if kill -0 "$sleeper_pid" 2>/dev/null; then
|
|
206
|
+
pass "stop identity: stale lock cannot signal an unrelated live process"
|
|
207
|
+
else
|
|
208
|
+
fail "stop identity: stale lock signaled an unrelated live process"
|
|
209
|
+
fi
|
|
210
|
+
kill "$sleeper_pid" 2>/dev/null || true
|
|
211
|
+
wait "$sleeper_pid" 2>/dev/null || true
|
|
212
|
+
rm -rf "$st"
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
unit_failed_start_rolls_back_state() {
|
|
216
|
+
local st
|
|
217
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-failed-start.XXXXXX")
|
|
218
|
+
mkdir -p "$st/state"
|
|
219
|
+
printf 'pending\n' > "$st/state/.subsuper-escalations"
|
|
220
|
+
printf 'wedged\n' > "$st/state/.subsuper-inject-wedged"
|
|
221
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" FM_SUPERVISOR_TARGET=unused \
|
|
222
|
+
FM_SUPERVISOR_BACKEND=unsupported "$LAUNCH" start >/dev/null 2>&1; then
|
|
223
|
+
fail "failed start: unsupported backend unexpectedly succeeded"
|
|
224
|
+
elif [ ! -e "$st/state/.afk" ] \
|
|
225
|
+
&& [ "$(cat "$st/state/.subsuper-escalations")" = pending ] \
|
|
226
|
+
&& [ "$(cat "$st/state/.subsuper-inject-wedged")" = wedged ]; then
|
|
227
|
+
pass "failed start: away flag and delivery artifacts roll back"
|
|
228
|
+
else
|
|
229
|
+
fail "failed start: left false away state or discarded delivery artifacts"
|
|
230
|
+
fi
|
|
231
|
+
rm -rf "$st"
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
unit_concurrent_start_serialized() {
|
|
235
|
+
command -v tmux >/dev/null 2>&1 || { echo "skip: tmux not found (concurrent start)"; return 0; }
|
|
236
|
+
local st cap_session cap_pane first second rec count
|
|
237
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-concurrent.XXXXXX")
|
|
238
|
+
cap_session="fm-afk-concurrent-cap-$$"
|
|
239
|
+
tmux new-session -d -s "$cap_session" 2>/dev/null || { fail "concurrent start: captain session creation failed"; rm -rf "$st"; return 0; }
|
|
240
|
+
TRACK_TMUX_SESSIONS="$TRACK_TMUX_SESSIONS $cap_session"
|
|
241
|
+
cap_pane=$(tmux display-message -p -t "$cap_session" '#{pane_id}')
|
|
242
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" FM_SUPERVISOR_TARGET="$cap_pane" \
|
|
243
|
+
FM_SUPERVISOR_BACKEND=tmux FM_AFK_LAUNCH_ENTRY="$SLEEPER" "$LAUNCH" start >/dev/null 2>&1 & first=$!
|
|
244
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" FM_SUPERVISOR_TARGET="$cap_pane" \
|
|
245
|
+
FM_SUPERVISOR_BACKEND=tmux FM_AFK_LAUNCH_ENTRY="$SLEEPER" "$LAUNCH" start >/dev/null 2>&1 & second=$!
|
|
246
|
+
wait "$first"; wait "$second"
|
|
247
|
+
rec=$(cut -f2 "$st/state/.afk-daemon-terminal" 2>/dev/null || true)
|
|
248
|
+
count=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | awk -v expected="$rec" '$0 == expected {n++} END{print n+0}')
|
|
249
|
+
TRACK_TMUX_SESSIONS="$TRACK_TMUX_SESSIONS $rec"
|
|
250
|
+
if [ -n "$rec" ] && tmux has-session -t "$rec" 2>/dev/null && [ "$count" -eq 1 ]; then
|
|
251
|
+
pass "concurrent start: one serialized daemon terminal remains tracked"
|
|
252
|
+
else
|
|
253
|
+
fail "concurrent start: leaked or lost daemon terminal (count $count, record $rec)"
|
|
254
|
+
fi
|
|
255
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" "$LAUNCH" stop >/dev/null 2>&1
|
|
256
|
+
tmux kill-session -t "$cap_session" 2>/dev/null || true
|
|
257
|
+
rm -rf "$st"
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
unit_lock_initialization_grace() {
|
|
261
|
+
local st marker initializer
|
|
262
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-lock-init.XXXXXX")
|
|
263
|
+
marker="$st/initialized"
|
|
264
|
+
mkdir -p "$st/state/.afk-launch.lock"
|
|
265
|
+
(
|
|
266
|
+
sleep 0.15
|
|
267
|
+
if [ -d "$st/state/.afk-launch.lock" ]; then
|
|
268
|
+
printf '%s' "$$" > "$st/state/.afk-launch.lock/pid"
|
|
269
|
+
( . "$ROOT/bin/fm-wake-lib.sh"; fm_pid_identity "$$" > "$st/state/.afk-launch.lock/pid-identity" 2>/dev/null ) || true
|
|
270
|
+
# shellcheck disable=SC2031 # The subshell writes the path value; it does not reassign the variable.
|
|
271
|
+
: > "$marker"
|
|
272
|
+
sleep 0.15
|
|
273
|
+
rm -rf "$st/state/.afk-launch.lock"
|
|
274
|
+
fi
|
|
275
|
+
) &
|
|
276
|
+
initializer=$!
|
|
277
|
+
# shellcheck disable=SC2031 # The initializer communicates through this shared file path.
|
|
278
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
279
|
+
. "$1"
|
|
280
|
+
fm_afk_launch_lock_acquire
|
|
281
|
+
fm_afk_launch_lock_release
|
|
282
|
+
' _ "$LAUNCH" && [ -e "$marker" ]; then
|
|
283
|
+
pass "launcher lock: incomplete publication receives initialization grace"
|
|
284
|
+
else
|
|
285
|
+
fail "launcher lock: contender removed a lock during initialization"
|
|
286
|
+
fi
|
|
287
|
+
wait "$initializer" 2>/dev/null || true
|
|
288
|
+
rm -rf "$st"
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
unit_signal_exits_with_lock_cleanup() {
|
|
292
|
+
local st marker child
|
|
293
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-signal.XXXXXX")
|
|
294
|
+
marker="$st/resumed"
|
|
295
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
296
|
+
. "$1"
|
|
297
|
+
fm_afk_launch_start() { sleep 30; }
|
|
298
|
+
fm_afk_launch_main start
|
|
299
|
+
: > "$2"
|
|
300
|
+
' _ "$LAUNCH" "$marker" &
|
|
301
|
+
child=$!
|
|
302
|
+
# Signal only once the lifecycle actually holds its lock. Killing before the
|
|
303
|
+
# lock exists tests nothing, and on a loaded machine it used to race: the
|
|
304
|
+
# lock could be created just after the kill and outlive the process.
|
|
305
|
+
local locked=0 _
|
|
306
|
+
for _ in $(seq 1 100); do
|
|
307
|
+
if [ -d "$st/state/.afk-launch.lock" ]; then locked=1; break; fi
|
|
308
|
+
sleep 0.05
|
|
309
|
+
done
|
|
310
|
+
[ "$locked" = 1 ] || fail "launcher signal: lifecycle never acquired its lock to interrupt"
|
|
311
|
+
kill -TERM "$child" 2>/dev/null || true
|
|
312
|
+
wait "$child" 2>/dev/null || true
|
|
313
|
+
# The signal handler releases the lock as it exits; give that removal a
|
|
314
|
+
# bounded settle rather than sampling the instant `wait` returns.
|
|
315
|
+
for _ in $(seq 1 100); do
|
|
316
|
+
[ -e "$st/state/.afk-launch.lock" ] || break
|
|
317
|
+
sleep 0.05
|
|
318
|
+
done
|
|
319
|
+
if [ ! -e "$marker" ] && [ ! -e "$st/state/.afk-launch.lock" ]; then
|
|
320
|
+
pass "launcher signal: TERM exits and releases the lifecycle lock"
|
|
321
|
+
else
|
|
322
|
+
fail "launcher signal: interrupted lifecycle resumed or retained its lock"
|
|
323
|
+
fi
|
|
324
|
+
rm -rf "$st"
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
unit_herdr_partial_create_recovery() {
|
|
328
|
+
local st recorded
|
|
329
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-herdr-partial.XXXXXX")
|
|
330
|
+
recorded="$st/recorded"
|
|
331
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" FM_AFK_LAUNCH_ENTRY=/bin/true \
|
|
332
|
+
FM_AFK_LAUNCH_LABEL=afk-exact-label RECORDED="$recorded" bash -c '
|
|
333
|
+
. "$1"
|
|
334
|
+
fm_backend_source() { return 0; }
|
|
335
|
+
fm_backend_herdr_server_ensure() { return 0; }
|
|
336
|
+
fm_backend_herdr_cli() {
|
|
337
|
+
if [ "$2 $3" = "workspace create" ]; then
|
|
338
|
+
printf %s '\''truncated'\''
|
|
339
|
+
return 1
|
|
340
|
+
elif [ "$2 $3" = "workspace list" ]; then
|
|
341
|
+
printf %s '\''{"result":{"workspaces":[{"workspace_id":"ws-partial","label":"afk-exact-label"}]}}'\''
|
|
342
|
+
else
|
|
343
|
+
printf %s '\''{"result":{"panes":[{"pane_id":"pane-exact"}]}}'\''
|
|
344
|
+
fi
|
|
345
|
+
}
|
|
346
|
+
fm_afk_launch_record_write() { printf "%s:%s:%s" "$1" "$2" "$3" > "$RECORDED"; }
|
|
347
|
+
fm_afk_launch_create_herdr lab:captain herdr
|
|
348
|
+
' _ "$LAUNCH"
|
|
349
|
+
if [ "$(cat "$recorded" 2>/dev/null || true)" = "herdr:lab:pane-exact:ws-partial" ]; then
|
|
350
|
+
pass "herdr create: malformed response recovers durable exact ownership"
|
|
351
|
+
else
|
|
352
|
+
fail "herdr create: malformed response left terminal ownership unknown"
|
|
353
|
+
fi
|
|
354
|
+
rm -rf "$st"
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
unit_herdr_error_with_exact_ids_closes_exact() {
|
|
358
|
+
local st
|
|
359
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-herdr-error-exact.XXXXXX")
|
|
360
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
361
|
+
. "$1"
|
|
362
|
+
fm_backend_source() { return 0; }
|
|
363
|
+
fm_backend_herdr_server_ensure() { return 0; }
|
|
364
|
+
fm_backend_herdr_cli() {
|
|
365
|
+
if [ "$2 $3" = "workspace create" ]; then
|
|
366
|
+
printf %s '\''{"result":{"workspace":{"workspace_id":"ws-exact"},"root_pane":{"pane_id":"pane-exact"}}}'\''
|
|
367
|
+
return 1
|
|
368
|
+
elif [ "$2 $3" = "pane get" ]; then
|
|
369
|
+
printf %s '\''{"error":{"code":"transport_error"}}'\''
|
|
370
|
+
return 2
|
|
371
|
+
fi
|
|
372
|
+
return 2
|
|
373
|
+
}
|
|
374
|
+
! fm_afk_launch_create_herdr lab:captain herdr
|
|
375
|
+
' _ "$LAUNCH"
|
|
376
|
+
if [ "$(cut -f2 "$st/state/.afk-daemon-terminal" 2>/dev/null || true)" = "lab:pane-exact" ]; then
|
|
377
|
+
pass "herdr create error: unconfirmed exact id is persisted for reconciliation"
|
|
378
|
+
else
|
|
379
|
+
fail "herdr create error: unconfirmed exact cleanup id was discarded"
|
|
380
|
+
fi
|
|
381
|
+
rm -rf "$st"
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
unit_herdr_run_failure_preserves_unconfirmed_record() {
|
|
385
|
+
local st
|
|
386
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-herdr-run-fail.XXXXXX")
|
|
387
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
388
|
+
. "$1"
|
|
389
|
+
fm_backend_source() { return 0; }
|
|
390
|
+
fm_backend_herdr_server_ensure() { return 0; }
|
|
391
|
+
fm_backend_herdr_cli() {
|
|
392
|
+
if [ "$2 $3" = "workspace create" ]; then
|
|
393
|
+
printf %s '\''{"result":{"workspace":{"workspace_id":"ws-exact"},"root_pane":{"pane_id":"pane-exact"}}}'\''
|
|
394
|
+
return 0
|
|
395
|
+
elif [ "$2 $3" = "pane run" ]; then
|
|
396
|
+
return 1
|
|
397
|
+
elif [ "$2 $3" = "pane get" ]; then
|
|
398
|
+
printf %s '\''{"error":{"code":"transport_error"}}'\''
|
|
399
|
+
return 2
|
|
400
|
+
fi
|
|
401
|
+
return 2
|
|
402
|
+
}
|
|
403
|
+
! fm_afk_launch_create_herdr lab:captain herdr
|
|
404
|
+
' _ "$LAUNCH"
|
|
405
|
+
if [ "$(cut -f2 "$st/state/.afk-daemon-terminal" 2>/dev/null || true)" = "lab:pane-exact" ]; then
|
|
406
|
+
pass "herdr run failure: unconfirmed exact id remains reconcilable"
|
|
407
|
+
else
|
|
408
|
+
fail "herdr run failure: unconfirmed exact id was discarded"
|
|
409
|
+
fi
|
|
410
|
+
rm -rf "$st"
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
unit_record_failure_closes_terminal() {
|
|
414
|
+
local st closed
|
|
415
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-record-fail.XXXXXX")
|
|
416
|
+
closed="$st/closed"
|
|
417
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" CLOSED="$closed" bash -c '
|
|
418
|
+
. "$1"
|
|
419
|
+
fm_afk_launch_record_write() { return 1; }
|
|
420
|
+
fm_afk_launch_close_terminal() { printf "%s:%s" "$1" "$2" > "$CLOSED"; }
|
|
421
|
+
! fm_afk_launch_commit_terminal tmux exact-session ""
|
|
422
|
+
' _ "$LAUNCH"
|
|
423
|
+
if [ "$(cat "$closed" 2>/dev/null || true)" = "tmux:exact-session" ]; then
|
|
424
|
+
pass "record failure: newly created terminal is closed by exact id"
|
|
425
|
+
else
|
|
426
|
+
fail "record failure: newly created terminal leaked"
|
|
427
|
+
fi
|
|
428
|
+
rm -rf "$st"
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
unit_readiness_failure_rolls_back_terminal() {
|
|
432
|
+
local st closed
|
|
433
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-not-ready.XXXXXX")
|
|
434
|
+
closed="$st/closed"
|
|
435
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" CLOSED="$closed" bash -c '
|
|
436
|
+
. "$1"
|
|
437
|
+
fm_afk_launch_wait_ready() { return 1; }
|
|
438
|
+
fm_afk_launch_close_terminal() { printf "%s:%s" "$1" "$2" > "$CLOSED"; }
|
|
439
|
+
fm_afk_launch_terminal_absent() { [ -e "$CLOSED" ]; }
|
|
440
|
+
! fm_afk_launch_commit_terminal tmux exact-session ""
|
|
441
|
+
' _ "$LAUNCH"
|
|
442
|
+
if [ "$(cat "$closed" 2>/dev/null || true)" = "tmux:exact-session" ] \
|
|
443
|
+
&& [ ! -e "$st/state/.afk-daemon-terminal" ]; then
|
|
444
|
+
pass "readiness failure: exact terminal and durable record roll back"
|
|
445
|
+
else
|
|
446
|
+
fail "readiness failure: terminal or record survived"
|
|
447
|
+
fi
|
|
448
|
+
rm -rf "$st"
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
unit_readiness_failure_preserves_unconfirmed_record() {
|
|
452
|
+
local st
|
|
453
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-not-ready-unconfirmed.XXXXXX")
|
|
454
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
455
|
+
. "$1"
|
|
456
|
+
fm_afk_launch_wait_ready() { return 1; }
|
|
457
|
+
fm_afk_launch_close_terminal() { return 1; }
|
|
458
|
+
fm_afk_launch_terminal_absent() { return 1; }
|
|
459
|
+
! fm_afk_launch_commit_terminal tmux exact-session ""
|
|
460
|
+
' _ "$LAUNCH"
|
|
461
|
+
if [ "$(cut -f2 "$st/state/.afk-daemon-terminal" 2>/dev/null || true)" = exact-session ]; then
|
|
462
|
+
pass "readiness failure: unconfirmed terminal retains its reconciliation id"
|
|
463
|
+
else
|
|
464
|
+
fail "readiness failure: unconfirmed terminal lost its reconciliation id"
|
|
465
|
+
fi
|
|
466
|
+
rm -rf "$st"
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
unit_tmux_absence_distinguishes_probe_failure() {
|
|
470
|
+
local st
|
|
471
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-tmux-probe.XXXXXX")
|
|
472
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
473
|
+
. "$1"
|
|
474
|
+
tmux() { printf "%s" "can'\''t find session: exact-session" >&2; return 1; }
|
|
475
|
+
fm_afk_launch_terminal_absent tmux exact-session
|
|
476
|
+
tmux() { printf "%s" "error connecting to /tmp/tmux.sock" >&2; return 1; }
|
|
477
|
+
! fm_afk_launch_terminal_absent tmux exact-session
|
|
478
|
+
' _ "$LAUNCH"; then
|
|
479
|
+
pass "tmux absence: clean missing differs from transport probe failure"
|
|
480
|
+
else
|
|
481
|
+
fail "tmux absence: probe failure was treated as confirmed absence"
|
|
482
|
+
fi
|
|
483
|
+
rm -rf "$st"
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
unit_native_lifecycle() {
|
|
487
|
+
local st
|
|
488
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-native.XXXXXX")
|
|
489
|
+
mkdir -p "$st/state"
|
|
490
|
+
: > "$st/state/.subsuper-escalations"
|
|
491
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" "$LAUNCH" start-native >/dev/null 2>&1 \
|
|
492
|
+
&& [ "$(cut -f1 "$st/state/.afk-daemon-terminal")" = none ] \
|
|
493
|
+
&& [ -e "$st/state/.afk" ] \
|
|
494
|
+
&& [ ! -e "$st/state/.subsuper-escalations" ]; then
|
|
495
|
+
pass "native lifecycle: launcher owns state with no terminal"
|
|
496
|
+
else
|
|
497
|
+
fail "native lifecycle: state preparation or no-terminal record failed"
|
|
498
|
+
fi
|
|
499
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" "$LAUNCH" stop >/dev/null 2>&1
|
|
500
|
+
if [ ! -e "$st/state/.afk" ] && [ ! -e "$st/state/.afk-daemon-terminal" ]; then
|
|
501
|
+
pass "native lifecycle: uniform stop clears state without closing a terminal"
|
|
502
|
+
else
|
|
503
|
+
fail "native lifecycle: uniform stop retained state"
|
|
504
|
+
fi
|
|
505
|
+
rm -rf "$st"
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
unit_native_entry_preserves_prepared_state() {
|
|
509
|
+
local st
|
|
510
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-native-entry.XXXXXX")
|
|
511
|
+
mkdir -p "$st/state"
|
|
512
|
+
: > "$st/state/.afk"
|
|
513
|
+
: > "$st/state/.subsuper-escalations"
|
|
514
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" FM_AFK_STATE_PREPARED=1 bash -c '
|
|
515
|
+
. "$1"
|
|
516
|
+
FM_AFK_DAEMON=/bin/true
|
|
517
|
+
fm_afk_start_main
|
|
518
|
+
' _ "$START" >/dev/null 2>&1
|
|
519
|
+
if [ -e "$st/state/.afk" ] && [ -e "$st/state/.subsuper-escalations" ]; then
|
|
520
|
+
pass "native entry: launcher-prepared lifecycle state is not rewritten"
|
|
521
|
+
else
|
|
522
|
+
fail "native entry: launcher-prepared lifecycle state was mutated"
|
|
523
|
+
fi
|
|
524
|
+
rm -rf "$st"
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
unit_close_failure_preserves_record() {
|
|
528
|
+
local st
|
|
529
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-close-fail.XXXXXX")
|
|
530
|
+
mkdir -p "$st/state"
|
|
531
|
+
printf 'tmux\texact-session\towned\n' > "$st/state/.afk-daemon-terminal"
|
|
532
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
533
|
+
. "$1"
|
|
534
|
+
fm_afk_launch_close_terminal() { return 1; }
|
|
535
|
+
fm_afk_launch_terminal_absent() { return 1; }
|
|
536
|
+
! fm_afk_launch_reconcile
|
|
537
|
+
' _ "$LAUNCH"
|
|
538
|
+
if [ -e "$st/state/.afk-daemon-terminal" ]; then
|
|
539
|
+
pass "teardown failure: exact terminal record is preserved"
|
|
540
|
+
else
|
|
541
|
+
fail "teardown failure: exact terminal record was discarded"
|
|
542
|
+
fi
|
|
543
|
+
rm -rf "$st"
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
unit_record_publication_atomic() {
|
|
547
|
+
local st
|
|
548
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-record-atomic.XXXXXX")
|
|
549
|
+
mkdir -p "$st/state"
|
|
550
|
+
printf 'tmux\told-session\towned\n' > "$st/state/.afk-daemon-terminal"
|
|
551
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
552
|
+
. "$1"
|
|
553
|
+
mv() { return 1; }
|
|
554
|
+
! fm_afk_launch_record_write tmux new-session owned
|
|
555
|
+
' _ "$LAUNCH" \
|
|
556
|
+
&& [ "$(cat "$st/state/.afk-daemon-terminal")" = $'tmux\told-session\towned' ] \
|
|
557
|
+
&& ! find "$st/state" -name '.afk-daemon-terminal.pending.*' -print -quit | grep -q .; then
|
|
558
|
+
pass "record publication: failed atomic rename preserves the complete prior record"
|
|
559
|
+
else
|
|
560
|
+
fail "record publication: failed write truncated or replaced the prior record"
|
|
561
|
+
fi
|
|
562
|
+
rm -rf "$st"
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
unit_malformed_record_fails_closed() {
|
|
566
|
+
local st acted
|
|
567
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-record-malformed.XXXXXX")
|
|
568
|
+
mkdir -p "$st/state"
|
|
569
|
+
printf 'tmux\tonly-two-fields\n' > "$st/state/.afk-daemon-terminal"
|
|
570
|
+
acted="$st/acted"
|
|
571
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" ACTED="$acted" bash -c '
|
|
572
|
+
. "$1"
|
|
573
|
+
fm_afk_launch_close_terminal() { : > "$ACTED"; }
|
|
574
|
+
! fm_afk_launch_reconcile
|
|
575
|
+
' _ "$LAUNCH" \
|
|
576
|
+
&& [ ! -e "$acted" ] && [ -e "$st/state/.afk-daemon-terminal" ]; then
|
|
577
|
+
pass "record read: malformed record fails closed without acting on a partial id"
|
|
578
|
+
else
|
|
579
|
+
fail "record read: malformed record was acted on or discarded"
|
|
580
|
+
fi
|
|
581
|
+
rm -rf "$st"
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
unit_stop_malformed_record_fails_closed() {
|
|
585
|
+
local st
|
|
586
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-stop-malformed.XXXXXX")
|
|
587
|
+
mkdir -p "$st/state"
|
|
588
|
+
: > "$st/state/.afk"
|
|
589
|
+
printf 'tmux\tonly-two-fields\n' > "$st/state/.afk-daemon-terminal"
|
|
590
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
591
|
+
. "$1"
|
|
592
|
+
! fm_afk_launch_stop
|
|
593
|
+
' _ "$LAUNCH" && [ -e "$st/state/.afk" ] && [ -e "$st/state/.afk-daemon-terminal" ]; then
|
|
594
|
+
pass "stop: malformed terminal record preserves away state and fails closed"
|
|
595
|
+
else
|
|
596
|
+
fail "stop: malformed terminal record cleared protected lifecycle state"
|
|
597
|
+
fi
|
|
598
|
+
rm -rf "$st"
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
unit_tmux_planned_record_and_collision() {
|
|
602
|
+
local st first second
|
|
603
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-tmux-plan.XXXXXX")
|
|
604
|
+
mkdir -p "$st/state"
|
|
605
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
606
|
+
. "$1"
|
|
607
|
+
tmux() {
|
|
608
|
+
if [ "$1" = new-session ]; then
|
|
609
|
+
[ -s "$FM_AFK_LAUNCH_RECORD" ] || return 9
|
|
610
|
+
printf "%s" "$4" > "$FM_HOME/created-name"
|
|
611
|
+
return 1
|
|
612
|
+
fi
|
|
613
|
+
[ "$1" != kill-session ] || : > "$FM_HOME/killed"
|
|
614
|
+
return 1
|
|
615
|
+
}
|
|
616
|
+
! fm_afk_launch_create_tmux captain:0 tmux
|
|
617
|
+
' _ "$LAUNCH" && [ ! -e "$st/state/.afk-daemon-terminal" ] && [ ! -e "$st/killed" ]; then
|
|
618
|
+
pass "tmux launch: planned exact target is recorded before creation and removed on failure"
|
|
619
|
+
else
|
|
620
|
+
fail "tmux launch: creation began before exact target publication"
|
|
621
|
+
fi
|
|
622
|
+
first=$(cat "$st/created-name")
|
|
623
|
+
rm -rf "$st"
|
|
624
|
+
|
|
625
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-tmux-unique.XXXXXX")
|
|
626
|
+
mkdir -p "$st/state"
|
|
627
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
628
|
+
. "$1"
|
|
629
|
+
tmux() {
|
|
630
|
+
[ "$1" != new-session ] || { printf "%s" "$4" > "$FM_HOME/created-name"; return 1; }
|
|
631
|
+
[ "$1" != kill-session ] || : > "$FM_HOME/killed"
|
|
632
|
+
return 1
|
|
633
|
+
}
|
|
634
|
+
! fm_afk_launch_create_tmux captain:0 tmux
|
|
635
|
+
' _ "$LAUNCH" && [ ! -e "$st/killed" ]; then
|
|
636
|
+
second=$(cat "$st/created-name")
|
|
637
|
+
if [ "$first" != "$second" ]; then
|
|
638
|
+
pass "tmux launch: unique names eliminate collision teardown"
|
|
639
|
+
else
|
|
640
|
+
fail "tmux launch: consecutive launches reused a session name"
|
|
641
|
+
fi
|
|
642
|
+
else
|
|
643
|
+
fail "tmux launch: creation failure attempted session teardown"
|
|
644
|
+
fi
|
|
645
|
+
rm -rf "$st"
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
unit_stop_validates_before_signal() {
|
|
649
|
+
local st sleeper_pid
|
|
650
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-stop-validate.XXXXXX")
|
|
651
|
+
mkdir -p "$st/state"
|
|
652
|
+
: > "$st/state/.afk"
|
|
653
|
+
printf 'tmux\tonly-two-fields\n' > "$st/state/.afk-daemon-terminal"
|
|
654
|
+
sleep 30 & sleeper_pid=$!
|
|
655
|
+
mkdir -p "$st/state/.supervise-daemon.lock"
|
|
656
|
+
printf '%s' "$sleeper_pid" > "$st/state/.supervise-daemon.lock/pid"
|
|
657
|
+
( . "$ROOT/bin/fm-wake-lib.sh"; fm_pid_identity "$sleeper_pid" > "$st/state/.supervise-daemon.lock/pid-identity" )
|
|
658
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" "$LAUNCH" stop >/dev/null 2>&1 || true
|
|
659
|
+
if kill -0 "$sleeper_pid" 2>/dev/null && [ -e "$st/state/.afk" ]; then
|
|
660
|
+
pass "stop validation: malformed record causes no daemon or state side effects"
|
|
661
|
+
else
|
|
662
|
+
fail "stop validation: malformed record signaled daemon or cleared state"
|
|
663
|
+
fi
|
|
664
|
+
kill "$sleeper_pid" 2>/dev/null || true
|
|
665
|
+
wait "$sleeper_pid" 2>/dev/null || true
|
|
666
|
+
rm -rf "$st"
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
unit_lock_requires_complete_metadata() {
|
|
670
|
+
local st
|
|
671
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-lock-metadata.XXXXXX")
|
|
672
|
+
mkdir -p "$st/state"
|
|
673
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
674
|
+
. "$1"
|
|
675
|
+
fm_pid_identity() { return 1; }
|
|
676
|
+
! fm_afk_launch_lock_acquire
|
|
677
|
+
' _ "$LAUNCH" && [ ! -e "$st/state/.afk-launch.lock" ]; then
|
|
678
|
+
pass "launcher lock: incomplete metadata fails acquisition and releases lock"
|
|
679
|
+
else
|
|
680
|
+
fail "launcher lock: incomplete metadata was accepted"
|
|
681
|
+
fi
|
|
682
|
+
rm -rf "$st"
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
unit_stop_surfaces_afk_removal_failure() {
|
|
686
|
+
local st
|
|
687
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-stop-remove.XXXXXX")
|
|
688
|
+
mkdir -p "$st/state"
|
|
689
|
+
: > "$st/state/.afk"
|
|
690
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
691
|
+
. "$1"
|
|
692
|
+
rm() { local last=${!#}; [ "$last" != "$FM_AFK_LAUNCH_STATE/.afk" ]; }
|
|
693
|
+
! fm_afk_launch_stop
|
|
694
|
+
' _ "$LAUNCH"; then
|
|
695
|
+
pass "stop state: away-flag removal failure is surfaced"
|
|
696
|
+
else
|
|
697
|
+
fail "stop state: away-flag removal failure reported success"
|
|
698
|
+
fi
|
|
699
|
+
rm -rf "$st"
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
unit_stop_confirms_daemon_exit() {
|
|
703
|
+
local st daemon_pid
|
|
704
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-stop-live.XXXXXX")
|
|
705
|
+
mkdir -p "$st/state/.supervise-daemon.lock"
|
|
706
|
+
: > "$st/state/.afk"
|
|
707
|
+
printf 'none\t-\tnative\n' > "$st/state/.afk-daemon-terminal"
|
|
708
|
+
bash -c 'trap "" TERM; while :; do sleep 1; done' &
|
|
709
|
+
daemon_pid=$!
|
|
710
|
+
printf '%s' "$daemon_pid" > "$st/state/.supervise-daemon.lock/pid"
|
|
711
|
+
( . "$ROOT/bin/fm-wake-lib.sh"; fm_pid_identity "$daemon_pid" > "$st/state/.supervise-daemon.lock/pid-identity" )
|
|
712
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
713
|
+
. "$1"
|
|
714
|
+
seq() { printf "1\n"; }
|
|
715
|
+
sleep() { :; }
|
|
716
|
+
kill() {
|
|
717
|
+
command kill "$@"
|
|
718
|
+
if [ "$1" = -TERM ]; then
|
|
719
|
+
rm -rf "$FM_AFK_LAUNCH_STATE/.supervise-daemon.lock"
|
|
720
|
+
fi
|
|
721
|
+
}
|
|
722
|
+
! fm_afk_launch_stop
|
|
723
|
+
' _ "$LAUNCH" && kill -0 "$daemon_pid" 2>/dev/null \
|
|
724
|
+
&& [ ! -e "$st/state/.supervise-daemon.lock" ] \
|
|
725
|
+
&& [ -e "$st/state/.afk" ] && [ -e "$st/state/.afk-daemon-terminal" ]; then
|
|
726
|
+
pass "stop liveness: captured live daemon preserves lifecycle state after lock release"
|
|
727
|
+
else
|
|
728
|
+
fail "stop liveness: lock release was mistaken for captured daemon exit"
|
|
729
|
+
fi
|
|
730
|
+
kill -KILL "$daemon_pid" 2>/dev/null || true
|
|
731
|
+
wait "$daemon_pid" 2>/dev/null || true
|
|
732
|
+
rm -rf "$st"
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
unit_refresh_validates_record() {
|
|
736
|
+
local st daemon_pid
|
|
737
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-refresh-record.XXXXXX")
|
|
738
|
+
mkdir -p "$st/state/.supervise-daemon.lock"
|
|
739
|
+
printf 'tmux\tonly-two-fields\n' > "$st/state/.afk-daemon-terminal"
|
|
740
|
+
sleep 30 & daemon_pid=$!
|
|
741
|
+
printf '%s' "$daemon_pid" > "$st/state/.supervise-daemon.lock/pid"
|
|
742
|
+
( . "$ROOT/bin/fm-wake-lib.sh"; fm_pid_identity "$daemon_pid" > "$st/state/.supervise-daemon.lock/pid-identity" )
|
|
743
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" FM_SUPERVISOR_TARGET=unused \
|
|
744
|
+
FM_SUPERVISOR_BACKEND=tmux bash -c '
|
|
745
|
+
. "$1"
|
|
746
|
+
! fm_afk_launch_start && ! fm_afk_launch_start_native
|
|
747
|
+
' _ "$LAUNCH" && [ ! -e "$st/state/.afk" ]; then
|
|
748
|
+
pass "refresh record: malformed terminal identity fails closed"
|
|
749
|
+
else
|
|
750
|
+
fail "refresh record: malformed terminal identity was accepted"
|
|
751
|
+
fi
|
|
752
|
+
kill "$daemon_pid" 2>/dev/null || true
|
|
753
|
+
wait "$daemon_pid" 2>/dev/null || true
|
|
754
|
+
rm -rf "$st"
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
unit_clear_failure_aborts_entry() {
|
|
758
|
+
local st
|
|
759
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-clear-fail.XXXXXX")
|
|
760
|
+
mkdir -p "$st/state"
|
|
761
|
+
: > "$st/state/.subsuper-escalations"
|
|
762
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
763
|
+
. "$1"
|
|
764
|
+
fm_afk_launch_reconcile() { return 0; }
|
|
765
|
+
fm_afk_clear_stale_artifacts() { return 1; }
|
|
766
|
+
! fm_afk_launch_start_native
|
|
767
|
+
' _ "$LAUNCH" && [ ! -e "$st/state/.afk" ] && [ -e "$st/state/.subsuper-escalations" ]; then
|
|
768
|
+
pass "clear failure: native entry aborts and restores prior state"
|
|
769
|
+
else
|
|
770
|
+
fail "clear failure: native entry proceeded or lost prior state"
|
|
771
|
+
fi
|
|
772
|
+
rm -rf "$st"
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
unit_confirmed_absence_succeeds() {
|
|
776
|
+
local st
|
|
777
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-confirmed-absent.XXXXXX")
|
|
778
|
+
mkdir -p "$st/state"
|
|
779
|
+
printf 'tmux\texact-session\towned\n' > "$st/state/.afk-daemon-terminal"
|
|
780
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
781
|
+
. "$1"
|
|
782
|
+
fm_afk_launch_close_terminal() { return 1; }
|
|
783
|
+
fm_afk_launch_terminal_absent() { return 0; }
|
|
784
|
+
fm_afk_launch_reconcile
|
|
785
|
+
' _ "$LAUNCH" && [ ! -e "$st/state/.afk-daemon-terminal" ]; then
|
|
786
|
+
pass "confirmed absence: cleanup succeeds and removes the stale record"
|
|
787
|
+
else
|
|
788
|
+
fail "confirmed absence: close error incorrectly failed reconciliation"
|
|
789
|
+
fi
|
|
790
|
+
rm -rf "$st"
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
unit_incomplete_restore_retains_backup() {
|
|
794
|
+
local st backup
|
|
795
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-restore-fail.XXXXXX")
|
|
796
|
+
mkdir -p "$st/state"
|
|
797
|
+
backup=$(mktemp -d "$st/state/.afk-launch-backup.XXXXXX")
|
|
798
|
+
printf 'prior\n' > "$backup/.afk"
|
|
799
|
+
if FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
800
|
+
. "$1"
|
|
801
|
+
cp() { return 1; }
|
|
802
|
+
! fm_afk_launch_restore_backup "$2" 1
|
|
803
|
+
' _ "$LAUNCH" "$backup" && [ -d "$backup" ] && [ -e "$backup/.afk" ]; then
|
|
804
|
+
pass "rollback restore: incomplete restoration retains its recovery backup"
|
|
805
|
+
else
|
|
806
|
+
fail "rollback restore: incomplete restoration discarded its backup"
|
|
807
|
+
fi
|
|
808
|
+
rm -rf "$st"
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
unit_flag_write_failure_aborts() {
|
|
812
|
+
local st
|
|
813
|
+
st=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-flag-fail.XXXXXX")
|
|
814
|
+
mkdir -p "$st/state"
|
|
815
|
+
FM_HOME="$st" FM_STATE_OVERRIDE="$st/state" bash -c '
|
|
816
|
+
. "$1"
|
|
817
|
+
fm_afk_launch_flag_write() { return 1; }
|
|
818
|
+
! fm_afk_launch_start_native
|
|
819
|
+
' _ "$LAUNCH"
|
|
820
|
+
if [ ! -e "$st/state/.afk" ] && [ ! -e "$st/state/.afk-daemon-terminal" ]; then
|
|
821
|
+
pass "flag failure: lifecycle aborts without active state"
|
|
822
|
+
else
|
|
823
|
+
fail "flag failure: lifecycle reported active state"
|
|
824
|
+
fi
|
|
825
|
+
rm -rf "$st"
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
# ---------------------------------------------------------------------------
|
|
829
|
+
# E2E herdr: topology invariant.
|
|
830
|
+
# ---------------------------------------------------------------------------
|
|
831
|
+
e2e_herdr() {
|
|
832
|
+
command -v herdr >/dev/null 2>&1 || { echo "skip: herdr not found (herdr e2e)"; return 0; }
|
|
833
|
+
command -v jq >/dev/null 2>&1 || { echo "skip: jq not found (herdr e2e)"; return 0; }
|
|
834
|
+
# shellcheck source=tests/herdr-test-safety.sh
|
|
835
|
+
. "$ROOT/tests/herdr-test-safety.sh"
|
|
836
|
+
# shellcheck source=/dev/null
|
|
837
|
+
. "$ROOT/bin/fm-backend.sh"
|
|
838
|
+
|
|
839
|
+
local SESSION home_tmp cap_ws cap_tab cap_pane target
|
|
840
|
+
local before during after ws_before ws_during ws_after out dtgt dtab
|
|
841
|
+
SESSION="fm-lab-afk-launch-e2e-$$"
|
|
842
|
+
export HERDR_SESSION="$SESSION"
|
|
843
|
+
home_tmp=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-e2e-home.XXXXXX")
|
|
844
|
+
E2E_HERDR_CLEANUP() {
|
|
845
|
+
# shellcheck disable=SC2031 # Cleanup reads the caller's resolved target; it does not reassign it.
|
|
846
|
+
FM_HOME="$home_tmp" FM_STATE_OVERRIDE="$home_tmp/state" \
|
|
847
|
+
FM_SUPERVISOR_TARGET="$target" FM_SUPERVISOR_BACKEND=herdr "$LAUNCH" stop >/dev/null 2>&1 || true
|
|
848
|
+
herdr_safe_stop_and_delete "$SESSION" >/dev/null 2>&1 || true
|
|
849
|
+
rm -rf "$home_tmp" 2>/dev/null || true
|
|
850
|
+
}
|
|
851
|
+
fm_herdr_lab_prepare "$SESSION" || { fail "herdr e2e: could not prepare isolated lab session"; return 0; }
|
|
852
|
+
fm_backend_source herdr || { E2E_HERDR_CLEANUP; fail "herdr e2e: fm_backend_source herdr failed"; return 0; }
|
|
853
|
+
fm_backend_herdr_server_ensure "$SESSION" || { E2E_HERDR_CLEANUP; fail "herdr e2e: lab server did not start"; return 0; }
|
|
854
|
+
|
|
855
|
+
out=$(fm_backend_herdr_cli "$SESSION" workspace create --cwd "$ROOT" --label captain --no-focus 2>/dev/null)
|
|
856
|
+
cap_ws=$(printf '%s' "$out" | jq -r '.result.workspace.workspace_id // empty')
|
|
857
|
+
cap_tab=$(printf '%s' "$out" | jq -r '.result.tab.tab_id // empty')
|
|
858
|
+
cap_pane=$(printf '%s' "$out" | jq -r '.result.root_pane.pane_id // empty')
|
|
859
|
+
if [ -z "$cap_ws" ] || [ -z "$cap_pane" ]; then E2E_HERDR_CLEANUP; fail "herdr e2e: could not create captain workspace"; return 0; fi
|
|
860
|
+
target="$SESSION:$cap_pane"
|
|
861
|
+
before=$(fm_backend_herdr_cli "$SESSION" pane list --workspace "$cap_ws" 2>/dev/null | jq --arg t "$cap_tab" '[.result.panes[]?|select(.tab_id==$t)]|length')
|
|
862
|
+
ws_before=$(fm_backend_herdr_cli "$SESSION" workspace list 2>/dev/null | jq '[.result.workspaces[]?]|length')
|
|
863
|
+
|
|
864
|
+
FM_HOME="$home_tmp" FM_STATE_OVERRIDE="$home_tmp/state" \
|
|
865
|
+
FM_SUPERVISOR_TARGET="$target" FM_SUPERVISOR_BACKEND=herdr FM_AFK_LAUNCH_ENTRY="$SLEEPER" \
|
|
866
|
+
"$LAUNCH" start >/dev/null 2>&1
|
|
867
|
+
|
|
868
|
+
during=$(fm_backend_herdr_cli "$SESSION" pane list --workspace "$cap_ws" 2>/dev/null | jq --arg t "$cap_tab" '[.result.panes[]?|select(.tab_id==$t)]|length')
|
|
869
|
+
ws_during=$(fm_backend_herdr_cli "$SESSION" workspace list 2>/dev/null | jq '[.result.workspaces[]?]|length')
|
|
870
|
+
dtgt=$(cut -f2 "$home_tmp/state/.afk-daemon-terminal" 2>/dev/null || true)
|
|
871
|
+
dtab=$(fm_backend_herdr_cli "$SESSION" pane get "${dtgt#*:}" 2>/dev/null | jq -r '.result.pane.tab_id // empty')
|
|
872
|
+
|
|
873
|
+
if [ "$before" = "$during" ]; then pass "herdr e2e: captain tab pane count unchanged after start (no split)"; else fail "herdr e2e: captain tab pane count changed ($before -> $during)"; fi
|
|
874
|
+
if [ "$ws_during" -gt "$ws_before" ]; then pass "herdr e2e: daemon launched in a separate non-visible workspace"; else fail "herdr e2e: no separate daemon workspace created"; fi
|
|
875
|
+
if [ -n "$dtab" ] && [ "$dtab" != "$cap_tab" ]; then pass "herdr e2e: daemon pane is NOT in the captain's tab"; else fail "herdr e2e: daemon pane shares the captain tab ($dtab)"; fi
|
|
876
|
+
case "$dtgt" in "$SESSION":*) pass "herdr e2e: daemon terminal scoped to the lab session" ;; *) fail "herdr e2e: daemon terminal not in the lab session ($dtgt)" ;; esac
|
|
877
|
+
|
|
878
|
+
FM_HOME="$home_tmp" FM_STATE_OVERRIDE="$home_tmp/state" \
|
|
879
|
+
FM_SUPERVISOR_TARGET="$target" FM_SUPERVISOR_BACKEND=herdr "$LAUNCH" stop >/dev/null 2>&1
|
|
880
|
+
|
|
881
|
+
after=$(fm_backend_herdr_cli "$SESSION" pane list --workspace "$cap_ws" 2>/dev/null | jq --arg t "$cap_tab" '[.result.panes[]?|select(.tab_id==$t)]|length')
|
|
882
|
+
ws_after=$(fm_backend_herdr_cli "$SESSION" workspace list 2>/dev/null | jq '[.result.workspaces[]?]|length')
|
|
883
|
+
if [ "$after" = "$before" ]; then pass "herdr e2e: captain tab pane count restored after stop"; else fail "herdr e2e: captain tab pane count not restored ($before -> $after)"; fi
|
|
884
|
+
if [ "$ws_after" = "$ws_before" ]; then pass "herdr e2e: daemon workspace removed by exact id on stop"; else fail "herdr e2e: daemon workspace leaked ($ws_before -> $ws_after)"; fi
|
|
885
|
+
if [ ! -e "$home_tmp/state/.afk-daemon-terminal" ] && [ ! -e "$home_tmp/state/.afk" ]; then pass "herdr e2e: record + .afk cleared on stop"; else fail "herdr e2e: record or .afk not cleared"; fi
|
|
886
|
+
|
|
887
|
+
E2E_HERDR_CLEANUP
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
# ---------------------------------------------------------------------------
|
|
891
|
+
# E2E tmux: topology invariant (captain window untouched; daemon in a separate
|
|
892
|
+
# detached session).
|
|
893
|
+
# ---------------------------------------------------------------------------
|
|
894
|
+
e2e_tmux() {
|
|
895
|
+
command -v tmux >/dev/null 2>&1 || { echo "skip: tmux not found (tmux e2e)"; return 0; }
|
|
896
|
+
local cap_session home_tmp cap_pane before during after rec
|
|
897
|
+
cap_session="fm-afk-launch-cap-$$"
|
|
898
|
+
home_tmp=$(mktemp -d "${TMPDIR:-/tmp}/fm-afk-tmux-home.XXXXXX")
|
|
899
|
+
tmux new-session -d -s "$cap_session" 2>/dev/null || { fail "tmux e2e: could not create captain session"; rm -rf "$home_tmp"; return 0; }
|
|
900
|
+
TRACK_TMUX_SESSIONS="$TRACK_TMUX_SESSIONS $cap_session"
|
|
901
|
+
cap_pane=$(tmux display-message -p -t "$cap_session" '#{pane_id}')
|
|
902
|
+
before=$(tmux list-panes -t "$cap_session" | wc -l | tr -d ' ')
|
|
903
|
+
|
|
904
|
+
FM_HOME="$home_tmp" FM_STATE_OVERRIDE="$home_tmp/state" \
|
|
905
|
+
FM_SUPERVISOR_TARGET="$cap_pane" FM_SUPERVISOR_BACKEND=tmux FM_AFK_LAUNCH_ENTRY="$SLEEPER" \
|
|
906
|
+
"$LAUNCH" start >/dev/null 2>&1
|
|
907
|
+
|
|
908
|
+
during=$(tmux list-panes -t "$cap_session" | wc -l | tr -d ' ')
|
|
909
|
+
rec=$(cut -f2 "$home_tmp/state/.afk-daemon-terminal" 2>/dev/null || true)
|
|
910
|
+
TRACK_TMUX_SESSIONS="$TRACK_TMUX_SESSIONS $rec"
|
|
911
|
+
if [ "$before" = "$during" ]; then pass "tmux e2e: captain window pane count unchanged after start (no split-window)"; else fail "tmux e2e: captain window pane count changed ($before -> $during)"; fi
|
|
912
|
+
if [ -n "$rec" ] && tmux has-session -t "$rec" 2>/dev/null && [ "$rec" != "$cap_session" ]; then pass "tmux e2e: daemon launched in a separate detached session"; else fail "tmux e2e: no separate daemon session ($rec)"; fi
|
|
913
|
+
|
|
914
|
+
FM_HOME="$home_tmp" FM_STATE_OVERRIDE="$home_tmp/state" \
|
|
915
|
+
FM_SUPERVISOR_TARGET="$cap_pane" FM_SUPERVISOR_BACKEND=tmux "$LAUNCH" stop >/dev/null 2>&1
|
|
916
|
+
|
|
917
|
+
after=$(tmux list-panes -t "$cap_session" | wc -l | tr -d ' ')
|
|
918
|
+
if [ "$after" = "$before" ]; then pass "tmux e2e: captain window pane count unchanged after stop"; else fail "tmux e2e: captain window changed ($before -> $after)"; fi
|
|
919
|
+
if [ -n "$rec" ] && ! tmux has-session -t "$rec" 2>/dev/null; then pass "tmux e2e: daemon session killed by exact id on stop"; else fail "tmux e2e: daemon session leaked ($rec)"; fi
|
|
920
|
+
if [ ! -e "$home_tmp/state/.afk-daemon-terminal" ] && [ ! -e "$home_tmp/state/.afk" ]; then pass "tmux e2e: record + .afk cleared on stop"; else fail "tmux e2e: record or .afk not cleared"; fi
|
|
921
|
+
|
|
922
|
+
tmux kill-session -t "$cap_session" 2>/dev/null || true
|
|
923
|
+
rm -rf "$home_tmp" 2>/dev/null || true
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
unit_clear_stale
|
|
927
|
+
unit_relative_paths_are_absolute_before_daemon_launch
|
|
928
|
+
unit_fresh_vs_refresh
|
|
929
|
+
unit_stop_ordering
|
|
930
|
+
unit_stop_rejects_reused_pid
|
|
931
|
+
unit_failed_start_rolls_back_state
|
|
932
|
+
unit_concurrent_start_serialized
|
|
933
|
+
unit_lock_initialization_grace
|
|
934
|
+
unit_signal_exits_with_lock_cleanup
|
|
935
|
+
unit_herdr_partial_create_recovery
|
|
936
|
+
unit_herdr_error_with_exact_ids_closes_exact
|
|
937
|
+
unit_herdr_run_failure_preserves_unconfirmed_record
|
|
938
|
+
unit_record_failure_closes_terminal
|
|
939
|
+
unit_readiness_failure_rolls_back_terminal
|
|
940
|
+
unit_readiness_failure_preserves_unconfirmed_record
|
|
941
|
+
unit_tmux_absence_distinguishes_probe_failure
|
|
942
|
+
unit_native_lifecycle
|
|
943
|
+
unit_native_entry_preserves_prepared_state
|
|
944
|
+
unit_close_failure_preserves_record
|
|
945
|
+
unit_record_publication_atomic
|
|
946
|
+
unit_malformed_record_fails_closed
|
|
947
|
+
unit_stop_malformed_record_fails_closed
|
|
948
|
+
unit_tmux_planned_record_and_collision
|
|
949
|
+
unit_stop_validates_before_signal
|
|
950
|
+
unit_lock_requires_complete_metadata
|
|
951
|
+
unit_stop_surfaces_afk_removal_failure
|
|
952
|
+
unit_stop_confirms_daemon_exit
|
|
953
|
+
unit_refresh_validates_record
|
|
954
|
+
unit_clear_failure_aborts_entry
|
|
955
|
+
unit_confirmed_absence_succeeds
|
|
956
|
+
unit_incomplete_restore_retains_backup
|
|
957
|
+
unit_flag_write_failure_aborts
|
|
958
|
+
e2e_herdr
|
|
959
|
+
e2e_tmux
|
|
960
|
+
|
|
961
|
+
[ "$FAILED" -eq 0 ] || exit 1
|