@zerwiz/ymir 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/README.md +291 -0
- package/.agents/agents/bragi-marketer.md +111 -0
- package/.agents/agents/brokk.md +352 -0
- package/.agents/agents/forseti-reviewer.md +93 -0
- package/.agents/agents/frigg-consent.md +77 -0
- package/.agents/agents/groa-update.md +77 -0
- package/.agents/agents/hnoss-designer.md +69 -0
- package/.agents/agents/huginn-researcher.md +99 -0
- package/.agents/agents/jord-projects.md +78 -0
- package/.agents/agents/kvasir-scout.md +91 -0
- package/.agents/agents/mimir-planner.md +92 -0
- package/.agents/agents/muninn-stow.md +77 -0
- package/.agents/agents/saga-bearings.md +78 -0
- package/.agents/agents/sindri-developer.md +114 -0
- package/.agents/agents/snotra-documenter.md +105 -0
- package/.agents/agents/syn-recovery.md +78 -0
- package/.agents/agents/tyr-check.md +78 -0
- package/.agents/agents/urdh-hold.md +77 -0
- package/.agents/agents/volundr-smith.md +80 -0
- package/.agents/agents/vor-diagnostics.md +77 -0
- package/.agents/assets/agents/naming.md +50 -0
- package/.agents/assets/agents/registry.md +93 -0
- package/.agents/assets/agents/runtime.md +36 -0
- package/.agents/assets/agents/toon-tasks-cli.md +42 -0
- package/.agents/assets/templates/company_entity.template.md +37 -0
- package/.agents/backend/backends/cmux.sh +649 -0
- package/.agents/backend/backends/herdr-eventwait.py +157 -0
- package/.agents/backend/backends/herdr-workspace-move.py +114 -0
- package/.agents/backend/backends/herdr.sh +3386 -0
- package/.agents/backend/backends/orca.sh +290 -0
- package/.agents/backend/backends/tmux.sh +357 -0
- package/.agents/backend/backends/zellij.sh +700 -0
- package/.agents/backend/fm-afk-launch.sh +654 -0
- package/.agents/backend/fm-afk-return.sh +241 -0
- package/.agents/backend/fm-afk-start.sh +172 -0
- package/.agents/backend/fm-arm-command-policy.mjs +960 -0
- package/.agents/backend/fm-arm-pretool-check.sh +200 -0
- package/.agents/backend/fm-backend-hometag-lib.sh +52 -0
- package/.agents/backend/fm-backend.sh +981 -0
- package/.agents/backend/fm-backlog-handoff.sh +909 -0
- package/.agents/backend/fm-backlog-receive.sh +187 -0
- package/.agents/backend/fm-backlog-transition-lib.sh +779 -0
- package/.agents/backend/fm-bearings-board.sh +204 -0
- package/.agents/backend/fm-bearings-snapshot.sh +546 -0
- package/.agents/backend/fm-bootstrap.sh +1568 -0
- package/.agents/backend/fm-branch-outcome.sh +203 -0
- package/.agents/backend/fm-branch-prompt.sh +101 -0
- package/.agents/backend/fm-brief.sh +452 -0
- package/.agents/backend/fm-busy-event.sh +234 -0
- package/.agents/backend/fm-busy-lib.sh +986 -0
- package/.agents/backend/fm-captain-hold.sh +1001 -0
- package/.agents/backend/fm-cd-command-policy.mjs +153 -0
- package/.agents/backend/fm-cd-pretool-check.sh +190 -0
- package/.agents/backend/fm-check-lib.sh +74 -0
- package/.agents/backend/fm-check-register.sh +42 -0
- package/.agents/backend/fm-check-unregister.sh +52 -0
- package/.agents/backend/fm-classify-lib.sh +1770 -0
- package/.agents/backend/fm-claude-stop-autoarm.sh +334 -0
- package/.agents/backend/fm-composer-lib.sh +1416 -0
- package/.agents/backend/fm-config-inherit-lib.sh +1202 -0
- package/.agents/backend/fm-config-push.sh +254 -0
- package/.agents/backend/fm-control-lib.sh +251 -0
- package/.agents/backend/fm-control.sh +878 -0
- package/.agents/backend/fm-crew-state.sh +625 -0
- package/.agents/backend/fm-cursor-lib.sh +243 -0
- package/.agents/backend/fm-decision-hold.sh +232 -0
- package/.agents/backend/fm-doc-audience-check.sh +269 -0
- package/.agents/backend/fm-dod-lib.sh +67 -0
- package/.agents/backend/fm-ensure-agents-md.sh +253 -0
- package/.agents/backend/fm-extension-launch-barrier.mjs +129 -0
- package/.agents/backend/fm-extension.mjs +2577 -0
- package/.agents/backend/fm-extension.sh +16 -0
- package/.agents/backend/fm-ff-lib.sh +420 -0
- package/.agents/backend/fm-fleet-snapshot.sh +1463 -0
- package/.agents/backend/fm-fleet-sync.sh +458 -0
- package/.agents/backend/fm-fleet-view.sh +96 -0
- package/.agents/backend/fm-gate-refuse-lib.sh +102 -0
- package/.agents/backend/fm-guard.sh +242 -0
- package/.agents/backend/fm-harness.sh +195 -0
- package/.agents/backend/fm-herdr-ci-cleanup.sh +115 -0
- package/.agents/backend/fm-herdr-lab.sh +346 -0
- package/.agents/backend/fm-herdr-session-cleanup.sh +337 -0
- package/.agents/backend/fm-home-seed.sh +984 -0
- package/.agents/backend/fm-home-summary-refresh.sh +255 -0
- package/.agents/backend/fm-hook-host-lib.sh +36 -0
- package/.agents/backend/fm-inactive-reconcile.sh +531 -0
- package/.agents/backend/fm-inbox.sh +399 -0
- package/.agents/backend/fm-install-actionlint.sh +84 -0
- package/.agents/backend/fm-install-herdr.sh +96 -0
- package/.agents/backend/fm-install-shellcheck.sh +84 -0
- package/.agents/backend/fm-install-treehouse.sh +95 -0
- package/.agents/backend/fm-kimi-turnend-hook.sh +276 -0
- package/.agents/backend/fm-lease-lib.sh +218 -0
- package/.agents/backend/fm-lease.sh +189 -0
- package/.agents/backend/fm-line-cap-lib.sh +51 -0
- package/.agents/backend/fm-lint-workflows.sh +137 -0
- package/.agents/backend/fm-lint.sh +591 -0
- package/.agents/backend/fm-lock-lib.sh +104 -0
- package/.agents/backend/fm-lock.sh +107 -0
- package/.agents/backend/fm-marker-lib.sh +12 -0
- package/.agents/backend/fm-merge-local.sh +74 -0
- package/.agents/backend/fm-merge-outcome-lib.sh +138 -0
- package/.agents/backend/fm-nm-run-lib.sh +124 -0
- package/.agents/backend/fm-on.sh +125 -0
- package/.agents/backend/fm-operational-input.sh +253 -0
- package/.agents/backend/fm-peek.sh +46 -0
- package/.agents/backend/fm-pending-reply-lib.sh +1409 -0
- package/.agents/backend/fm-pr-check.sh +135 -0
- package/.agents/backend/fm-pr-lib.sh +1016 -0
- package/.agents/backend/fm-pr-merge.sh +707 -0
- package/.agents/backend/fm-pr-poll.sh +110 -0
- package/.agents/backend/fm-primary-scope-lib.sh +33 -0
- package/.agents/backend/fm-procevent-extension-capture.pl +259 -0
- package/.agents/backend/fm-procevent-lavish.sh +628 -0
- package/.agents/backend/fm-procevent-lib.sh +892 -0
- package/.agents/backend/fm-procevent-quota.sh +290 -0
- package/.agents/backend/fm-procevent-remote-reply.sh +577 -0
- package/.agents/backend/fm-procevent-when.sh +504 -0
- package/.agents/backend/fm-procevent.sh +1466 -0
- package/.agents/backend/fm-project-mode.sh +92 -0
- package/.agents/backend/fm-project-origin-lib.sh +180 -0
- package/.agents/backend/fm-promote.sh +282 -0
- package/.agents/backend/fm-public-followup-emit.sh +260 -0
- package/.agents/backend/fm-public-followup-lib.sh +398 -0
- package/.agents/backend/fm-public-followup.sh +1330 -0
- package/.agents/backend/fm-push-transition-lib.sh +168 -0
- package/.agents/backend/fm-quota-axi-lib.sh +93 -0
- package/.agents/backend/fm-quota-choose.sh +384 -0
- package/.agents/backend/fm-remote-delta-read.sh +189 -0
- package/.agents/backend/fm-remote-doctor.sh +800 -0
- package/.agents/backend/fm-remote-entrypoint.sh +193 -0
- package/.agents/backend/fm-remote-file.sh +234 -0
- package/.agents/backend/fm-remote-home-provision.sh +259 -0
- package/.agents/backend/fm-remote-home-seed.sh +259 -0
- package/.agents/backend/fm-remote-inherit-push.sh +90 -0
- package/.agents/backend/fm-remote-inherit.sh +179 -0
- package/.agents/backend/fm-remote-job-lib.sh +1241 -0
- package/.agents/backend/fm-remote-job-reap-orphans.sh +137 -0
- package/.agents/backend/fm-remote-job-worker.sh +1124 -0
- package/.agents/backend/fm-remote-readiness-lib.sh +44 -0
- package/.agents/backend/fm-remote-secondmate-control.sh +340 -0
- package/.agents/backend/fm-review-diff.sh +158 -0
- package/.agents/backend/fm-secondmate-charter-lib.sh +44 -0
- package/.agents/backend/fm-secondmate-nudge-lib.sh +68 -0
- package/.agents/backend/fm-secondmate-parent-lib.sh +70 -0
- package/.agents/backend/fm-secondmate-reconcile.sh +371 -0
- package/.agents/backend/fm-secondmate-registry-lib.sh +311 -0
- package/.agents/backend/fm-secondmate-report.sh +88 -0
- package/.agents/backend/fm-send.sh +1059 -0
- package/.agents/backend/fm-session-lock-lib.sh +176 -0
- package/.agents/backend/fm-session-start.sh +970 -0
- package/.agents/backend/fm-sessionstart-cursor.sh +40 -0
- package/.agents/backend/fm-sessionstart-nudge.sh +45 -0
- package/.agents/backend/fm-sessionstart-run.sh +145 -0
- package/.agents/backend/fm-spawn.sh +3153 -0
- package/.agents/backend/fm-startup-memory-budget-lib.sh +224 -0
- package/.agents/backend/fm-startup-memory-budget.sh +94 -0
- package/.agents/backend/fm-startup-network.sh +642 -0
- package/.agents/backend/fm-stow-cascade.sh +251 -0
- package/.agents/backend/fm-subagent-pretool-check.sh +207 -0
- package/.agents/backend/fm-supervise-daemon.sh +1732 -0
- package/.agents/backend/fm-supervision-instructions.sh +215 -0
- package/.agents/backend/fm-supervision-lib.sh +90 -0
- package/.agents/backend/fm-supervisor-target-lib.sh +78 -0
- package/.agents/backend/fm-tangle-lib.sh +53 -0
- package/.agents/backend/fm-task-inbox-lib.sh +402 -0
- package/.agents/backend/fm-tasks-axi-lib.sh +123 -0
- package/.agents/backend/fm-teardown.sh +2922 -0
- package/.agents/backend/fm-test-isolation-proof.sh +588 -0
- package/.agents/backend/fm-test-run.sh +2191 -0
- package/.agents/backend/fm-timeout-lib.sh +141 -0
- package/.agents/backend/fm-timing-lib.sh +179 -0
- package/.agents/backend/fm-tmux-lib.sh +291 -0
- package/.agents/backend/fm-tool-update-check.sh +898 -0
- package/.agents/backend/fm-trace-context-lib.sh +227 -0
- package/.agents/backend/fm-transition-lib.sh +103 -0
- package/.agents/backend/fm-turnend-guard-cursor.sh +391 -0
- package/.agents/backend/fm-turnend-guard-grok.sh +90 -0
- package/.agents/backend/fm-turnend-guard.sh +432 -0
- package/.agents/backend/fm-update.sh +110 -0
- package/.agents/backend/fm-vendor-auth-probe.sh +190 -0
- package/.agents/backend/fm-voice-client.py +1373 -0
- package/.agents/backend/fm-voice-relay.py +1256 -0
- package/.agents/backend/fm-wake-drain.sh +592 -0
- package/.agents/backend/fm-wake-grant.sh +127 -0
- package/.agents/backend/fm-wake-lib.sh +1849 -0
- package/.agents/backend/fm-watch-arm.sh +602 -0
- package/.agents/backend/fm-watch-checkpoint.sh +109 -0
- package/.agents/backend/fm-watch.sh +1962 -0
- package/.agents/backend/fm-x-dismiss.sh +116 -0
- package/.agents/backend/fm-x-followup.sh +286 -0
- package/.agents/backend/fm-x-lib.sh +1000 -0
- package/.agents/backend/fm-x-link.sh +234 -0
- package/.agents/backend/fm-x-poll.sh +185 -0
- package/.agents/backend/fm-x-reply.sh +385 -0
- package/.agents/backend/fm_voice_frame.py +166 -0
- package/.agents/backend/fm_voice_records.py +574 -0
- package/.agents/backend/model-bridge.py +290 -0
- package/.agents/backend/opencode-go-bridge.py +30 -0
- package/.agents/bus/README.md +12 -0
- package/.agents/bus/messages.json +1 -0
- package/.agents/config/agents.machine.example.yaml +16 -0
- package/.agents/config/agents.yaml +50 -0
- package/.agents/config/agents.yaml.example +72 -0
- package/.agents/config/crew-dispatch.json +22 -0
- package/.agents/config/cron.yaml +9 -0
- package/.agents/config/eindri-dispatch.json +67 -0
- package/.agents/config/eindri-harness +1 -0
- package/.agents/config/startup-memory-budget +1 -0
- package/.agents/config/tailscale-sync.yaml +27 -0
- package/.agents/filebrowser/.filebrowser.json +6 -0
- package/.agents/filebrowser/README.md +11 -0
- package/.agents/gateway/README.md +12 -0
- package/.agents/github/README.md +15 -0
- package/.agents/github/webhooks/.gitkeep +0 -0
- package/.agents/github/workflows/.gitkeep +0 -0
- package/.agents/harness/opencode/plugins/lib/rodd-operational-input.js +37 -0
- package/.agents/harness/opencode/plugins/package.json +4 -0
- package/.agents/harness/opencode/plugins/saga-sessionstart.js +77 -0
- package/.agents/harness/opencode/plugins/syn-cd-check.js +61 -0
- package/.agents/harness/opencode/plugins/syn-pretool-check.js +68 -0
- package/.agents/harness/opencode/plugins/syn-turnend-guard.js +105 -0
- package/.agents/harness/opencode/plugins/syn-watch-arm.js +521 -0
- package/.agents/memory/README.md +46 -0
- package/.agents/memory/kaia.engram +0 -0
- package/.agents/memory/kaia.engram-shm +0 -0
- package/.agents/memory/kaia.engram-wal +0 -0
- package/.agents/memory/runes_audit.md +5 -0
- package/.agents/memory/well/episodes.jsonl +367 -0
- package/.agents/memory/well/workspace.jsonl +27 -0
- package/.agents/migrations/0001-hodd-layout.sh +38 -0
- package/.agents/migrations/0002-a2a-mcp.sh +27 -0
- package/.agents/migrations/0003-private-data-separation.sh +111 -0
- package/.agents/migrations/0004-hoard-and-realms.sh +104 -0
- package/.agents/sandbox/Dockerfile.utgard +19 -0
- package/.agents/sandbox/README.md +14 -0
- package/.agents/sandbox/utgard.config.json +14 -0
- package/.agents/skills/README.md +59 -0
- package/.agents/skills/bragi-marketing/README.md +3 -0
- package/.agents/skills/bragi-marketing/SKILL.md +82 -0
- package/.agents/skills/eindri-homes/README.md +3 -0
- package/.agents/skills/eindri-homes/SKILL.md +291 -0
- package/.agents/skills/eindri-homes/assets/control-plane.md +122 -0
- package/.agents/skills/eindri-homes/assets/dispatch.md +80 -0
- package/.agents/skills/frigg-consent/README.md +3 -0
- package/.agents/skills/frigg-consent/SKILL.md +56 -0
- package/.agents/skills/galdr-ymirsystem/README.md +3 -0
- package/.agents/skills/galdr-ymirsystem/SKILL.md +158 -0
- package/.agents/skills/galdr-ymirsystem/assets/README.md +74 -0
- package/.agents/skills/galdr-ymirsystem/assets/brokk-distro-runtime.md +390 -0
- package/.agents/skills/galdr-ymirsystem/assets/build-method.md +113 -0
- package/.agents/skills/galdr-ymirsystem/assets/build-tool-categories.md +178 -0
- package/.agents/skills/galdr-ymirsystem/assets/eindri-orchestration.md +518 -0
- package/.agents/skills/galdr-ymirsystem/assets/eindri-profiles.md +136 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +673 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/claude-code.md +197 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/codex.md +191 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/cursor.md +193 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/opencode.md +262 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/pi.md +295 -0
- package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +496 -0
- package/.agents/skills/galdr-ymirsystem/assets/installation.md +571 -0
- package/.agents/skills/galdr-ymirsystem/assets/local-models/HOST-DATA.example.md +72 -0
- package/.agents/skills/galdr-ymirsystem/assets/local-models.md +274 -0
- package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +161 -0
- package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +377 -0
- package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
- package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +129 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/einherjar-spawn.schema.json +81 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/herdr-profile.toml +75 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +96 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/supervision-tree.yml +163 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot-guide.md +58 -0
- package/.agents/skills/galdr-ymirsystem/assets/porting-upstream-to-norse.md +301 -0
- package/.agents/skills/galdr-ymirsystem/assets/principles.md +385 -0
- package/.agents/skills/galdr-ymirsystem/assets/reference-adoption.md +99 -0
- package/.agents/skills/galdr-ymirsystem/assets/registry.md +379 -0
- package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +463 -0
- package/.agents/skills/galdr-ymirsystem/assets/runtime-components.md +553 -0
- package/.agents/skills/galdr-ymirsystem/assets/smidja.md +175 -0
- package/.agents/skills/galdr-ymirsystem/schemas/toon-schemas.md +190 -0
- package/.agents/skills/galdr-ymirsystem/scripts/README.md +45 -0
- package/.agents/skills/galdr-ymirsystem/scripts/bench-one.sh +248 -0
- package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +360 -0
- package/.agents/skills/galdr-ymirsystem/scripts/gpu-sample.sh +43 -0
- package/.agents/skills/galdr-ymirsystem/scripts/stop-all.sh +34 -0
- package/.agents/skills/galdr-ymirsystem/scripts/toon-check.py +188 -0
- package/.agents/skills/gjallarhorn-relay/README.md +3 -0
- package/.agents/skills/gjallarhorn-relay/SKILL.md +283 -0
- package/.agents/skills/groa-update/SKILL.md +70 -0
- package/.agents/skills/hamr-adapters/README.md +3 -0
- package/.agents/skills/hamr-adapters/SKILL.md +123 -0
- package/.agents/skills/hamr-adapters/references/common/control-and-recovery.md +37 -0
- package/.agents/skills/hamr-adapters/references/common/dispatch.md +32 -0
- package/.agents/skills/hamr-adapters/references/common/model-and-effort.md +42 -0
- package/.agents/skills/hamr-adapters/references/common/primary-hooks.md +40 -0
- package/.agents/skills/hamr-adapters/references/harness/claude.md +55 -0
- package/.agents/skills/hamr-adapters/references/harness/codex.md +43 -0
- package/.agents/skills/hamr-adapters/references/harness/cursor.md +75 -0
- package/.agents/skills/hamr-adapters/references/harness/grok.md +69 -0
- package/.agents/skills/hamr-adapters/references/harness/kimi.md +51 -0
- package/.agents/skills/hamr-adapters/references/harness/muse.md +70 -0
- package/.agents/skills/hamr-adapters/references/harness/opencode.md +42 -0
- package/.agents/skills/hamr-adapters/references/harness/pi.md +56 -0
- package/.agents/skills/herdr-panes/README.md +3 -0
- package/.agents/skills/herdr-panes/SKILL.md +92 -0
- package/.agents/skills/herdr-panes/assets/herdr-backend.md +346 -0
- package/.agents/skills/herdr-panes/assets/tmux-backend.md +115 -0
- package/.agents/skills/hnoss-design/DESIGN.md +19 -0
- package/.agents/skills/hnoss-design/README.md +3 -0
- package/.agents/skills/hnoss-design/SKILL.md +83 -0
- package/.agents/skills/hvild-afk/README.md +3 -0
- package/.agents/skills/hvild-afk/SKILL.md +241 -0
- package/.agents/skills/jord-projects/README.md +3 -0
- package/.agents/skills/jord-projects/SKILL.md +92 -0
- package/.agents/skills/lifecycle/SKILL.md +56 -0
- package/.agents/skills/lifecycle/smoke_test.sh +52 -0
- package/.agents/skills/lifecycle/start.sh +21 -0
- package/.agents/skills/lifecycle/status.sh +35 -0
- package/.agents/skills/lifecycle/stop.sh +16 -0
- package/.agents/skills/muninn-stow/README.md +3 -0
- package/.agents/skills/muninn-stow/SKILL.md +309 -0
- package/.agents/skills/no-mistakes/SKILL.md +379 -0
- package/.agents/skills/nornir-schedule/README.md +3 -0
- package/.agents/skills/nornir-schedule/SKILL.md +25 -0
- package/.agents/skills/nornir-schedule/assets/events.md +137 -0
- package/.agents/skills/nornir-schedule/assets/quota.md +131 -0
- package/.agents/skills/nsr-compliance/README.md +3 -0
- package/.agents/skills/nsr-compliance/SKILL.md +29 -0
- package/.agents/skills/nsr-compliance/assets/nsr/README.md +40 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/README.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/git-workflow.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/security.md +22 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/tech-stack.md +8 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/SKILL.md +71 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/config/core_four.yaml +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/validate_code.sh +29 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/verify_docs.py +66 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/generate.sh +109 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/telemetry/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/README.md +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/README.md +36 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/SKILL-template.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/README.md +25 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/SKILL.md +13 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/create_branch.sh +6 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/safe_commit.sh +9 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/sync_upstream.sh +6 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/README.md +27 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/SKILL.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/smoke_test.sh +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/start.sh +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/status.sh +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/stop.sh +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/core_four.yaml +13 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/README.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/api.md +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/compliance-wiring.md +155 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/frontend.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/README.md +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/README.md +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/template-README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/deploy.sh +49 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/development.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/production.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/staging.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/template.env.example +8 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/README.md +26 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/agent.md +7 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/developers/dev-1.md +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/linux.md +9 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/macos.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/shared.md +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/windows.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/README.md +25 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/_template.md +41 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/hosting-1.md +41 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/README.md +21 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/README.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/deployment.md +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/disaster-recovery.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/ops-tasks.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/features/README.md +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/project-skills.md +146 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/software-compliance-research.md +269 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/working-with-agents.md +260 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/AGENTS.md +38 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/ARCHITECTURE.md +36 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/BEST_PRACTICES.md +27 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/CI_CD.md +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/FEATURES.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/README.md +22 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/RULES.md +37 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/STRUCTURE.md +45 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/TECH_STACK.md +28 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scaffold-spec.md +63 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scripts/scaffold.sh +103 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scripts/validate.sh +52 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/SKILL.md +71 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/config/core_four.yaml +16 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/validate_code.sh +29 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/verify_docs.py +66 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/generate.sh +109 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/telemetry/README.md +11 -0
- package/.agents/skills/pr-ops/README.md +3 -0
- package/.agents/skills/pr-ops/SKILL.md +109 -0
- package/.agents/skills/ratatoskr-a2a/README.md +3 -0
- package/.agents/skills/ratatoskr-a2a/SKILL.md +148 -0
- package/.agents/skills/rules-check-drift/SKILL.md +81 -0
- package/.agents/skills/saga-bearings/README.md +3 -0
- package/.agents/skills/saga-bearings/SKILL.md +26 -0
- package/.agents/skills/saga-bearings/assets/bearings.md +161 -0
- package/.agents/skills/saga-bearings/assets/board-template.html +731 -0
- package/.agents/skills/saga-bearings/assets/recap.md +56 -0
- package/.agents/skills/syn-recovery/README.md +3 -0
- package/.agents/skills/syn-recovery/SKILL.md +54 -0
- package/.agents/skills/tyr-check/README.md +3 -0
- package/.agents/skills/tyr-check/SKILL.md +218 -0
- package/.agents/skills/urdh-hold/README.md +3 -0
- package/.agents/skills/urdh-hold/SKILL.md +25 -0
- package/.agents/skills/urdh-hold/assets/decisions.md +15 -0
- package/.agents/skills/urdh-hold/assets/hold.md +54 -0
- package/.agents/skills/vor-diagnostics/README.md +3 -0
- package/.agents/skills/vor-diagnostics/SKILL.md +68 -0
- package/.agents/skills/ymir-host/README.md +3 -0
- package/.agents/skills/ymir-host/SKILL.md +34 -0
- package/.agents/skills/ymir-host/assets/desktop.md +36 -0
- package/.agents/skills/ymir-host/assets/omarchy.md +297 -0
- package/.agents/skills/ymir-host/assets/runbooks.md +15 -0
- package/.agents/skills/ymir-host/assets/thjazi.md +220 -0
- package/.agents/skills/ymir-host/assets/update.md +64 -0
- package/.agents/tests/assets/board-render-harness.mjs +117 -0
- package/.agents/tests/cmux-test-safety.sh +51 -0
- package/.agents/tests/fixtures.sh +291 -0
- package/.agents/tests/fm-afk-inject-e2e.test.sh +428 -0
- package/.agents/tests/fm-afk-inject-herdr-e2e.test.sh +536 -0
- package/.agents/tests/fm-afk-launch.test.sh +961 -0
- package/.agents/tests/fm-afk-pi-herdr-return-e2e.test.sh +297 -0
- package/.agents/tests/fm-afk-return.test.sh +283 -0
- package/.agents/tests/fm-arm-pretool-check.test.sh +482 -0
- package/.agents/tests/fm-ask-user-authority.test.sh +42 -0
- package/.agents/tests/fm-backend-autodetect-smoke.test.sh +176 -0
- package/.agents/tests/fm-backend-cmux-smoke.test.sh +188 -0
- package/.agents/tests/fm-backend-cmux.test.sh +1166 -0
- package/.agents/tests/fm-backend-herdr-eventwait-smoke.test.sh +136 -0
- package/.agents/tests/fm-backend-herdr-eventwait.test.py +106 -0
- package/.agents/tests/fm-backend-herdr-focus-flash-e2e.test.sh +409 -0
- package/.agents/tests/fm-backend-herdr-launcher-workspace-e2e.test.sh +437 -0
- package/.agents/tests/fm-backend-herdr-presentation-e2e.test.sh +1424 -0
- package/.agents/tests/fm-backend-herdr-prune-safety-e2e.test.sh +180 -0
- package/.agents/tests/fm-backend-herdr-respawn-idem-e2e.test.sh +182 -0
- package/.agents/tests/fm-backend-herdr-smoke.test.sh +355 -0
- package/.agents/tests/fm-backend-herdr-workspace-per-home-e2e.test.sh +251 -0
- package/.agents/tests/fm-backend-herdr.test.sh +4606 -0
- package/.agents/tests/fm-backend-orca.test.sh +1357 -0
- package/.agents/tests/fm-backend-tmux-smoke.test.sh +173 -0
- package/.agents/tests/fm-backend-zellij-smoke.test.sh +205 -0
- package/.agents/tests/fm-backend-zellij.test.sh +1360 -0
- package/.agents/tests/fm-backend.test.sh +1142 -0
- package/.agents/tests/fm-backlog-atomicity.test.sh +2302 -0
- package/.agents/tests/fm-backlog-handoff.test.sh +1360 -0
- package/.agents/tests/fm-bearings-board-render.test.sh +135 -0
- package/.agents/tests/fm-bearings-board.test.sh +414 -0
- package/.agents/tests/fm-bearings-snapshot.test.sh +1990 -0
- package/.agents/tests/fm-bootstrap-network-parallel.test.sh +316 -0
- package/.agents/tests/fm-bootstrap.test.sh +1178 -0
- package/.agents/tests/fm-branch-supervision.test.sh +552 -0
- package/.agents/tests/fm-brief.test.sh +785 -0
- package/.agents/tests/fm-busy-adapter-wiring.test.sh +324 -0
- package/.agents/tests/fm-busy-state.test.sh +465 -0
- package/.agents/tests/fm-calm-pi-extension.test.sh +3979 -0
- package/.agents/tests/fm-captain-hold-lifecycle.test.sh +1187 -0
- package/.agents/tests/fm-cd-pretool-check.test.sh +400 -0
- package/.agents/tests/fm-check-unregister.test.sh +198 -0
- package/.agents/tests/fm-classify-corr-token.test.sh +543 -0
- package/.agents/tests/fm-classify-decision-key.test.sh +340 -0
- package/.agents/tests/fm-claude-stop-autoarm-live-e2e.test.sh +164 -0
- package/.agents/tests/fm-claude-stop-autoarm.test.sh +1190 -0
- package/.agents/tests/fm-cmux-claude-composer-live-e2e.test.sh +110 -0
- package/.agents/tests/fm-codex-continuity-live-e2e.test.sh +55 -0
- package/.agents/tests/fm-composer-ghost.test.sh +716 -0
- package/.agents/tests/fm-composer-lib.test.sh +666 -0
- package/.agents/tests/fm-composer-matrix-live-e2e.test.sh +220 -0
- package/.agents/tests/fm-control-herdr-smoke.test.sh +149 -0
- package/.agents/tests/fm-control-relaunch.test.sh +1530 -0
- package/.agents/tests/fm-control.test.sh +910 -0
- package/.agents/tests/fm-crew-state.test.sh +1610 -0
- package/.agents/tests/fm-cursor-harness.test.sh +404 -0
- package/.agents/tests/fm-cursor-primary-live-e2e.test.sh +217 -0
- package/.agents/tests/fm-cursor-primary.test.sh +708 -0
- package/.agents/tests/fm-daemon.test.sh +2740 -0
- package/.agents/tests/fm-documentation-audiences.test.sh +141 -0
- package/.agents/tests/fm-ensure-agents-md.test.sh +371 -0
- package/.agents/tests/fm-extension-binding.test.sh +2187 -0
- package/.agents/tests/fm-fleet-snapshot-view.test.sh +816 -0
- package/.agents/tests/fm-fleet-sync.test.sh +721 -0
- package/.agents/tests/fm-gate-refuse.test.sh +356 -0
- package/.agents/tests/fm-gitignore-config.test.sh +89 -0
- package/.agents/tests/fm-gotmp.test.sh +230 -0
- package/.agents/tests/fm-grok-continuity-live-e2e.test.sh +112 -0
- package/.agents/tests/fm-grok-harness.test.sh +117 -0
- package/.agents/tests/fm-grok-stop-live-e2e.test.sh +219 -0
- package/.agents/tests/fm-guard-stale-banner.test.sh +709 -0
- package/.agents/tests/fm-harness-adapter-instructions-live-e2e.test.sh +134 -0
- package/.agents/tests/fm-harness-adapter-references.test.sh +30 -0
- package/.agents/tests/fm-harness-liveness-drift-live-e2e.test.sh +151 -0
- package/.agents/tests/fm-herdr-lab.test.sh +243 -0
- package/.agents/tests/fm-herdr-session-cleanup-e2e.test.sh +146 -0
- package/.agents/tests/fm-herdr-session-cleanup.test.sh +329 -0
- package/.agents/tests/fm-herdr-submit-confirm-live-e2e.test.sh +125 -0
- package/.agents/tests/fm-herdr-version-floor-live-e2e.test.sh +144 -0
- package/.agents/tests/fm-home-summary-refresh.test.sh +971 -0
- package/.agents/tests/fm-inactive-reconcile.test.sh +485 -0
- package/.agents/tests/fm-kimi-harness.test.sh +687 -0
- package/.agents/tests/fm-lint-workflows.test.sh +532 -0
- package/.agents/tests/fm-lint.test.sh +1023 -0
- package/.agents/tests/fm-muse-harness.test.sh +932 -0
- package/.agents/tests/fm-muse-signals-live-e2e.test.sh +205 -0
- package/.agents/tests/fm-no-mistakes-required.test.sh +72 -0
- package/.agents/tests/fm-on.test.sh +507 -0
- package/.agents/tests/fm-opencode-primary-live-e2e.test.sh +357 -0
- package/.agents/tests/fm-operational-input.test.sh +160 -0
- package/.agents/tests/fm-peek-remote.test.sh +110 -0
- package/.agents/tests/fm-pending-reply.test.sh +1288 -0
- package/.agents/tests/fm-pi-branch-extension.test.sh +3182 -0
- package/.agents/tests/fm-pi-branch-live-e2e.test.sh +527 -0
- package/.agents/tests/fm-pi-primary-live-e2e.test.sh +345 -0
- package/.agents/tests/fm-pi-primary-types.test.sh +68 -0
- package/.agents/tests/fm-pi-watch-extension.test.sh +2843 -0
- package/.agents/tests/fm-pr-check-security.test.sh +2140 -0
- package/.agents/tests/fm-pr-merge.test.sh +2135 -0
- package/.agents/tests/fm-procevent-quota.test.sh +225 -0
- package/.agents/tests/fm-procevent-when.test.sh +406 -0
- package/.agents/tests/fm-procevent.test.sh +1745 -0
- package/.agents/tests/fm-project-origin.test.sh +110 -0
- package/.agents/tests/fm-public-followup.test.sh +2272 -0
- package/.agents/tests/fm-quota-array-dispatch-live-e2e.test.sh +526 -0
- package/.agents/tests/fm-quota-choose.test.sh +626 -0
- package/.agents/tests/fm-remote-backlog-handoff.test.sh +453 -0
- package/.agents/tests/fm-remote-doctor.test.sh +625 -0
- package/.agents/tests/fm-remote-entrypoint.test.sh +59 -0
- package/.agents/tests/fm-remote-job-orphan-reap.test.sh +205 -0
- package/.agents/tests/fm-remote-job.test.sh +768 -0
- package/.agents/tests/fm-remote-reply.test.sh +520 -0
- package/.agents/tests/fm-remote-secondmate-lifecycle-e2e.test.sh +1256 -0
- package/.agents/tests/fm-remote-secondmate-parent-binding.test.sh +326 -0
- package/.agents/tests/fm-remote-secondmate-trace-context.test.sh +310 -0
- package/.agents/tests/fm-remote-transport-lanes.test.sh +431 -0
- package/.agents/tests/fm-review-diff.test.sh +176 -0
- package/.agents/tests/fm-secondmate-harness.test.sh +2600 -0
- package/.agents/tests/fm-secondmate-lifecycle-e2e.test.sh +259 -0
- package/.agents/tests/fm-secondmate-liveness.test.sh +559 -0
- package/.agents/tests/fm-secondmate-reconcile.test.sh +732 -0
- package/.agents/tests/fm-secondmate-safety.test.sh +2972 -0
- package/.agents/tests/fm-secondmate-sync.test.sh +913 -0
- package/.agents/tests/fm-send-inbox-doorbell-live-e2e.test.sh +206 -0
- package/.agents/tests/fm-send-inbox.test.sh +352 -0
- package/.agents/tests/fm-send-popup-settle.test.sh +167 -0
- package/.agents/tests/fm-send-remote-delivery.test.sh +796 -0
- package/.agents/tests/fm-send-resolve-key.test.sh +551 -0
- package/.agents/tests/fm-send-secondmate-marker-herdr-e2e.test.sh +187 -0
- package/.agents/tests/fm-send-secondmate-marker.test.sh +278 -0
- package/.agents/tests/fm-send-settle.test.sh +147 -0
- package/.agents/tests/fm-send-strict.test.sh +241 -0
- package/.agents/tests/fm-session-lock-ancestry.test.sh +365 -0
- package/.agents/tests/fm-session-start.test.sh +2515 -0
- package/.agents/tests/fm-sessionstart-hook-live-e2e.test.sh +631 -0
- package/.agents/tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh +230 -0
- package/.agents/tests/fm-sessionstart-nudge.test.sh +1039 -0
- package/.agents/tests/fm-shared-captain-inheritance.test.sh +404 -0
- package/.agents/tests/fm-spawn-batch.test.sh +148 -0
- package/.agents/tests/fm-spawn-dispatch-profile.test.sh +827 -0
- package/.agents/tests/fm-spawn-pool-base-freshen.test.sh +440 -0
- package/.agents/tests/fm-spawn-worktree-settle.test.sh +147 -0
- package/.agents/tests/fm-startup-memory-budget.test.sh +333 -0
- package/.agents/tests/fm-startup-network.test.sh +712 -0
- package/.agents/tests/fm-stow-cascade.test.sh +370 -0
- package/.agents/tests/fm-subagent-pretool-check.test.sh +291 -0
- package/.agents/tests/fm-supervision-events.test.sh +156 -0
- package/.agents/tests/fm-supervision-instructions.test.sh +189 -0
- package/.agents/tests/fm-tangle-guard.test.sh +277 -0
- package/.agents/tests/fm-task-delivery.test.sh +420 -0
- package/.agents/tests/fm-task-inbox.test.sh +513 -0
- package/.agents/tests/fm-teardown-endpoint-safety.test.sh +374 -0
- package/.agents/tests/fm-teardown.test.sh +2665 -0
- package/.agents/tests/fm-test-fixture-cleanup.test.sh +172 -0
- package/.agents/tests/fm-test-fixtures.test.sh +132 -0
- package/.agents/tests/fm-test-isolation-proof.test.sh +278 -0
- package/.agents/tests/fm-test-run.test.sh +1196 -0
- package/.agents/tests/fm-tmux-agent-liveness.test.sh +354 -0
- package/.agents/tests/fm-tmux-submit-busy.test.sh +340 -0
- package/.agents/tests/fm-tool-update-check.test.sh +1044 -0
- package/.agents/tests/fm-trace-context-lib.test.sh +253 -0
- package/.agents/tests/fm-trace-context-spawn.test.sh +581 -0
- package/.agents/tests/fm-transition-lib.test.sh +54 -0
- package/.agents/tests/fm-turnend-guard.test.sh +1822 -0
- package/.agents/tests/fm-update.test.sh +304 -0
- package/.agents/tests/fm-vendor-auth-probe.test.sh +395 -0
- package/.agents/tests/fm-voice-relay.test.sh +4741 -0
- package/.agents/tests/fm-wake-daemon-lifecycle-e2e.test.sh +171 -0
- package/.agents/tests/fm-wake-drain-open-decisions-cursor.test.sh +356 -0
- package/.agents/tests/fm-wake-drain-open-decisions.test.sh +226 -0
- package/.agents/tests/fm-wake-drain-unread-status.test.sh +379 -0
- package/.agents/tests/fm-wake-queue.test.sh +1224 -0
- package/.agents/tests/fm-watch-arm.test.sh +821 -0
- package/.agents/tests/fm-watch-checkpoint.test.sh +87 -0
- package/.agents/tests/fm-watch-recovery-loop.test.sh +223 -0
- package/.agents/tests/fm-watch-triage.test.sh +3889 -0
- package/.agents/tests/fm-watcher-lock.test.sh +1133 -0
- package/.agents/tests/fm-x-mode.test.sh +3042 -0
- package/.agents/tests/gleipnir-guard-pretool-check.test.sh +51 -0
- package/.agents/tests/gleipnir-machine-lock.test.sh +130 -0
- package/.agents/tests/herdr-test-safety.sh +42 -0
- package/.agents/tests/lib.sh +315 -0
- package/.agents/tests/remote-herdr-fixture.sh +125 -0
- package/.agents/tests/secondmate-helpers.sh +193 -0
- package/.agents/tests/smoke.test.sh +93 -0
- package/.agents/tests/wake-helpers.sh +338 -0
- package/.agents/tests/zellij-test-safety.sh +60 -0
- package/.agents/tools/README.md +10 -0
- package/.agents/tools/bin/tasks-cli +267 -0
- package/.agents/tools/tasks-cli.ts +216 -0
- package/AGENTS.md +454 -0
- package/CHANGELOG.md +881 -0
- package/LICENSE +1 -1
- package/NOTICE +63 -0
- package/README.md +113 -39
- package/RULES/01-domains.md +51 -0
- package/RULES/02-agents.md +78 -0
- package/RULES/03-houses.md +61 -0
- package/RULES/04-hoard.md +65 -0
- package/RULES/05-platforms.md +67 -0
- package/RULES/06-append-only.md +58 -0
- package/RULES/07-config.md +45 -0
- package/RULES/08-delivery-gate.md +75 -0
- package/RULES/README.md +20 -0
- package/bin/README.md +107 -0
- package/bin/a2a-mcp.sh +98 -0
- package/bin/a2a-serve.py +103 -0
- package/bin/a2a-serve.sh +8 -0
- package/bin/a2a-talk.sh +50 -0
- package/bin/a2abridge-ensure.sh +108 -0
- package/bin/agent-run.sh +43 -0
- package/bin/agents-config.sh +233 -0
- package/bin/apodex-smoke-test.sh +92 -0
- package/bin/bifrost-bridge.sh +112 -0
- package/bin/bifrost-ingress.sh +77 -0
- package/bin/bootstrap-macos.sh +107 -0
- package/bin/bootstrap-windows.ps1 +112 -0
- package/bin/branch-guard.sh +64 -0
- package/bin/brokk +73 -0
- package/bin/brokk-classify-lib.sh +1770 -0
- package/bin/brokk-lease-lib.sh +222 -0
- package/bin/brokk-lease.sh +193 -0
- package/bin/brokk-lint.sh +65 -0
- package/bin/brokk-send.sh +38 -0
- package/bin/brokk-update.sh +9 -0
- package/bin/brokk-wake-grant.sh +127 -0
- package/bin/brokk-wake-lib.sh +1849 -0
- package/bin/changelog-guard.sh +165 -0
- package/bin/crash-sense.sh +160 -0
- package/bin/desktop-place.sh +224 -0
- package/bin/docs-guard.sh +39 -0
- package/bin/editor-place.sh +207 -0
- package/bin/eindri-acclaim.sh +34 -0
- package/bin/eindri-control.sh +28 -0
- package/bin/eindri-role.sh +83 -0
- package/bin/eindri-seen.sh +41 -0
- package/bin/eindri-send.sh +15 -0
- package/bin/eindri-start.sh +85 -0
- package/bin/eindri-watch.sh +57 -0
- package/bin/einherjar-spawn.sh +576 -0
- package/bin/eir-doctor.sh +112 -0
- package/bin/erindi-brief.sh +271 -0
- package/bin/fleet-apply.sh +68 -0
- package/bin/github-deploy.sh +81 -0
- package/bin/gjallarhorn-expose.sh +102 -0
- package/bin/gjallarhorn-notify.sh +58 -0
- package/bin/gjallarhorn-purge.sh +54 -0
- package/bin/gjallarhorn-tunnel.sh +72 -0
- package/bin/gleipnir-lock-lib.sh +284 -0
- package/bin/groa-update.sh +122 -0
- package/bin/hall-snapshot.sh +92 -0
- package/bin/hamr-harness.sh +246 -0
- package/bin/heimdall-ssh-keys.sh +374 -0
- package/bin/herdr-agents.py +44 -0
- package/bin/herdr-ensure.sh +104 -0
- package/bin/herdr-run.sh +431 -0
- package/bin/hermes-ensure.sh +88 -0
- package/bin/hoard-lib.sh +25 -0
- package/bin/hodd.sh +78 -0
- package/bin/host-sense.sh +199 -0
- package/bin/huginn-research-worker.sh +108 -0
- package/bin/local-model-lock.sh +51 -0
- package/bin/mimir-bridge.py +226 -0
- package/bin/mimir-bridge.sh +87 -0
- package/bin/mimir-ingest.sh +131 -0
- package/bin/mimir.sh +113 -0
- package/bin/mjollnir-webhook.sh +59 -0
- package/bin/mjollnir.sh +85 -0
- package/bin/model-hardware.sh +82 -0
- package/bin/model-resolve.sh +147 -0
- package/bin/models-detect.sh +178 -0
- package/bin/nornir-cron-start.sh +162 -0
- package/bin/nornir-job-daily-briefing.sh +191 -0
- package/bin/nornir-job-git-sync.sh +152 -0
- package/bin/nornir-job-memory-housekeeping.sh +114 -0
- package/bin/nornir-job-observer.sh +166 -0
- package/bin/nornir-job-skillopt-sleep.sh +76 -0
- package/bin/omarchy-hook-install.sh +60 -0
- package/bin/omarchy-install.sh +148 -0
- package/bin/omarchy-plugins.sh +174 -0
- package/bin/omarchy-sense.sh +149 -0
- package/bin/perm-guard.sh +30 -0
- package/bin/pi-agent.sh +68 -0
- package/bin/pi-ensure.sh +77 -0
- package/bin/pi-local.sh +37 -0
- package/bin/pi-seat.sh +63 -0
- package/bin/prereq-ensure.sh +166 -0
- package/bin/private-guard.sh +38 -0
- package/bin/project-git.sh +55 -0
- package/bin/public-guard.sh +70 -0
- package/bin/ratatoskr.sh +49 -0
- package/bin/realm-lib.sh +32 -0
- package/bin/repo-scrub.sh +62 -0
- package/bin/rodd-operational-input.sh +250 -0
- package/bin/runes-append.sh +198 -0
- package/bin/saga-session-start.sh +172 -0
- package/bin/saga-sessionstart-run.sh +79 -0
- package/bin/saga-wake-drain.sh +41 -0
- package/bin/secret-guard.sh +63 -0
- package/bin/sessrumnir-ensure.sh +100 -0
- package/bin/sessrumnir-sync.sh +237 -0
- package/bin/sessrumnir.sh +91 -0
- package/bin/skillopt-setup.sh +76 -0
- package/bin/skuld-branch-outcome.sh +203 -0
- package/bin/skuld-branch-prompt.sh +101 -0
- package/bin/smidja-bootstrap.sh +90 -0
- package/bin/smidja-observe.sh +86 -0
- package/bin/syn-arm-pretool-check.sh +57 -0
- package/bin/syn-asset-pretool-check.sh +76 -0
- package/bin/syn-cd-pretool-check.sh +28 -0
- package/bin/syn-guard-pretool-check.sh +83 -0
- package/bin/syn-turnend-guard.sh +36 -0
- package/bin/syn-watch-arm.sh +99 -0
- package/bin/tailscale-sync.sh +190 -0
- package/bin/telegram-bot.sh +69 -0
- package/bin/toolchain.sh +74 -0
- package/bin/utgard.sh +111 -0
- package/bin/valhalla.sh +115 -0
- package/bin/valknut-load.sh +260 -0
- package/bin/vor-crew-state.sh +124 -0
- package/bin/wedge-notify.sh +47 -0
- package/bin/workspace-provision.sh +80 -0
- package/bin/workspace-rag.sh +150 -0
- package/bin/wyrd-db.sh +47 -0
- package/bin/yggdrasil.sh +152 -0
- package/bin/ymir-edit.sh +145 -0
- package/bin/ymir-install.sh +641 -0
- package/bin/ymir-invite.sh +93 -0
- package/bin/ymir-isolation.sh +88 -0
- package/bin/ymir-migrate.sh +61 -0
- package/bin/ymir-platform.sh +348 -0
- package/bin/ymir-say.sh +97 -0
- package/bin/ymir-setup-auth.sh +108 -0
- package/bin/ymir-validate.sh +173 -0
- package/bin/ymir.js +74 -16
- package/docs/Architecture.md +339 -0
- package/docs/design.md +351 -0
- package/docs/installations/README.md +63 -0
- package/docs/installations/ymir-home.md +93 -0
- package/docs/integration.md +105 -0
- package/docs/lore.md +1056 -0
- package/docs/research/agent-harnesses.md +290 -0
- package/docs/runbooks/README.md +19 -0
- package/docs/runbooks/a2a.md +91 -0
- package/docs/runbooks/agent-permissions.md +99 -0
- package/docs/runbooks/agents.md +57 -0
- package/docs/runbooks/models.md +69 -0
- package/docs/runbooks/realm-onboarding.md +60 -0
- package/docs/runbooks/secrets-and-hoard.md +60 -0
- package/docs/runbooks/updates-and-migrations.md +110 -0
- package/docs/runbooks/worktrees-isolation.md +72 -0
- package/docs/session-start.md +105 -0
- package/docs/skillopt-integration.md +321 -0
- package/docs/sync-system.md +66 -0
- package/docs/workspaces.md +104 -0
- package/docs/ymir-rut.md +260 -0
- package/package.json +44 -14
- package/packaging/build.sh +74 -0
- package/packaging/macos/Ymir Installer.command +43 -0
- package/packaging/windows/ymir-setup.nsi +74 -0
- package/scripts/README.md +22 -0
- package/scripts/electron.sh +210 -0
- package/scripts/start.sh +167 -0
- package/scripts/stop.sh +113 -0
- package/install.sh +0 -35
|
@@ -0,0 +1,732 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tests/fm-secondmate-reconcile.test.sh - the cooldown-limited reconcile ask.
|
|
3
|
+
#
|
|
4
|
+
# A backlog-vs-metadata inventory mismatch inside a secondmate home no longer
|
|
5
|
+
# blanks that home in the fleet snapshot, so the parent asks the home that owns
|
|
6
|
+
# those books to fix them. This suite pins that ask: it lands as a real durable
|
|
7
|
+
# steering record, a home is asked at most once per cooldown window however
|
|
8
|
+
# often the snapshot runs, a mismatch still sitting there after the window
|
|
9
|
+
# earns one gentle re-nudge, and the parent never touches the mate's own files.
|
|
10
|
+
set -u
|
|
11
|
+
|
|
12
|
+
# shellcheck source=tests/secondmate-helpers.sh disable=SC1091
|
|
13
|
+
. "$(dirname "${BASH_SOURCE[0]}")/secondmate-helpers.sh"
|
|
14
|
+
|
|
15
|
+
RECONCILE="$ROOT/bin/fm-secondmate-reconcile.sh"
|
|
16
|
+
TMP_ROOT=$(fm_test_tmproot fm-secondmate-reconcile)
|
|
17
|
+
|
|
18
|
+
command -v jq >/dev/null 2>&1 || { echo "skip: jq not found"; exit 0; }
|
|
19
|
+
|
|
20
|
+
export FM_SEND_SETTLE=0 FM_SEND_SLEEP=0 FM_SEND_RETRIES=1
|
|
21
|
+
|
|
22
|
+
# A main home with one registered, live, local secondmate reachable through the
|
|
23
|
+
# fake tmux backend, so fm-send's real inbox plane is exercised end to end.
|
|
24
|
+
make_main_home() { # <name> <mate-id>
|
|
25
|
+
local home="$TMP_ROOT/$1" mate="$TMP_ROOT/$1-mate" id=$2 abs fakebin
|
|
26
|
+
mkdir -p "$home/data" "$home/state"
|
|
27
|
+
seed_secondmate_home_marker "$mate" "$id"
|
|
28
|
+
abs=$(cd "$mate" && pwd -P)
|
|
29
|
+
printf -- '- %s - fixture domain (home: %s; scope: fixture; projects: sample; added 2026-08-26)\n' \
|
|
30
|
+
"$id" "$abs" > "$home/data/secondmates.md"
|
|
31
|
+
cat > "$home/state/$id.meta" <<META
|
|
32
|
+
window=firstmate:fm-$id
|
|
33
|
+
kind=secondmate
|
|
34
|
+
harness=claude
|
|
35
|
+
backend=tmux
|
|
36
|
+
spawn_gen=spawn-$id
|
|
37
|
+
home=$abs
|
|
38
|
+
worktree=$abs
|
|
39
|
+
META
|
|
40
|
+
fakebin=$(make_fake_tmux "$TMP_ROOT/$1-fake")
|
|
41
|
+
printf '%s\n' "$home" "$mate" "$fakebin"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# A minimal but schema-true fleet snapshot carrying one structured-home record.
|
|
45
|
+
write_snapshot() { # <path> <mate-id> <invalidity-json> [state]
|
|
46
|
+
jq -n --arg id "$2" --argjson inv "$3" --arg state "${4:-captain_decision}" '{
|
|
47
|
+
schema:"fm-fleet-snapshot.v1", generated:"2026-08-26T00:00:00Z",
|
|
48
|
+
secondmate_current:{records:[{
|
|
49
|
+
id:$id, home:("/tmp/" + $id), spawn_gen:("spawn-" + $id),
|
|
50
|
+
current:{state:$state, reason:null},
|
|
51
|
+
invalidity:$inv, reconcile_inventory:($inv // {kind:null,ids:[]}),
|
|
52
|
+
provenance:{selected:"structured-home", trust:"partial-structured"}}]}}' > "$1"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# The same shape, but for a persistent REMOTE secondmate: no spawn_gen (its
|
|
56
|
+
# parent metadata never carries one), host instead.
|
|
57
|
+
write_remote_snapshot() { # <path> <mate-id> <host> <invalidity-json> [state]
|
|
58
|
+
jq -n --arg id "$2" --arg host "$3" --argjson inv "$4" --arg state "${5:-captain_decision}" '{
|
|
59
|
+
schema:"fm-fleet-snapshot.v1", generated:"2026-08-26T00:00:00Z",
|
|
60
|
+
secondmate_current:{records:[{
|
|
61
|
+
id:$id, home:("/tmp/" + $id), host:$host, spawn_gen:null,
|
|
62
|
+
current:{state:$state, reason:null},
|
|
63
|
+
invalidity:$inv, reconcile_inventory:($inv // {kind:null,ids:[]}),
|
|
64
|
+
provenance:{selected:"structured-home", trust:"partial-structured"}}]}}' > "$1"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# A fake ssh that decodes fm-on.sh's base64 remote-home/argv payload and
|
|
68
|
+
# EXECUTES the real host-local leg (fm-remote-secondmate-control.sh) against a
|
|
69
|
+
# genuinely seeded remote-home fixture, exactly like the ssh stub proven in
|
|
70
|
+
# tests/fm-send-remote-delivery.test.sh.
|
|
71
|
+
make_remote_ssh_stub() { # <dir> -> echoes fakebin dir
|
|
72
|
+
local dir=$1 fb="$1/fakebin"
|
|
73
|
+
mkdir -p "$fb"
|
|
74
|
+
cat > "$fb/fake-ssh" <<'SH'
|
|
75
|
+
#!/usr/bin/env bash
|
|
76
|
+
set -u
|
|
77
|
+
cat > /dev/null
|
|
78
|
+
while [ "$#" -gt 0 ]; do
|
|
79
|
+
case "$1" in -o) shift 2 ;; --) shift; break ;; *) exit 90 ;; esac
|
|
80
|
+
done
|
|
81
|
+
shift 2 # host, fm-remote-entrypoint.sh
|
|
82
|
+
home_b64=$3
|
|
83
|
+
argv_b64=$4
|
|
84
|
+
remote_home=$(perl -MMIME::Base64=decode_base64 -e 'print decode_base64($ARGV[0])' "$home_b64")
|
|
85
|
+
rargs=()
|
|
86
|
+
while IFS= read -r -d '' a; do rargs+=("$a"); done \
|
|
87
|
+
< <(perl -MMIME::Base64=decode_base64 -e 'print decode_base64($ARGV[0])' "$argv_b64")
|
|
88
|
+
cmd=${rargs[0]}
|
|
89
|
+
rc=0
|
|
90
|
+
env FM_HOME="$remote_home" FM_ROOT_OVERRIDE="$FM_REMOTE_CODE_ROOT" \
|
|
91
|
+
"$FM_REMOTE_CODE_ROOT/bin/$cmd" "${rargs[@]:1}" || rc=$?
|
|
92
|
+
exit "$rc"
|
|
93
|
+
SH
|
|
94
|
+
chmod +x "$fb/fake-ssh"
|
|
95
|
+
printf '%s\n' "$fb"
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# A seeded remote secondmate home the real host-local leg validates and writes
|
|
99
|
+
# into (identity marker, Firstmate-checkout shape, parent-route endpoint meta).
|
|
100
|
+
make_remote_secondmate_home() { # <name> -> echoes remote home dir
|
|
101
|
+
local rh="$TMP_ROOT/$1-rhome"
|
|
102
|
+
mkdir -p "$rh/state/parent-route" "$rh/bin"
|
|
103
|
+
printf '%s\n' "$1" > "$rh/.fm-secondmate-home"
|
|
104
|
+
printf '# remote secondmate home fixture\n' > "$rh/AGENTS.md"
|
|
105
|
+
cat > "$rh/state/parent-route/$1.meta" <<META
|
|
106
|
+
window=fm-remote:p1
|
|
107
|
+
worktree=-
|
|
108
|
+
project=-
|
|
109
|
+
backend=herdr
|
|
110
|
+
endpoint_task_id=$1
|
|
111
|
+
harness=claude
|
|
112
|
+
herdr_session=fm-remote
|
|
113
|
+
herdr_workspace_id=w1
|
|
114
|
+
herdr_tab_id=t1
|
|
115
|
+
herdr_pane_id=p1
|
|
116
|
+
META
|
|
117
|
+
printf '%s\n' "$rh"
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# A parent home whose secondmate is a persistent REMOTE route (as
|
|
121
|
+
# spawn_remote_secondmate() writes it: no spawn_gen, remote_host instead).
|
|
122
|
+
make_remote_parent_home() { # <name> <mate-id> <remote-home> <host> -> echoes home dir
|
|
123
|
+
local home="$TMP_ROOT/$1" id=$2 rhome host=$4
|
|
124
|
+
mkdir -p "$home/data" "$home/state"
|
|
125
|
+
# Canonicalize: fm-on.sh's registry route parser rejects an empty path
|
|
126
|
+
# component, and $TMP_ROOT can carry one (a raw mktemp base under a
|
|
127
|
+
# trailing-slash TMPDIR), same as make_main_home's $abs above.
|
|
128
|
+
rhome=$(cd "$3" && pwd -P)
|
|
129
|
+
cat > "$home/state/$id.meta" <<META
|
|
130
|
+
window=remote:$id
|
|
131
|
+
endpoint_task_id=$id
|
|
132
|
+
harness=claude
|
|
133
|
+
kind=secondmate
|
|
134
|
+
mode=secondmate
|
|
135
|
+
yolo=off
|
|
136
|
+
remote_host=$host
|
|
137
|
+
remote_root=/remote/root
|
|
138
|
+
remote_backend=herdr
|
|
139
|
+
remote_herdr_session=fm-remote
|
|
140
|
+
remote_target=fm-remote:p1
|
|
141
|
+
META
|
|
142
|
+
cat > "$home/data/secondmates.md" <<EOF
|
|
143
|
+
- $id - remote fixture domain (host: $host; root: /remote/root; home: $rhome; scope: remote fixture; projects: sample; added 2026-08-26)
|
|
144
|
+
EOF
|
|
145
|
+
printf '%s\n' "$home"
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
remote_inbox_records() { # <remote-home> <mate-id>
|
|
149
|
+
find "$1/state/parent-route/$2.inbox" -maxdepth 1 -type f -name '*.msg' 2>/dev/null
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
run_remote_notify() { # <home> <fakebin> <snapshot>
|
|
153
|
+
local home=$1 fakebin=$2 snap=$3
|
|
154
|
+
FM_SSH_BIN="$fakebin/fake-ssh" FM_REMOTE_CODE_ROOT="$ROOT" \
|
|
155
|
+
PATH="$fakebin:$PATH" FM_HOME="$home" FM_ROOT_OVERRIDE="$ROOT" \
|
|
156
|
+
FM_STATE_OVERRIDE="$home/state" \
|
|
157
|
+
"$RECONCILE" notify --snapshot "$snap"
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
# Age the home's cooldown record so the next run sees the window as elapsed.
|
|
161
|
+
age_cooldown() { # <state-dir> <mate-id> <seconds-ago>
|
|
162
|
+
printf '%s\n' "$(( $(date +%s) - $3 ))" > "$1/$2.reconcile-nudged"
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
run_notify() { # <home> <fakebin> <name> <snapshot> [extra args...]
|
|
166
|
+
local home=$1 fakebin=$2 name=$3 snap=$4
|
|
167
|
+
shift 4
|
|
168
|
+
PATH="$fakebin:$PATH" FM_HOME="$home" FM_ROOT_OVERRIDE="$ROOT" \
|
|
169
|
+
FM_STATE_OVERRIDE="$home/state" \
|
|
170
|
+
FM_FAKE_TMUX_WINDOW="firstmate:fm-mate" \
|
|
171
|
+
FM_FAKE_TMUX_LOG="$TMP_ROOT/$name-tmux.log" \
|
|
172
|
+
FM_FAKE_TMUX_CAPTURE="$TMP_ROOT/$name-fake/pane.txt" \
|
|
173
|
+
"$RECONCILE" notify --snapshot "$snap" "$@"
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
inbox_records() { # <state-dir> <task-id>
|
|
177
|
+
find "$1/$2.inbox" -maxdepth 1 -type f -name '*.msg' 2>/dev/null | wc -l | tr -d '[:space:]'
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# Content-and-name fingerprint of a whole home, so any parent-side write shows up.
|
|
181
|
+
fingerprint_tree() { # <dir>
|
|
182
|
+
find "$1" -type f -print0 2>/dev/null | LC_ALL=C sort -z \
|
|
183
|
+
| while IFS= read -r -d '' f; do printf '%s %s\n' "${f#"$1"}" "$(cksum < "$f")"; done
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
inbox_text() { # <state-dir> <task-id>
|
|
187
|
+
local rec
|
|
188
|
+
for rec in "$1/$2.inbox"/*.msg; do
|
|
189
|
+
[ -f "$rec" ] || continue
|
|
190
|
+
bash -c '. "$1"; fm_task_inbox_body "$2"' _ "$ROOT/bin/fm-task-inbox-lib.sh" "$rec"
|
|
191
|
+
done
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
hold_lock_until_released() { # <lock> <ready> <release>
|
|
195
|
+
bash -c '
|
|
196
|
+
. "$1"
|
|
197
|
+
fm_lock_acquire_wait "$2"
|
|
198
|
+
: > "$3"
|
|
199
|
+
while [ ! -f "$4" ]; do sleep 0.01; done
|
|
200
|
+
fm_lock_release "$2"
|
|
201
|
+
' _ "$ROOT/bin/fm-wake-lib.sh" "$1" "$2" "$3" &
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
test_an_inventory_mismatch_asks_the_mate_once_per_window() {
|
|
206
|
+
local home mate fakebin snap out
|
|
207
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home once mate)
|
|
208
|
+
snap="$home/snapshot.json"
|
|
209
|
+
write_snapshot "$snap" mate '{"kind":"orphan_in_flight","ids":["stale-scout","watch-row"]}'
|
|
210
|
+
|
|
211
|
+
out=$(run_notify "$home" "$fakebin" once "$snap") || fail "the first reconcile ask failed: $out"
|
|
212
|
+
assert_contains "$out" "sent: mate orphan_in_flight" \
|
|
213
|
+
"the first ask did not report what it sent: $out"
|
|
214
|
+
[ "$(inbox_records "$home/state" mate)" -eq 1 ] \
|
|
215
|
+
|| fail "the ask did not land as exactly one durable steering record"
|
|
216
|
+
assert_contains "$(inbox_text "$home/state" mate)" "check your current books" \
|
|
217
|
+
"the instruction did not ask the mate to inspect its current state"
|
|
218
|
+
if printf '%s' "$(inbox_text "$home/state" mate)" | grep -Fq 'stale-scout'; then
|
|
219
|
+
fail "the instruction prescribed a repair from sampled details that can become stale"
|
|
220
|
+
fi
|
|
221
|
+
|
|
222
|
+
# Every later recap sees the same mismatch; none of them may nag.
|
|
223
|
+
out=$(run_notify "$home" "$fakebin" once "$snap") || fail "the repeat run failed: $out"
|
|
224
|
+
assert_contains "$out" "cooldown: mate" "a repeated snapshot did not report the cooldown: $out"
|
|
225
|
+
run_notify "$home" "$fakebin" once "$snap" >/dev/null
|
|
226
|
+
run_notify "$home" "$fakebin" once "$snap" >/dev/null
|
|
227
|
+
[ "$(inbox_records "$home/state" mate)" -eq 1 ] \
|
|
228
|
+
|| fail "repeated snapshots asked the mate more than once inside the cooldown"
|
|
229
|
+
pass "a home in mismatch is asked once, and later recaps stay silent"
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
test_a_mismatch_still_there_after_the_window_earns_one_more_nudge() {
|
|
233
|
+
local home mate fakebin snap out
|
|
234
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home window mate)
|
|
235
|
+
snap="$home/snapshot.json"
|
|
236
|
+
write_snapshot "$snap" mate '{"kind":"orphan_in_flight","ids":["ghost"]}'
|
|
237
|
+
run_notify "$home" "$fakebin" window "$snap" >/dev/null || fail "the first ask failed"
|
|
238
|
+
|
|
239
|
+
# Just inside four hours: still silent.
|
|
240
|
+
age_cooldown "$home/state" mate 14000
|
|
241
|
+
out=$(run_notify "$home" "$fakebin" window "$snap") || fail "the in-window run failed: $out"
|
|
242
|
+
assert_contains "$out" "cooldown: mate" "an ask inside the window was not suppressed: $out"
|
|
243
|
+
[ "$(inbox_records "$home/state" mate)" -eq 1 ] || fail "an in-window ask was sent anyway"
|
|
244
|
+
|
|
245
|
+
# Past four hours: exactly one gentle re-nudge, then silent again.
|
|
246
|
+
age_cooldown "$home/state" mate 14500
|
|
247
|
+
out=$(run_notify "$home" "$fakebin" window "$snap") || fail "the past-window run failed: $out"
|
|
248
|
+
assert_contains "$out" "sent: mate orphan_in_flight" \
|
|
249
|
+
"a mismatch outliving the window did not earn a re-nudge: $out"
|
|
250
|
+
[ "$(inbox_records "$home/state" mate)" -eq 2 ] \
|
|
251
|
+
|| fail "the re-nudge did not send exactly one more instruction"
|
|
252
|
+
run_notify "$home" "$fakebin" window "$snap" >/dev/null
|
|
253
|
+
[ "$(inbox_records "$home/state" mate)" -eq 2 ] \
|
|
254
|
+
|| fail "the re-nudge did not restart the cooldown"
|
|
255
|
+
pass "a mismatch outliving the cooldown earns one re-nudge, then goes quiet again"
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
test_the_cooldown_starts_when_delivery_finishes() {
|
|
259
|
+
local home mate fakebin snap started nudged
|
|
260
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home deliverytime mate)
|
|
261
|
+
snap="$home/snapshot.json"
|
|
262
|
+
write_snapshot "$snap" mate '{"kind":"orphan_in_flight","ids":["ghost"]}'
|
|
263
|
+
mv "$fakebin/tmux" "$fakebin/tmux-real"
|
|
264
|
+
cat > "$fakebin/tmux" <<'SH'
|
|
265
|
+
#!/usr/bin/env bash
|
|
266
|
+
set -u
|
|
267
|
+
if [ "${1:-}" = send-keys ]; then sleep 2; fi
|
|
268
|
+
exec "$(dirname "$0")/tmux-real" "$@"
|
|
269
|
+
SH
|
|
270
|
+
chmod +x "$fakebin/tmux"
|
|
271
|
+
|
|
272
|
+
started=$(date +%s)
|
|
273
|
+
run_notify "$home" "$fakebin" deliverytime "$snap" >/dev/null \
|
|
274
|
+
|| fail "the delayed reconcile ask failed"
|
|
275
|
+
nudged=$(cat "$home/state/mate.reconcile-nudged")
|
|
276
|
+
[ "$nudged" -ge "$((started + 2))" ] \
|
|
277
|
+
|| fail "the cooldown began before delivery finished: start=$started nudged=$nudged"
|
|
278
|
+
pass "the cooldown begins when delivery finishes"
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
test_the_window_is_four_hours() {
|
|
282
|
+
local home mate fakebin snap out now
|
|
283
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home fourhours mate)
|
|
284
|
+
snap="$home/snapshot.json"
|
|
285
|
+
write_snapshot "$snap" mate '{"kind":"terminal_in_flight","ids":["done-row"]}'
|
|
286
|
+
run_notify "$home" "$fakebin" fourhours "$snap" >/dev/null || fail "the first ask failed"
|
|
287
|
+
now=$(date +%s)
|
|
288
|
+
cat > "$fakebin/date" <<'SH'
|
|
289
|
+
#!/usr/bin/env bash
|
|
290
|
+
if [ -n "${FM_TEST_DATE_NOW:-}" ] && [ "${1:-}" = +%s ]; then
|
|
291
|
+
printf '%s\n' "$FM_TEST_DATE_NOW"
|
|
292
|
+
exit 0
|
|
293
|
+
fi
|
|
294
|
+
exec /bin/date "$@"
|
|
295
|
+
SH
|
|
296
|
+
chmod +x "$fakebin/date"
|
|
297
|
+
# One second short of four hours is still inside; one second past is not.
|
|
298
|
+
printf '%s\n' "$((now - 14399))" > "$home/state/mate.reconcile-nudged"
|
|
299
|
+
out=$(FM_TEST_DATE_NOW=$now run_notify "$home" "$fakebin" fourhours "$snap")
|
|
300
|
+
assert_contains "$out" "cooldown: mate" "the window was shorter than four hours: $out"
|
|
301
|
+
printf '%s\n' "$((now - 14401))" > "$home/state/mate.reconcile-nudged"
|
|
302
|
+
out=$(FM_TEST_DATE_NOW=$now run_notify "$home" "$fakebin" fourhours "$snap")
|
|
303
|
+
assert_contains "$out" "sent: mate" "the window was longer than four hours: $out"
|
|
304
|
+
pass "the cooldown window is four hours"
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
test_each_home_carries_its_own_cooldown() {
|
|
308
|
+
local home mate fakebin snap out
|
|
309
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home perhome mate)
|
|
310
|
+
# A second registered mate in the same home, so one nudge cannot silence the other.
|
|
311
|
+
cp "$home/state/mate.meta" "$home/state/other.meta"
|
|
312
|
+
sed -i.bak 's/fm-mate/fm-other/' "$home/state/other.meta" && rm -f "$home/state/other.meta.bak"
|
|
313
|
+
snap="$home/snapshot.json"
|
|
314
|
+
jq -n '{schema:"fm-fleet-snapshot.v1", generated:"2026-08-26T00:00:00Z",
|
|
315
|
+
secondmate_current:{records:[
|
|
316
|
+
{id:"mate", home:"/tmp/mate", spawn_gen:"spawn-mate", current:{state:"captain_decision",reason:null},
|
|
317
|
+
invalidity:{kind:"orphan_in_flight",ids:["a"]},
|
|
318
|
+
reconcile_inventory:{kind:"orphan_in_flight",ids:["a"]},
|
|
319
|
+
provenance:{selected:"structured-home",trust:"partial-structured"}},
|
|
320
|
+
{id:"other", home:"/tmp/other", spawn_gen:"spawn-mate", current:{state:"captain_decision",reason:null},
|
|
321
|
+
invalidity:{kind:"unowned_current",ids:["b"]},
|
|
322
|
+
reconcile_inventory:{kind:"unowned_current",ids:["b"]},
|
|
323
|
+
provenance:{selected:"structured-home",trust:"partial-structured"}}]}}' > "$snap"
|
|
324
|
+
out=$(run_notify "$home" "$fakebin" perhome "$snap") || fail "the first run failed: $out"
|
|
325
|
+
assert_contains "$out" "sent: mate" "the first home was not asked: $out"
|
|
326
|
+
assert_contains "$out" "sent: other" "the second home was not asked: $out"
|
|
327
|
+
[ "$(inbox_records "$home/state" mate)" -eq 1 ] || fail "the first home got the wrong count"
|
|
328
|
+
[ "$(inbox_records "$home/state" other)" -eq 1 ] || fail "the second home got the wrong count"
|
|
329
|
+
# Only one home's window elapses; the other must stay quiet.
|
|
330
|
+
age_cooldown "$home/state" mate 14500
|
|
331
|
+
out=$(run_notify "$home" "$fakebin" perhome "$snap")
|
|
332
|
+
assert_contains "$out" "sent: mate" "an elapsed window did not re-nudge its own home: $out"
|
|
333
|
+
assert_contains "$out" "cooldown: other" "one home's nudge reset another home's window: $out"
|
|
334
|
+
pass "the cooldown is per home, not fleet-wide"
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
test_the_ask_never_arms_a_reply_expectation_or_a_re_ring() {
|
|
338
|
+
local home mate fakebin snap ladder
|
|
339
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home fireforget mate)
|
|
340
|
+
snap="$home/snapshot.json"
|
|
341
|
+
write_snapshot "$snap" mate '{"kind":"orphan_in_flight","ids":["ghost"]}'
|
|
342
|
+
run_notify "$home" "$fakebin" fireforget "$snap" >/dev/null || fail "the ask failed"
|
|
343
|
+
[ "$(inbox_records "$home/state" mate)" -eq 1 ] || fail "the ask was not durably recorded"
|
|
344
|
+
|
|
345
|
+
# The parent expects no answer, so nothing may chase one.
|
|
346
|
+
[ "$(find "$home/state/pending-replies" -type f 2>/dev/null | wc -l | tr -d '[:space:]')" -eq 0 ] \
|
|
347
|
+
|| fail "the reconcile ask armed a pending-reply expectation"
|
|
348
|
+
|
|
349
|
+
# The record stays unhandled. With the ladder's grace elapsed, an ordinary
|
|
350
|
+
# steer in that position is due for a re-ring; this one must stay invisible.
|
|
351
|
+
ladder=$(FM_TASK_INBOX_GRACE_SECS=0 bash -c '. "$1"; fm_task_inbox_due_action "$2" "$3"' _ \
|
|
352
|
+
"$ROOT/bin/fm-task-inbox-lib.sh" "$home/state" mate 2>&1 || true)
|
|
353
|
+
[ "$ladder" = quiet ] \
|
|
354
|
+
|| fail "the unacknowledged reconcile record entered the re-ring ladder: $ladder"
|
|
355
|
+
|
|
356
|
+
# Divergence check, so the assertion above cannot pass for the wrong reason:
|
|
357
|
+
# the same inbox, same grace, with an ordinary unhandled steer added.
|
|
358
|
+
PATH="$fakebin:$PATH" FM_HOME="$home" FM_ROOT_OVERRIDE="$ROOT" \
|
|
359
|
+
FM_STATE_OVERRIDE="$home/state" \
|
|
360
|
+
FM_FAKE_TMUX_WINDOW="firstmate:fm-mate" \
|
|
361
|
+
FM_FAKE_TMUX_LOG="$TMP_ROOT/fireforget-tmux.log" \
|
|
362
|
+
FM_FAKE_TMUX_CAPTURE="$TMP_ROOT/fireforget-fake/pane.txt" \
|
|
363
|
+
"$ROOT/bin/fm-send.sh" mate "an ordinary steer that does expect handling" >/dev/null 2>&1 \
|
|
364
|
+
|| fail "the control steer could not be recorded"
|
|
365
|
+
ladder=$(FM_TASK_INBOX_GRACE_SECS=0 bash -c '. "$1"; fm_task_inbox_due_action "$2" "$3"' _ \
|
|
366
|
+
"$ROOT/bin/fm-task-inbox-lib.sh" "$home/state" mate 2>&1 || true)
|
|
367
|
+
case "$ladder" in
|
|
368
|
+
ring\ *) ;;
|
|
369
|
+
*) fail "the ladder ignored an ordinary steer too, so the quiet verdict proved nothing: $ladder" ;;
|
|
370
|
+
esac
|
|
371
|
+
pass "the reconcile ask expects no reply and stays out of a ladder that still rings ordinary steers"
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
test_a_readable_home_without_a_mismatch_is_never_asked() {
|
|
375
|
+
local home mate fakebin snap out
|
|
376
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home quiet mate)
|
|
377
|
+
snap="$home/snapshot.json"
|
|
378
|
+
write_snapshot "$snap" mate '{"kind":"child_current_unavailable","ids":["x"]}' unknown
|
|
379
|
+
out=$(run_notify "$home" "$fakebin" quiet "$snap") || fail "notify failed: $out"
|
|
380
|
+
[ "$(inbox_records "$home/state" mate)" -eq 0 ] \
|
|
381
|
+
|| fail "an unavailable child state was mistaken for a books problem"
|
|
382
|
+
write_snapshot "$snap" mate '{"kind":null,"ids":[]}' no_active_work
|
|
383
|
+
out=$(run_notify "$home" "$fakebin" quiet "$snap") || fail "notify failed: $out"
|
|
384
|
+
[ "$(inbox_records "$home/state" mate)" -eq 0 ] || fail "a healthy home was asked to reconcile"
|
|
385
|
+
pass "only a backlog-vs-metadata mismatch produces an ask"
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
test_the_parent_never_changes_the_mates_own_files() {
|
|
389
|
+
local home mate fakebin snap before after
|
|
390
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home readonly mate)
|
|
391
|
+
snap="$home/snapshot.json"
|
|
392
|
+
write_snapshot "$snap" mate '{"kind":"orphan_in_flight","ids":["ghost"]}'
|
|
393
|
+
before=$(fingerprint_tree "$mate")
|
|
394
|
+
[ -n "$before" ] || fail "the mate fixture has no files to compare"
|
|
395
|
+
run_notify "$home" "$fakebin" readonly "$snap" >/dev/null || fail "the ask failed"
|
|
396
|
+
after=$(fingerprint_tree "$mate")
|
|
397
|
+
[ "$before" = "$after" ] \
|
|
398
|
+
|| fail "asking for a reconcile changed the mate's own files: $before / $after"
|
|
399
|
+
pass "the parent asks and changes nothing inside the mate's home"
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
test_a_failed_send_is_retried_on_the_next_run() {
|
|
403
|
+
local home mate fakebin snap out rc
|
|
404
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home retry mate)
|
|
405
|
+
snap="$home/snapshot.json"
|
|
406
|
+
write_snapshot "$snap" absent-mate '{"kind":"orphan_in_flight","ids":["ghost"]}'
|
|
407
|
+
set +e
|
|
408
|
+
out=$(run_notify "$home" "$fakebin" retry "$snap"); rc=$?
|
|
409
|
+
set -e
|
|
410
|
+
[ "$rc" -ne 0 ] || fail "an unroutable ask reported success: $out"
|
|
411
|
+
assert_contains "$out" "failed: absent-mate" "the failure was not reported: $out"
|
|
412
|
+
assert_absent "$home/state/absent-mate.reconcile-nudged" \
|
|
413
|
+
"a failed ask started a cooldown and would never be retried"
|
|
414
|
+
pass "a failed ask starts no cooldown, so the next run retries it"
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
test_busy_lifecycle_locks_never_hold_up_the_digest() {
|
|
418
|
+
local label home mate fakebin snap lock ready release holder notify out
|
|
419
|
+
for label in reconcile control meta; do
|
|
420
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home "busy-$label" mate)
|
|
421
|
+
snap="$home/snapshot.json"
|
|
422
|
+
write_snapshot "$snap" mate '{"kind":"orphan_in_flight","ids":["ghost"]}'
|
|
423
|
+
case "$label" in
|
|
424
|
+
reconcile) lock="$home/state/.mate.reconcile.lock" ;;
|
|
425
|
+
control) lock="$home/state/.control-mate.lock" ;;
|
|
426
|
+
meta) lock="$home/state/.meta-mate.lock" ;;
|
|
427
|
+
esac
|
|
428
|
+
ready="$home/lock-ready"
|
|
429
|
+
release="$home/lock-release"
|
|
430
|
+
hold_lock_until_released "$lock" "$ready" "$release"
|
|
431
|
+
holder=$!
|
|
432
|
+
while [ ! -f "$ready" ]; do sleep 0.01; done
|
|
433
|
+
run_notify "$home" "$fakebin" "busy-$label" "$snap" > "$home/notify.out" 2>&1 &
|
|
434
|
+
notify=$!
|
|
435
|
+
sleep 0.2
|
|
436
|
+
if kill -0 "$notify" 2>/dev/null; then
|
|
437
|
+
: > "$release"
|
|
438
|
+
wait "$notify" 2>/dev/null || true
|
|
439
|
+
wait "$holder" 2>/dev/null || true
|
|
440
|
+
fail "a busy $label lock blocked the reconcile path"
|
|
441
|
+
fi
|
|
442
|
+
wait "$notify" || fail "a busy $label lock made notify fail"
|
|
443
|
+
: > "$release"
|
|
444
|
+
wait "$holder" || fail "the $label lock holder failed"
|
|
445
|
+
out=$(cat "$home/notify.out")
|
|
446
|
+
assert_contains "$out" "skipped: mate lock" \
|
|
447
|
+
"a busy $label lock was not reported as a skipped nudge: $out"
|
|
448
|
+
assert_absent "$home/state/mate.reconcile-nudged" \
|
|
449
|
+
"a skipped $label-lock nudge started the cooldown"
|
|
450
|
+
done
|
|
451
|
+
pass "busy reconcile lifecycle locks never block the digest or start cooldown"
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
test_concurrent_recaps_send_one_instruction() {
|
|
455
|
+
local home mate fakebin snap
|
|
456
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home concurrent mate)
|
|
457
|
+
snap="$home/snapshot.json"
|
|
458
|
+
write_snapshot "$snap" mate '{"kind":"orphan_in_flight","ids":["ghost"]}'
|
|
459
|
+
run_notify "$home" "$fakebin" concurrent "$snap" >/dev/null 2>&1 &
|
|
460
|
+
run_notify "$home" "$fakebin" concurrent "$snap" >/dev/null 2>&1 &
|
|
461
|
+
wait
|
|
462
|
+
[ "$(inbox_records "$home/state" mate)" -eq 1 ] \
|
|
463
|
+
|| fail "two simultaneous recaps asked the mate twice"
|
|
464
|
+
pass "simultaneous recaps still ask the mate only once"
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
test_a_delayed_snapshot_never_prescribes_a_stale_repair() {
|
|
468
|
+
local home mate fakebin old_snap new_snap out text
|
|
469
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home delayed mate)
|
|
470
|
+
old_snap="$home/old-snapshot.json"
|
|
471
|
+
new_snap="$home/new-snapshot.json"
|
|
472
|
+
write_snapshot "$old_snap" mate '{"kind":"orphan_in_flight","ids":["already-repaired"]}'
|
|
473
|
+
write_snapshot "$new_snap" mate '{"kind":"unowned_current","ids":["current-row"]}'
|
|
474
|
+
|
|
475
|
+
out=$(run_notify "$home" "$fakebin" delayed "$old_snap") \
|
|
476
|
+
|| fail "the delayed reconcile ask failed: $out"
|
|
477
|
+
assert_contains "$out" "sent: mate orphan_in_flight" \
|
|
478
|
+
"the delayed snapshot did not produce the cooldown-limited check: $out"
|
|
479
|
+
out=$(run_notify "$home" "$fakebin" delayed "$new_snap") \
|
|
480
|
+
|| fail "the current snapshot reconcile failed: $out"
|
|
481
|
+
assert_contains "$out" "cooldown: mate" \
|
|
482
|
+
"the per-home cooldown did not deduplicate the newer observation: $out"
|
|
483
|
+
[ "$(inbox_records "$home/state" mate)" -eq 1 ] \
|
|
484
|
+
|| fail "the old and new snapshots produced more than one ask inside the cooldown"
|
|
485
|
+
text=$(inbox_text "$home/state" mate)
|
|
486
|
+
assert_contains "$text" "check your current books" \
|
|
487
|
+
"the delayed ask did not direct the mate to current state"
|
|
488
|
+
if printf '%s' "$text" | grep -Eq 'already-repaired|current-row'; then
|
|
489
|
+
fail "the delayed ask embedded sampled row details and could prescribe a stale repair: $text"
|
|
490
|
+
fi
|
|
491
|
+
pass "a delayed snapshot asks for a current check instead of prescribing a stale repair"
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
test_a_stale_snapshot_never_targets_a_replacement_mate() {
|
|
495
|
+
local home mate fakebin snap out
|
|
496
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home stale mate)
|
|
497
|
+
snap="$home/snapshot.json"
|
|
498
|
+
write_snapshot "$snap" mate '{"kind":"orphan_in_flight","ids":["old-ghost"]}'
|
|
499
|
+
awk '{ sub(/^spawn_gen=.*/, "spawn_gen=spawn-replacement"); print }' \
|
|
500
|
+
"$home/state/mate.meta" > "$home/state/mate.meta.tmp"
|
|
501
|
+
mv "$home/state/mate.meta.tmp" "$home/state/mate.meta"
|
|
502
|
+
|
|
503
|
+
out=$(run_notify "$home" "$fakebin" stale "$snap") \
|
|
504
|
+
|| fail "a stale snapshot made reconcile fail: $out"
|
|
505
|
+
assert_contains "$out" "stale: mate orphan_in_flight" \
|
|
506
|
+
"the stale snapshot was not identified: $out"
|
|
507
|
+
[ "$(inbox_records "$home/state" mate)" -eq 0 ] \
|
|
508
|
+
|| fail "a replacement mate received its predecessor's reconcile ask"
|
|
509
|
+
assert_absent "$home/state/mate.reconcile-nudged" \
|
|
510
|
+
"a replacement mate inherited cooldown from a stale snapshot"
|
|
511
|
+
pass "a stale snapshot cannot ask or silence a replacement mate"
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
test_teardown_cannot_leave_its_replacement_in_cooldown() {
|
|
515
|
+
local home mate fakebin snap signal release lifecycle_done cooldown notify_pid lifecycle_pid
|
|
516
|
+
{ read -r home; read -r mate; read -r fakebin; } < <(make_main_home lifecycle mate)
|
|
517
|
+
snap="$home/snapshot.json"
|
|
518
|
+
signal="$home/send-ringing"
|
|
519
|
+
release="$home/release-ring"
|
|
520
|
+
lifecycle_done="$home/lifecycle-done"
|
|
521
|
+
cooldown="$home/state/mate.reconcile-nudged"
|
|
522
|
+
write_snapshot "$snap" mate '{"kind":"orphan_in_flight","ids":["ghost"]}'
|
|
523
|
+
mv "$fakebin/tmux" "$fakebin/tmux-real"
|
|
524
|
+
cat > "$fakebin/tmux" <<'SH'
|
|
525
|
+
#!/usr/bin/env bash
|
|
526
|
+
set -u
|
|
527
|
+
if [ "${1:-}" = send-keys ]; then
|
|
528
|
+
: > "$FM_FAKE_TMUX_SEND_SIGNAL"
|
|
529
|
+
while [ ! -f "$FM_FAKE_TMUX_SEND_RELEASE" ]; do sleep 0.01; done
|
|
530
|
+
fi
|
|
531
|
+
exec "$(dirname "$0")/tmux-real" "$@"
|
|
532
|
+
SH
|
|
533
|
+
chmod +x "$fakebin/tmux"
|
|
534
|
+
|
|
535
|
+
FM_FAKE_TMUX_SEND_SIGNAL="$signal" FM_FAKE_TMUX_SEND_RELEASE="$release" \
|
|
536
|
+
run_notify "$home" "$fakebin" lifecycle "$snap" >/dev/null 2>&1 &
|
|
537
|
+
notify_pid=$!
|
|
538
|
+
while [ ! -f "$signal" ]; do sleep 0.01; done
|
|
539
|
+
|
|
540
|
+
(
|
|
541
|
+
. "$ROOT/bin/fm-wake-lib.sh"
|
|
542
|
+
fm_lock_acquire_wait "$home/state/.control-mate.lock"
|
|
543
|
+
fm_lock_acquire_wait "$home/state/.meta-mate.lock"
|
|
544
|
+
rm -rf "$home/state/mate.inbox"
|
|
545
|
+
rm -f "$home/state/mate.meta" "$home/state/mate.reconcile-nudged"
|
|
546
|
+
cat > "$home/state/mate.meta" <<META
|
|
547
|
+
window=firstmate:fm-mate
|
|
548
|
+
kind=secondmate
|
|
549
|
+
harness=claude
|
|
550
|
+
backend=tmux
|
|
551
|
+
spawn_gen=spawn-replacement
|
|
552
|
+
home=$mate
|
|
553
|
+
worktree=$mate
|
|
554
|
+
META
|
|
555
|
+
fm_lock_release "$home/state/.meta-mate.lock"
|
|
556
|
+
fm_lock_release "$home/state/.control-mate.lock"
|
|
557
|
+
: > "$lifecycle_done"
|
|
558
|
+
) &
|
|
559
|
+
lifecycle_pid=$!
|
|
560
|
+
|
|
561
|
+
sleep 0.1
|
|
562
|
+
: > "$release"
|
|
563
|
+
wait "$notify_pid" 2>/dev/null || true
|
|
564
|
+
wait "$lifecycle_pid" || fail "the simulated teardown and reseed failed"
|
|
565
|
+
[ -f "$lifecycle_done" ] || fail "the simulated lifecycle transition did not finish"
|
|
566
|
+
assert_absent "$cooldown" \
|
|
567
|
+
"a retired mate's cooldown was recreated after its replacement was seeded"
|
|
568
|
+
pass "teardown retires the cooldown before a replacement can inherit it"
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
# A persistent REMOTE secondmate's parent metadata never carries spawn_gen
|
|
572
|
+
# (bin/fm-spawn.sh's spawn_remote_secondmate() never writes one). This is the
|
|
573
|
+
# proven marker-bearing path's counterpart: same durable fire-and-forget
|
|
574
|
+
# delivery and cooldown behavior, driven end to end through the real remote
|
|
575
|
+
# transport, for a mate that legitimately has no generation marker at all.
|
|
576
|
+
test_a_markerless_remote_secondmate_is_nudged_once_per_window() {
|
|
577
|
+
local home rhome fakebin snap out
|
|
578
|
+
fakebin=$(make_remote_ssh_stub "$TMP_ROOT/remote-once")
|
|
579
|
+
rhome=$(make_remote_secondmate_home remote-once-mate)
|
|
580
|
+
home=$(make_remote_parent_home remote-once remote-once-mate "$rhome" remote-once-host)
|
|
581
|
+
snap="$home/snapshot.json"
|
|
582
|
+
write_remote_snapshot "$snap" remote-once-mate remote-once-host \
|
|
583
|
+
'{"kind":"orphan_in_flight","ids":["stale-scout"]}'
|
|
584
|
+
|
|
585
|
+
out=$(run_remote_notify "$home" "$fakebin" "$snap") \
|
|
586
|
+
|| fail "the first markerless reconcile ask failed: $out"
|
|
587
|
+
assert_contains "$out" "sent: remote-once-mate orphan_in_flight" \
|
|
588
|
+
"a legitimately markerless remote mate got no ask: $out"
|
|
589
|
+
[ -n "$(remote_inbox_records "$rhome" remote-once-mate)" ] \
|
|
590
|
+
|| fail "the ask did not land as a durable record in the remote steering inbox"
|
|
591
|
+
[ -f "$home/state/remote-once-mate.reconcile-nudged" ] \
|
|
592
|
+
|| fail "a successful markerless ask did not start the cooldown"
|
|
593
|
+
|
|
594
|
+
out=$(run_remote_notify "$home" "$fakebin" "$snap") \
|
|
595
|
+
|| fail "the repeat markerless run failed: $out"
|
|
596
|
+
assert_contains "$out" "cooldown: remote-once-mate" \
|
|
597
|
+
"a repeated markerless snapshot did not report the cooldown: $out"
|
|
598
|
+
[ "$(remote_inbox_records "$rhome" remote-once-mate | grep -c . || true)" -eq 1 ] \
|
|
599
|
+
|| fail "repeated markerless snapshots asked the mate more than once inside the cooldown"
|
|
600
|
+
pass "a legitimately markerless persistent remote secondmate is nudged once per window"
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
# The safety boundary the spawn_gen check protects for local mates has a
|
|
604
|
+
# host-keyed counterpart for markerless remote mates: a snapshot sampled
|
|
605
|
+
# before the route moved to a different host must never reach the new host's
|
|
606
|
+
# mate or arm its cooldown.
|
|
607
|
+
test_a_stale_remote_route_is_refused() {
|
|
608
|
+
local home rhome fakebin snap out
|
|
609
|
+
fakebin=$(make_remote_ssh_stub "$TMP_ROOT/remote-stale")
|
|
610
|
+
rhome=$(make_remote_secondmate_home remote-stale-mate)
|
|
611
|
+
home=$(make_remote_parent_home remote-stale remote-stale-mate "$rhome" old-host)
|
|
612
|
+
snap="$home/snapshot.json"
|
|
613
|
+
write_remote_snapshot "$snap" remote-stale-mate old-host \
|
|
614
|
+
'{"kind":"orphan_in_flight","ids":["old-ghost"]}'
|
|
615
|
+
# The route was re-seeded to a different host since the snapshot was taken.
|
|
616
|
+
sed -i.bak 's/^remote_host=.*/remote_host=new-host/' "$home/state/remote-stale-mate.meta" \
|
|
617
|
+
&& rm -f "$home/state/remote-stale-mate.meta.bak"
|
|
618
|
+
|
|
619
|
+
out=$(run_remote_notify "$home" "$fakebin" "$snap") \
|
|
620
|
+
|| fail "a stale remote-route snapshot made reconcile fail: $out"
|
|
621
|
+
assert_contains "$out" "stale: remote-stale-mate orphan_in_flight" \
|
|
622
|
+
"a snapshot sampled from a retired remote route was not identified: $out"
|
|
623
|
+
[ -z "$(remote_inbox_records "$rhome" remote-stale-mate)" ] \
|
|
624
|
+
|| fail "a replacement remote route received its predecessor's reconcile ask"
|
|
625
|
+
assert_absent "$home/state/remote-stale-mate.reconcile-nudged" \
|
|
626
|
+
"a replacement remote route inherited cooldown from a stale snapshot"
|
|
627
|
+
pass "a stale remote-route snapshot cannot ask or silence a replacement mate"
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
test_route_replacement_during_send_is_refused() {
|
|
631
|
+
local home rhome fakebin snap signal release out rc notify_pid
|
|
632
|
+
fakebin=$(make_remote_ssh_stub "$TMP_ROOT/remote-send-race")
|
|
633
|
+
rhome=$(make_remote_secondmate_home remote-send-race-mate)
|
|
634
|
+
home=$(make_remote_parent_home remote-send-race remote-send-race-mate "$rhome" old-host)
|
|
635
|
+
snap="$home/snapshot.json"
|
|
636
|
+
signal="$home/fm-send-started"
|
|
637
|
+
release="$home/release-fm-send"
|
|
638
|
+
write_remote_snapshot "$snap" remote-send-race-mate old-host \
|
|
639
|
+
'{"kind":"orphan_in_flight","ids":["old-ghost"]}'
|
|
640
|
+
cat > "$fakebin/dirname" <<'SH'
|
|
641
|
+
#!/usr/bin/env bash
|
|
642
|
+
set -u
|
|
643
|
+
case "${1:-}" in
|
|
644
|
+
*/fm-send.sh)
|
|
645
|
+
: > "$FM_RECONCILE_RACE_SIGNAL"
|
|
646
|
+
while [ ! -f "$FM_RECONCILE_RACE_RELEASE" ]; do sleep 0.01; done
|
|
647
|
+
;;
|
|
648
|
+
esac
|
|
649
|
+
case "${1:-}" in
|
|
650
|
+
*/*) printf '%s\n' "${1%/*}" ;;
|
|
651
|
+
*) printf '.\n' ;;
|
|
652
|
+
esac
|
|
653
|
+
SH
|
|
654
|
+
chmod +x "$fakebin/dirname"
|
|
655
|
+
|
|
656
|
+
rc=0
|
|
657
|
+
FM_RECONCILE_RACE_SIGNAL="$signal" FM_RECONCILE_RACE_RELEASE="$release" \
|
|
658
|
+
run_remote_notify "$home" "$fakebin" "$snap" > "$home/notify.out" 2>&1 &
|
|
659
|
+
notify_pid=$!
|
|
660
|
+
while [ ! -f "$signal" ]; do
|
|
661
|
+
kill -0 "$notify_pid" 2>/dev/null || fail "reconcile exited before entering fm-send"
|
|
662
|
+
sleep 0.01
|
|
663
|
+
done
|
|
664
|
+
|
|
665
|
+
. "$ROOT/bin/fm-wake-lib.sh"
|
|
666
|
+
fm_lock_acquire_wait "$home/state/.control-remote-send-race-mate.lock"
|
|
667
|
+
fm_lock_acquire_wait "$home/state/.meta-remote-send-race-mate.lock"
|
|
668
|
+
sed 's/^remote_host=.*/remote_host=new-host/' \
|
|
669
|
+
"$home/state/remote-send-race-mate.meta" > "$home/state/remote-send-race-mate.meta.tmp"
|
|
670
|
+
mv "$home/state/remote-send-race-mate.meta.tmp" "$home/state/remote-send-race-mate.meta"
|
|
671
|
+
sed 's/host: old-host/host: new-host/' \
|
|
672
|
+
"$home/data/secondmates.md" > "$home/data/secondmates.md.tmp"
|
|
673
|
+
mv "$home/data/secondmates.md.tmp" "$home/data/secondmates.md"
|
|
674
|
+
fm_lock_release "$home/state/.meta-remote-send-race-mate.lock"
|
|
675
|
+
fm_lock_release "$home/state/.control-remote-send-race-mate.lock"
|
|
676
|
+
: > "$release"
|
|
677
|
+
wait "$notify_pid" || rc=$?
|
|
678
|
+
out=$(cat "$home/notify.out")
|
|
679
|
+
|
|
680
|
+
[ "$rc" -ne 0 ] || fail "a route replacement during send reported success: $out"
|
|
681
|
+
assert_contains "$out" "failed: remote-send-race-mate orphan_in_flight" \
|
|
682
|
+
"a route replacement during send was not refused: $out"
|
|
683
|
+
[ -z "$(remote_inbox_records "$rhome" remote-send-race-mate)" ] \
|
|
684
|
+
|| fail "a replacement route received its predecessor's reconcile ask"
|
|
685
|
+
assert_absent "$home/state/remote-send-race-mate.reconcile-nudged" \
|
|
686
|
+
"a refused route replacement started the cooldown"
|
|
687
|
+
pass "a route replacement between reconcile and fm-send cannot receive a stale ask"
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
# A row with neither a spawn generation nor a host carries no safe identity at
|
|
691
|
+
# all - the markerless path must not swallow that case the way the original
|
|
692
|
+
# bug swallowed every markerless row.
|
|
693
|
+
test_a_row_with_no_identity_at_all_fails_loudly() {
|
|
694
|
+
local home rhome fakebin snap out
|
|
695
|
+
fakebin=$(make_remote_ssh_stub "$TMP_ROOT/remote-noid")
|
|
696
|
+
rhome=$(make_remote_secondmate_home remote-noid-mate)
|
|
697
|
+
home=$(make_remote_parent_home remote-noid remote-noid-mate "$rhome" remote-noid-host)
|
|
698
|
+
snap="$home/snapshot.json"
|
|
699
|
+
write_remote_snapshot "$snap" remote-noid-mate "" \
|
|
700
|
+
'{"kind":"orphan_in_flight","ids":["ghost"]}'
|
|
701
|
+
|
|
702
|
+
set +e
|
|
703
|
+
out=$(run_remote_notify "$home" "$fakebin" "$snap"); rc=$?
|
|
704
|
+
set -e
|
|
705
|
+
[ "$rc" -ne 0 ] || fail "a row with no sampled identity at all reported success: $out"
|
|
706
|
+
assert_contains "$out" "failed: remote-noid-mate orphan_in_flight" \
|
|
707
|
+
"an unidentifiable row was not reported as failed: $out"
|
|
708
|
+
[ -z "$(remote_inbox_records "$rhome" remote-noid-mate)" ] \
|
|
709
|
+
|| fail "an unidentifiable row still received a reconcile ask"
|
|
710
|
+
assert_absent "$home/state/remote-noid-mate.reconcile-nudged" \
|
|
711
|
+
"an unidentifiable row started a cooldown"
|
|
712
|
+
pass "a row with neither a spawn generation nor a host fails loudly instead of vanishing"
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
test_an_inventory_mismatch_asks_the_mate_once_per_window
|
|
716
|
+
test_a_mismatch_still_there_after_the_window_earns_one_more_nudge
|
|
717
|
+
test_the_cooldown_starts_when_delivery_finishes
|
|
718
|
+
test_the_window_is_four_hours
|
|
719
|
+
test_each_home_carries_its_own_cooldown
|
|
720
|
+
test_the_ask_never_arms_a_reply_expectation_or_a_re_ring
|
|
721
|
+
test_a_readable_home_without_a_mismatch_is_never_asked
|
|
722
|
+
test_the_parent_never_changes_the_mates_own_files
|
|
723
|
+
test_a_failed_send_is_retried_on_the_next_run
|
|
724
|
+
test_busy_lifecycle_locks_never_hold_up_the_digest
|
|
725
|
+
test_concurrent_recaps_send_one_instruction
|
|
726
|
+
test_a_delayed_snapshot_never_prescribes_a_stale_repair
|
|
727
|
+
test_a_stale_snapshot_never_targets_a_replacement_mate
|
|
728
|
+
test_teardown_cannot_leave_its_replacement_in_cooldown
|
|
729
|
+
test_a_markerless_remote_secondmate_is_nudged_once_per_window
|
|
730
|
+
test_a_stale_remote_route_is_refused
|
|
731
|
+
test_route_replacement_during_send_is_refused
|
|
732
|
+
test_a_row_with_no_identity_at_all_fails_loudly
|