@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,913 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Tests for the local-HEAD secondmate sync: every secondmate home tracks the
|
|
3
|
+
# PRIMARY firstmate checkout's current default-branch commit by a purely LOCAL
|
|
4
|
+
# fast-forward (no origin fetch). Two hook points drive it - bin/fm-spawn.sh
|
|
5
|
+
# (before launching a secondmate) and bin/fm-bootstrap.sh (a startup sweep of
|
|
6
|
+
# every live secondmate home) - and both share the ff machinery in
|
|
7
|
+
# bin/fm-ff-lib.sh.
|
|
8
|
+
#
|
|
9
|
+
# The guarantees under test:
|
|
10
|
+
# - The shared ff helper, driven with a LOCAL commit base, advances a behind
|
|
11
|
+
# home (updated), is a no-op on an already-current home (current, no nudge),
|
|
12
|
+
# and refuses - leaving work untouched - on a dirty, diverged, or
|
|
13
|
+
# in-flight (feature-branch) home.
|
|
14
|
+
# - No origin fetch happens in the local-HEAD sync path.
|
|
15
|
+
# - The bootstrap sweep fast-forwards every live secondmate home and sends a
|
|
16
|
+
# reread nudge ONLY for a running secondmate whose instruction surface
|
|
17
|
+
# actually changed; a successful send is reported as BOOTSTRAP_INFO:, a
|
|
18
|
+
# failed send is reported as NUDGE_SECONDMATES:, an already-current or
|
|
19
|
+
# readme-only home is never nudged, a skipped home is reported as
|
|
20
|
+
# SECONDMATE_SYNC:, and a home with no live metadata is never swept.
|
|
21
|
+
# - Spawning a secondmate fast-forwards its worktree to the primary's HEAD
|
|
22
|
+
# before launch, or warns and launches unchanged when the sync is skipped.
|
|
23
|
+
set -u
|
|
24
|
+
|
|
25
|
+
# shellcheck source=tests/lib.sh
|
|
26
|
+
. "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
|
27
|
+
|
|
28
|
+
# shellcheck source=bin/fm-ff-lib.sh
|
|
29
|
+
. "$ROOT/bin/fm-ff-lib.sh"
|
|
30
|
+
|
|
31
|
+
BASE_PATH=${FM_TEST_BASE_PATH:-/usr/bin:/bin:/usr/sbin:/sbin}
|
|
32
|
+
|
|
33
|
+
# Deterministic, isolated git identity for fixture commits.
|
|
34
|
+
fm_git_identity fmtest fmtest@example.com
|
|
35
|
+
|
|
36
|
+
TMP_ROOT=$(fm_test_tmproot fm-secondmate-sync)
|
|
37
|
+
export FM_BACKEND=tmux
|
|
38
|
+
|
|
39
|
+
# --- world builders --------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
# new_world <name>: a PRIMARY firstmate repo on `main` with one commit (the
|
|
42
|
+
# instruction surface seeded) and a home dir with state/ and data/. NO origin
|
|
43
|
+
# remote: the local-HEAD sync never needs one. Echoes the world dir.
|
|
44
|
+
new_world() {
|
|
45
|
+
local name=$1 w
|
|
46
|
+
w="$TMP_ROOT/$name"
|
|
47
|
+
mkdir -p "$w/home/state" "$w/home/data"
|
|
48
|
+
# Fresh watcher beacon keeps fm-guard quiet for the spawn path.
|
|
49
|
+
touch "$w/home/state/.last-watcher-beat"
|
|
50
|
+
|
|
51
|
+
git init -q -b main "$w/main"
|
|
52
|
+
# Mirror the real repo: the gitignored operational dirs never dirty a worktree,
|
|
53
|
+
# so a secondmate home's data/state/projects can never block its fast-forward.
|
|
54
|
+
printf 'projects/\nstate/\ndata/\n.no-mistakes/\nconfig/crew-harness\nscratchpad*\n' > "$w/main/.gitignore"
|
|
55
|
+
printf 'v1\n' > "$w/main/AGENTS.md"
|
|
56
|
+
printf 'r1\n' > "$w/main/README.md"
|
|
57
|
+
mkdir -p "$w/main/bin" "$w/main/.agents/skills"
|
|
58
|
+
printf 'echo a\n' > "$w/main/bin/tool.sh"
|
|
59
|
+
printf 's1\n' > "$w/main/.agents/skills/note.md"
|
|
60
|
+
git -C "$w/main" add -A
|
|
61
|
+
git -C "$w/main" commit -qm c1
|
|
62
|
+
printf '%s\n' "$w"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# add_sm_worktree <w> <id> <commit>: a secondmate home as a DETACHED worktree of
|
|
66
|
+
# the primary at <commit>, plus its seed marker and a LIVE kind=secondmate meta
|
|
67
|
+
# (a window= makes it a running direct report).
|
|
68
|
+
add_sm_worktree() {
|
|
69
|
+
local w=$1 id=$2 commit=$3
|
|
70
|
+
git -C "$w/main" worktree add -q --detach "$w/$id" "$commit"
|
|
71
|
+
printf '%s\n' "$id" > "$w/$id/.fm-secondmate-home"
|
|
72
|
+
{
|
|
73
|
+
printf 'window=firstmate:fm-%s\n' "$id"
|
|
74
|
+
printf 'kind=secondmate\n'
|
|
75
|
+
printf 'harness=codex\n'
|
|
76
|
+
printf 'home=%s/%s\n' "$w" "$id"
|
|
77
|
+
} > "$w/home/state/$id.meta"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# bump_primary <w> <mode>: advance the PRIMARY's main branch by one local commit.
|
|
81
|
+
# instr changes the instruction surface (AGENTS.md, bin, .agents/skills) plus README;
|
|
82
|
+
# readme changes only README. No push - the sync follows the primary's local HEAD.
|
|
83
|
+
bump_primary() {
|
|
84
|
+
local w=$1 mode=$2
|
|
85
|
+
printf 'r-%s\n' "$mode" >> "$w/main/README.md"
|
|
86
|
+
if [ "$mode" = instr ]; then
|
|
87
|
+
printf 'v-%s\n' "$mode" > "$w/main/AGENTS.md"
|
|
88
|
+
printf 'echo %s\n' "$mode" > "$w/main/bin/tool.sh"
|
|
89
|
+
printf 's-%s\n' "$mode" > "$w/main/.agents/skills/note.md"
|
|
90
|
+
fi
|
|
91
|
+
git -C "$w/main" add -A
|
|
92
|
+
git -C "$w/main" commit -qm "bump-$mode"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
head_of() { git -C "$1" rev-parse HEAD; }
|
|
96
|
+
|
|
97
|
+
# ignore_marker_commit <w>: land THE FIX in the primary - add the seed marker to
|
|
98
|
+
# the tracked .gitignore and commit it on main. The marker (.fm-secondmate-home)
|
|
99
|
+
# is firstmate-generic, written by bin/fm-home-seed.sh into every seeded home; once
|
|
100
|
+
# a home fast-forwards past this commit the marker is git-ignored and can no longer
|
|
101
|
+
# read as a dirty working tree to any `git status --porcelain` dirtiness check.
|
|
102
|
+
ignore_marker_commit() {
|
|
103
|
+
local w=$1
|
|
104
|
+
printf '.fm-secondmate-home\n' >> "$w/main/.gitignore"
|
|
105
|
+
git -C "$w/main" add -A
|
|
106
|
+
git -C "$w/main" commit -qm "gitignore seed marker"
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
# seed_marked_home <w> <id> <commit>: a secondmate home matching what
|
|
110
|
+
# bin/fm-home-seed.sh actually lays down - a detached worktree at <commit>, the
|
|
111
|
+
# seed marker, a live kind=secondmate meta, and the gitignored operational dirs
|
|
112
|
+
# with a charter. The ONLY unignored extra file is the seed marker, which is
|
|
113
|
+
# exactly what this fix must keep from dirtying the home.
|
|
114
|
+
seed_marked_home() {
|
|
115
|
+
local w=$1 id=$2 commit=$3
|
|
116
|
+
add_sm_worktree "$w" "$id" "$commit"
|
|
117
|
+
mkdir -p "$w/$id/data" "$w/$id/state" "$w/$id/config" "$w/$id/projects"
|
|
118
|
+
printf 'charter\n' > "$w/$id/data/charter.md"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# run_ff <dir> <base>: drive the shared ff helper in THIS shell (output to a file,
|
|
122
|
+
# not a subshell, so FF_STATUS / FF_INSTR propagate). Sets FF_OUT to the printed
|
|
123
|
+
# status line. Uses allow_detached=yes, ignore_seed_marker=yes (the secondmate
|
|
124
|
+
# home contract).
|
|
125
|
+
FF_OUT=""
|
|
126
|
+
run_ff() {
|
|
127
|
+
local dir=$1 base=$2 outfile="$TMP_ROOT/ff.out"
|
|
128
|
+
ff_target "$dir" "secondmate sm" "$base" yes yes >"$outfile" 2>&1
|
|
129
|
+
FF_OUT=$(cat "$outfile")
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# --- T1: updated - a behind home fast-forwards to the primary's local HEAD ---
|
|
133
|
+
test_ff_updated() {
|
|
134
|
+
local w c1 base
|
|
135
|
+
w=$(new_world ff-updated)
|
|
136
|
+
c1=$(head_of "$w/main")
|
|
137
|
+
git -C "$w/main" worktree add -q --detach "$w/sm" "$c1"
|
|
138
|
+
bump_primary "$w" instr
|
|
139
|
+
base=$(primary_head_commit "$w/main")
|
|
140
|
+
|
|
141
|
+
run_ff "$w/sm" "$base"
|
|
142
|
+
|
|
143
|
+
[ "$FF_STATUS" = updated ] || fail "FF_STATUS: expected updated, got '$FF_STATUS'"
|
|
144
|
+
assert_contains "$FF_OUT" "secondmate sm: updated " "updated home prints an advance line"
|
|
145
|
+
assert_contains "$FF_INSTR" "AGENTS.md" "instruction change is recorded in FF_INSTR"
|
|
146
|
+
[ "$(head_of "$w/sm")" = "$base" ] || fail "home did not advance to the primary's local HEAD"
|
|
147
|
+
git -C "$w/sm" symbolic-ref -q HEAD >/dev/null && fail "home is no longer detached"
|
|
148
|
+
# A fast-forwarded tip has exactly one parent; a merge would have two.
|
|
149
|
+
[ "$(git -C "$w/sm" rev-list --parents -n1 HEAD | wc -w | tr -d ' ')" -eq 2 ] \
|
|
150
|
+
|| fail "home tip is not a single-parent fast-forward"
|
|
151
|
+
pass "T1 updated: a behind home fast-forwards to the primary's local HEAD"
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# --- T2: current - already on the primary's HEAD is a no-op (no nudge) -------
|
|
155
|
+
test_ff_current() {
|
|
156
|
+
local w base
|
|
157
|
+
w=$(new_world ff-current)
|
|
158
|
+
bump_primary "$w" instr
|
|
159
|
+
base=$(primary_head_commit "$w/main")
|
|
160
|
+
git -C "$w/main" worktree add -q --detach "$w/sm" "$base"
|
|
161
|
+
|
|
162
|
+
run_ff "$w/sm" "$base"
|
|
163
|
+
|
|
164
|
+
[ "$FF_STATUS" = current ] || fail "FF_STATUS: expected current, got '$FF_STATUS'"
|
|
165
|
+
assert_contains "$FF_OUT" "secondmate sm: already current" "current home reports already current"
|
|
166
|
+
[ -z "$FF_INSTR" ] || fail "a no-op must not report instruction changes (would trigger a nudge)"
|
|
167
|
+
[ "$(head_of "$w/sm")" = "$base" ] || fail "current home HEAD moved"
|
|
168
|
+
pass "T2 current: an already-current home is a no-op and reports no instruction change"
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
# --- T3: dirty - a home with uncommitted edits is skipped, edit preserved ----
|
|
172
|
+
test_ff_dirty() {
|
|
173
|
+
local w c1 base before
|
|
174
|
+
w=$(new_world ff-dirty)
|
|
175
|
+
c1=$(head_of "$w/main")
|
|
176
|
+
git -C "$w/main" worktree add -q --detach "$w/sm" "$c1"
|
|
177
|
+
bump_primary "$w" instr
|
|
178
|
+
base=$(primary_head_commit "$w/main")
|
|
179
|
+
printf 'uncommitted local edit\n' >> "$w/sm/AGENTS.md"
|
|
180
|
+
before=$(head_of "$w/sm")
|
|
181
|
+
|
|
182
|
+
run_ff "$w/sm" "$base"
|
|
183
|
+
|
|
184
|
+
[ "$FF_STATUS" = skipped ] || fail "FF_STATUS: expected skipped, got '$FF_STATUS'"
|
|
185
|
+
assert_contains "$FF_OUT" "secondmate sm: skipped: dirty working tree" "dirty home is skipped"
|
|
186
|
+
[ "$(head_of "$w/sm")" = "$before" ] || fail "dirty home HEAD moved"
|
|
187
|
+
grep -q 'uncommitted local edit' "$w/sm/AGENTS.md" || fail "dirty edit was discarded"
|
|
188
|
+
pass "T3 dirty: an uncommitted home is skipped, its edit preserved"
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
# --- scratchpad2 must not trip the dirty-home sync guard ----------------------
|
|
192
|
+
test_scratchpad2_does_not_dirty_home() {
|
|
193
|
+
local w c1 base
|
|
194
|
+
w=$(new_world scratchpad2-clean)
|
|
195
|
+
c1=$(head_of "$w/main")
|
|
196
|
+
git -C "$w/main" worktree add -q --detach "$w/sm" "$c1"
|
|
197
|
+
mkdir -p "$w/sm/scratchpad2"
|
|
198
|
+
printf 'notes\n' > "$w/sm/scratchpad2/notes.txt"
|
|
199
|
+
bump_primary "$w" instr
|
|
200
|
+
base=$(primary_head_commit "$w/main")
|
|
201
|
+
|
|
202
|
+
run_ff "$w/sm" "$base"
|
|
203
|
+
|
|
204
|
+
[ "$FF_STATUS" = updated ] || fail "FF_STATUS: expected updated, got '$FF_STATUS' (scratchpad2/ must not dirty the home)"
|
|
205
|
+
[ "$(head_of "$w/sm")" = "$base" ] || fail "home did not fast-forward past an ignored scratchpad2/ directory"
|
|
206
|
+
grep -q notes "$w/sm/scratchpad2/notes.txt" || fail "scratchpad2 contents were discarded"
|
|
207
|
+
pass "scratchpad2/ does not mark a secondmate home dirty for the sync guard"
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
# --- T4: diverged - a home with its own commit is skipped, commit preserved --
|
|
211
|
+
test_ff_diverged() {
|
|
212
|
+
local w c1 base before
|
|
213
|
+
w=$(new_world ff-diverged)
|
|
214
|
+
c1=$(head_of "$w/main")
|
|
215
|
+
git -C "$w/main" worktree add -q --detach "$w/sm" "$c1"
|
|
216
|
+
printf 'fork work\n' > "$w/sm/AGENTS.md"
|
|
217
|
+
git -C "$w/sm" add -A
|
|
218
|
+
git -C "$w/sm" commit -qm local-work
|
|
219
|
+
before=$(head_of "$w/sm")
|
|
220
|
+
bump_primary "$w" instr
|
|
221
|
+
base=$(primary_head_commit "$w/main")
|
|
222
|
+
|
|
223
|
+
run_ff "$w/sm" "$base"
|
|
224
|
+
|
|
225
|
+
[ "$FF_STATUS" = skipped ] || fail "FF_STATUS: expected skipped, got '$FF_STATUS'"
|
|
226
|
+
assert_contains "$FF_OUT" "secondmate sm: skipped: diverged from $base" "diverged home is skipped"
|
|
227
|
+
[ "$(head_of "$w/sm")" = "$before" ] || fail "diverged home HEAD moved (unlanded work at risk)"
|
|
228
|
+
pass "T4 diverged: a home that is not an ancestor of the primary's HEAD is skipped"
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
# --- T5: in-flight - a home on a feature branch is skipped, work preserved ----
|
|
232
|
+
# A secondmate home carrying its own in-flight work sits on a named feature
|
|
233
|
+
# branch, not a detached default-branch HEAD; the ff helper refuses to move it.
|
|
234
|
+
test_ff_inflight_feature_branch() {
|
|
235
|
+
local w c1 base before
|
|
236
|
+
w=$(new_world ff-inflight)
|
|
237
|
+
c1=$(head_of "$w/main")
|
|
238
|
+
git -C "$w/main" worktree add -q -b feature/wip "$w/sm" "$c1"
|
|
239
|
+
printf 'work in progress\n' >> "$w/sm/README.md"
|
|
240
|
+
git -C "$w/sm" add -A
|
|
241
|
+
git -C "$w/sm" commit -qm wip
|
|
242
|
+
before=$(head_of "$w/sm")
|
|
243
|
+
bump_primary "$w" instr
|
|
244
|
+
base=$(primary_head_commit "$w/main")
|
|
245
|
+
|
|
246
|
+
run_ff "$w/sm" "$base"
|
|
247
|
+
|
|
248
|
+
[ "$FF_STATUS" = skipped ] || fail "FF_STATUS: expected skipped, got '$FF_STATUS'"
|
|
249
|
+
assert_contains "$FF_OUT" "secondmate sm: skipped: on feature/wip, expected main" \
|
|
250
|
+
"a home on a feature branch is skipped"
|
|
251
|
+
[ "$(head_of "$w/sm")" = "$before" ] || fail "in-flight home HEAD moved (work at risk)"
|
|
252
|
+
pass "T5 in-flight: a home on a feature branch is skipped, its work preserved"
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
# --- T6: no origin fetch happens in the local-HEAD sync path -----------------
|
|
256
|
+
# A bare `git fetch` would need the network; the sync must never reach for it.
|
|
257
|
+
# Shadow git with a wrapper that records any `fetch` invocation, then drive the
|
|
258
|
+
# updated path and confirm the wrapper saw none.
|
|
259
|
+
test_no_fetch_in_local_path() {
|
|
260
|
+
local w c1 base fakebin log real_git
|
|
261
|
+
w=$(new_world ff-nofetch)
|
|
262
|
+
c1=$(head_of "$w/main")
|
|
263
|
+
git -C "$w/main" worktree add -q --detach "$w/sm" "$c1"
|
|
264
|
+
bump_primary "$w" instr
|
|
265
|
+
base=$(primary_head_commit "$w/main")
|
|
266
|
+
|
|
267
|
+
fakebin="$w/fakebin"
|
|
268
|
+
log="$w/fetch.log"
|
|
269
|
+
real_git=$(command -v git)
|
|
270
|
+
mkdir -p "$fakebin"
|
|
271
|
+
cat > "$fakebin/git" <<SH
|
|
272
|
+
#!/usr/bin/env bash
|
|
273
|
+
for a in "\$@"; do
|
|
274
|
+
if [ "\$a" = fetch ]; then printf 'FETCH\n' >> '$log'; fi
|
|
275
|
+
done
|
|
276
|
+
exec '$real_git' "\$@"
|
|
277
|
+
SH
|
|
278
|
+
chmod +x "$fakebin/git"
|
|
279
|
+
|
|
280
|
+
PATH="$fakebin:$BASE_PATH" run_ff "$w/sm" "$base"
|
|
281
|
+
|
|
282
|
+
[ "$FF_STATUS" = updated ] || fail "FF_STATUS: expected updated, got '$FF_STATUS'"
|
|
283
|
+
[ ! -f "$log" ] || fail "git fetch was invoked in the local-HEAD sync path: $(cat "$log")"
|
|
284
|
+
pass "T6 no fetch: the local-HEAD sync never invokes git fetch"
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
# --- T7: sweep advances a readme-only home but does NOT nudge it -------------
|
|
288
|
+
test_sweep_nudge_requires_instruction_change() {
|
|
289
|
+
local w c1 base
|
|
290
|
+
w=$(new_world sweep-gate)
|
|
291
|
+
c1=$(head_of "$w/main")
|
|
292
|
+
add_sm_worktree "$w" sm-r "$c1"
|
|
293
|
+
bump_primary "$w" readme
|
|
294
|
+
base=$(primary_head_commit "$w/main")
|
|
295
|
+
|
|
296
|
+
FM_ROOT="$w/main" FM_HOME="$w/home"
|
|
297
|
+
FF_NUDGE_WINDOWS=""
|
|
298
|
+
FF_SEEN_HOMES=""
|
|
299
|
+
sweep_live_secondmate_metas "$w/home/state" "$base" yes >/dev/null
|
|
300
|
+
|
|
301
|
+
[ -z "$FF_NUDGE_WINDOWS" ] \
|
|
302
|
+
|| fail "readme-only advance must not nudge, got: '$FF_NUDGE_WINDOWS'"
|
|
303
|
+
[ "$(head_of "$w/sm-r")" = "$base" ] \
|
|
304
|
+
|| fail "home should still fast-forward even when it is not nudged"
|
|
305
|
+
pass "T7 sweep nudges on a real instruction change only, but still fast-forwards"
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
# --- T8: bootstrap sweeps live homes, nudges only the real instruction change -
|
|
309
|
+
make_fake_toolchain() {
|
|
310
|
+
local dir=$1 fakebin
|
|
311
|
+
fakebin="$dir/fakebin"
|
|
312
|
+
mkdir -p "$fakebin"
|
|
313
|
+
fm_fake_exit0 "$fakebin" node chrome-devtools-axi
|
|
314
|
+
fm_fake_version_tool "$fakebin" lavish-axi FM_FAKE_LAVISH_AXI_VERSION 0.1.46
|
|
315
|
+
cat > "$fakebin/gh-axi" <<'SH'
|
|
316
|
+
#!/usr/bin/env bash
|
|
317
|
+
if [ "${1:-}" = --version ]; then
|
|
318
|
+
printf '%s\n' '0.1.29'
|
|
319
|
+
exit 0
|
|
320
|
+
fi
|
|
321
|
+
exit 0
|
|
322
|
+
SH
|
|
323
|
+
chmod +x "$fakebin/gh-axi"
|
|
324
|
+
cat > "$fakebin/tmux" <<'SH'
|
|
325
|
+
#!/usr/bin/env bash
|
|
326
|
+
if [ -n "${FM_FAKE_TMUX_LOG:-}" ]; then
|
|
327
|
+
printf '%s\n' "$*" >> "$FM_FAKE_TMUX_LOG"
|
|
328
|
+
fi
|
|
329
|
+
case "$*" in
|
|
330
|
+
list-windows*)
|
|
331
|
+
sed -n 's/^window=[^:]*://p' "${FM_HOME:?}"/state/*.meta
|
|
332
|
+
exit 0
|
|
333
|
+
;;
|
|
334
|
+
*display-message*'#{pane_current_command}'*) printf '%s\n' codex; exit 0 ;;
|
|
335
|
+
*display-message*'#{pane_id}'*) printf '%s\n' '%1'; exit 0 ;;
|
|
336
|
+
*display-message*'#{cursor_y}'*) printf '%s\n' 0; exit 0 ;;
|
|
337
|
+
*capture-pane*) printf '❯\n'; exit 0 ;;
|
|
338
|
+
*'send-keys'*' -l '*)
|
|
339
|
+
[ "${FM_FAKE_TMUX_FAIL_LITERAL:-0}" = 1 ] && exit 1
|
|
340
|
+
exit 0
|
|
341
|
+
;;
|
|
342
|
+
esac
|
|
343
|
+
exit 0
|
|
344
|
+
SH
|
|
345
|
+
chmod +x "$fakebin/tmux"
|
|
346
|
+
cat > "$fakebin/gh" <<'SH'
|
|
347
|
+
#!/usr/bin/env bash
|
|
348
|
+
exit 0
|
|
349
|
+
SH
|
|
350
|
+
chmod +x "$fakebin/gh"
|
|
351
|
+
cat > "$fakebin/treehouse" <<'SH'
|
|
352
|
+
#!/usr/bin/env bash
|
|
353
|
+
if [ "${1:-}" = get ] && [ "${2:-}" = --help ]; then
|
|
354
|
+
printf '%s\n' 'Usage: treehouse get [--lease]'
|
|
355
|
+
fi
|
|
356
|
+
exit 0
|
|
357
|
+
SH
|
|
358
|
+
chmod +x "$fakebin/treehouse"
|
|
359
|
+
cat > "$fakebin/no-mistakes" <<'SH'
|
|
360
|
+
#!/usr/bin/env bash
|
|
361
|
+
if [ "${1:-}" = --version ]; then
|
|
362
|
+
printf '%s\n' 'no-mistakes version v1.46.0 (fake)'
|
|
363
|
+
exit 0
|
|
364
|
+
fi
|
|
365
|
+
exit 0
|
|
366
|
+
SH
|
|
367
|
+
chmod +x "$fakebin/no-mistakes"
|
|
368
|
+
cat > "$fakebin/tasks-axi" <<'SH'
|
|
369
|
+
#!/usr/bin/env bash
|
|
370
|
+
case "${1:-} ${2:-}" in
|
|
371
|
+
"--version ") printf '%s\n' '0.2.4' ;;
|
|
372
|
+
"update --help") printf '%s\n' 'usage: tasks-axi update <id> [flags]' ' --archive-body' ;;
|
|
373
|
+
"mv --help") printf '%s\n' 'usage: tasks-axi mv <id> [<id>...] --to <path-or-dir>' ;;
|
|
374
|
+
esac
|
|
375
|
+
exit 0
|
|
376
|
+
SH
|
|
377
|
+
chmod +x "$fakebin/tasks-axi"
|
|
378
|
+
cat > "$fakebin/quota-axi" <<'SH'
|
|
379
|
+
#!/usr/bin/env bash
|
|
380
|
+
if [ "${1:-}" = --version ]; then
|
|
381
|
+
printf '%s\n' 'quota-axi 0.1.29 (fake)'
|
|
382
|
+
fi
|
|
383
|
+
exit 0
|
|
384
|
+
SH
|
|
385
|
+
chmod +x "$fakebin/quota-axi"
|
|
386
|
+
printf '%s\n' "$fakebin"
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
add_real_jq() {
|
|
390
|
+
local fakebin=$1 real_jq
|
|
391
|
+
real_jq=$(command -v jq 2>/dev/null) || return 1
|
|
392
|
+
cat > "$fakebin/jq" <<SH
|
|
393
|
+
#!/usr/bin/env bash
|
|
394
|
+
exec '$real_jq' "\$@"
|
|
395
|
+
SH
|
|
396
|
+
chmod +x "$fakebin/jq"
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
test_bootstrap_sweep_nudges_only_instruction_change() {
|
|
400
|
+
local w c1 c2 c3 fakebin out info_line log marker_dir
|
|
401
|
+
w=$(new_world boot-sweep)
|
|
402
|
+
c1=$(head_of "$w/main")
|
|
403
|
+
add_sm_worktree "$w" sm-instr "$c1" # behind by an instruction change
|
|
404
|
+
bump_primary "$w" instr
|
|
405
|
+
c2=$(head_of "$w/main")
|
|
406
|
+
add_sm_worktree "$w" sm-readme "$c2" # behind by a readme-only change
|
|
407
|
+
bump_primary "$w" readme
|
|
408
|
+
c3=$(head_of "$w/main")
|
|
409
|
+
add_sm_worktree "$w" sm-current "$c3" # already on the primary's HEAD
|
|
410
|
+
# A home with NO live meta must never be swept (live = a running direct report).
|
|
411
|
+
git -C "$w/main" worktree add -q --detach "$w/sm-nonlive" "$c1"
|
|
412
|
+
printf 'sm-nonlive\n' > "$w/sm-nonlive/.fm-secondmate-home"
|
|
413
|
+
|
|
414
|
+
fakebin=$(make_fake_toolchain "$w")
|
|
415
|
+
log="$w/tmux.log"
|
|
416
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$w/home" FM_ROOT_OVERRIDE="$w/main" \
|
|
417
|
+
FM_SEND_SETTLE=0 FM_FAKE_TMUX_LOG="$log" \
|
|
418
|
+
"$ROOT/bin/fm-bootstrap.sh" 2>/dev/null)
|
|
419
|
+
|
|
420
|
+
info_line=$(printf '%s\n' "$out" | grep '^BOOTSTRAP_INFO: nudged fm-sm-instr ' || true)
|
|
421
|
+
[ -n "$info_line" ] || fail "no BOOTSTRAP_INFO nudge line emitted (got: $out)"
|
|
422
|
+
assert_contains "$info_line" "firstmate was updated to the latest - please re-read your AGENTS.md to pick up the new instructions." \
|
|
423
|
+
"successful nudge report should include the exact message sent"
|
|
424
|
+
assert_not_contains "$out" "NUDGE_SECONDMATES:" "successful nudge must not leave a firstmate action item"
|
|
425
|
+
assert_not_contains "$out" "sm-readme" "readme-only advance is not nudged"
|
|
426
|
+
assert_not_contains "$out" "sm-current" "already-current secondmate is not nudged"
|
|
427
|
+
# The nudge rides fm-send's durable inbox plane: the marked message lands in
|
|
428
|
+
# the secondmate task's steering-inbox record while only the doorbell is typed.
|
|
429
|
+
assert_contains "$(cat "$w/home/state/sm-instr.inbox/001.msg")" "[fm-from-firstmate]" \
|
|
430
|
+
"nudge send should use the marked fm-send secondmate path"
|
|
431
|
+
assert_contains "$(cat "$w/home/state/sm-instr.inbox/001.msg")" "firstmate was updated to the latest - please re-read your AGENTS.md" \
|
|
432
|
+
"nudge send should enqueue the exact re-read message"
|
|
433
|
+
assert_contains "$(cat "$log")" "Firstmate instruction waiting" \
|
|
434
|
+
"nudge send should ring the doorbell at the secondmate pane"
|
|
435
|
+
marker_dir="$w/home/state/.secondmate-nudge-pending"
|
|
436
|
+
[ ! -e "$marker_dir/sm-instr.pending" ] || fail "successful nudge should clear its retry marker"
|
|
437
|
+
|
|
438
|
+
# Every live home advanced to the primary's HEAD; the already-current one stayed.
|
|
439
|
+
[ "$(head_of "$w/sm-instr")" = "$c3" ] || fail "sm-instr not at primary HEAD"
|
|
440
|
+
[ "$(head_of "$w/sm-readme")" = "$c3" ] || fail "sm-readme not at primary HEAD"
|
|
441
|
+
[ "$(head_of "$w/sm-current")" = "$c3" ] || fail "sm-current moved off primary HEAD"
|
|
442
|
+
# The non-live home is never touched by the bootstrap sweep.
|
|
443
|
+
[ "$(head_of "$w/sm-nonlive")" = "$c1" ] || fail "a home with no live meta was swept"
|
|
444
|
+
pass "T8 bootstrap sweeps live homes and sends exactly one marked nudge for the instruction change"
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
test_bootstrap_nudge_send_uses_state_override() {
|
|
448
|
+
local w c1 fakebin out log override_state marker
|
|
449
|
+
w=$(new_world nudge-state-override)
|
|
450
|
+
c1=$(head_of "$w/main")
|
|
451
|
+
add_sm_worktree "$w" sm-instr "$c1"
|
|
452
|
+
bump_primary "$w" instr
|
|
453
|
+
override_state="$w/override-state"
|
|
454
|
+
mkdir -p "$override_state"
|
|
455
|
+
mv "$w/home/state/sm-instr.meta" "$override_state/sm-instr.meta"
|
|
456
|
+
touch "$override_state/.last-watcher-beat"
|
|
457
|
+
fakebin=$(make_fake_toolchain "$w")
|
|
458
|
+
log="$w/tmux.log"
|
|
459
|
+
|
|
460
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$w/home" FM_ROOT_OVERRIDE="$w/main" \
|
|
461
|
+
FM_STATE_OVERRIDE="$override_state" FM_SEND_SETTLE=0 FM_FAKE_TMUX_LOG="$log" \
|
|
462
|
+
"$ROOT/bin/fm-bootstrap.sh" 2>/dev/null)
|
|
463
|
+
|
|
464
|
+
assert_contains "$out" "BOOTSTRAP_INFO: nudged fm-sm-instr with" \
|
|
465
|
+
"nudge send should resolve fm-sm-instr through the effective state dir"
|
|
466
|
+
assert_not_contains "$out" "NUDGE_SECONDMATES:" \
|
|
467
|
+
"effective-state nudge should not fail through FM_HOME/state"
|
|
468
|
+
assert_contains "$(cat "$override_state/sm-instr.inbox/001.msg")" "[fm-from-firstmate]" \
|
|
469
|
+
"effective-state nudge should still use secondmate marker metadata"
|
|
470
|
+
marker="$override_state/.secondmate-nudge-pending/sm-instr.pending"
|
|
471
|
+
assert_absent "$marker" "successful effective-state nudge should clear its retry marker"
|
|
472
|
+
pass "T8a bootstrap nudge send respects FM_STATE_OVERRIDE"
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
test_bootstrap_nudge_retry_rejects_malformed_marker_id() {
|
|
476
|
+
local w c1 fakebin out marker log evil
|
|
477
|
+
w=$(new_world nudge-malformed-id)
|
|
478
|
+
c1=$(head_of "$w/main")
|
|
479
|
+
evil="$w/evil"
|
|
480
|
+
git -C "$w/main" worktree add -q --detach "$evil" "$c1"
|
|
481
|
+
printf '../escape\n' > "$evil/.fm-secondmate-home"
|
|
482
|
+
mkdir -p "$w/home/state/.secondmate-nudge-pending"
|
|
483
|
+
marker="$w/home/state/.secondmate-nudge-pending/bad.pending"
|
|
484
|
+
{
|
|
485
|
+
printf 'id=../escape\n'
|
|
486
|
+
printf 'selector=fm-../escape\n'
|
|
487
|
+
printf 'home=%s\n' "$evil"
|
|
488
|
+
printf 'commit=%s\n' "$c1"
|
|
489
|
+
printf 'instructions=AGENTS.md\n'
|
|
490
|
+
printf 'message=firstmate was updated to the latest - please re-read your AGENTS.md to pick up the new instructions.\n'
|
|
491
|
+
} > "$marker"
|
|
492
|
+
{
|
|
493
|
+
printf 'window=firstmate:fm-evil\n'
|
|
494
|
+
printf 'kind=secondmate\n'
|
|
495
|
+
printf 'home=%s\n' "$evil"
|
|
496
|
+
} > "$w/home/escape.meta"
|
|
497
|
+
fakebin=$(make_fake_toolchain "$w")
|
|
498
|
+
log="$w/tmux.log"
|
|
499
|
+
|
|
500
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$w/home" FM_ROOT_OVERRIDE="$w/main" \
|
|
501
|
+
FM_SEND_SETTLE=0 FM_FAKE_TMUX_LOG="$log" \
|
|
502
|
+
"$ROOT/bin/fm-bootstrap.sh" 2>/dev/null)
|
|
503
|
+
|
|
504
|
+
assert_contains "$out" "NUDGE_SECONDMATES: secondmate ../escape: send failed: retry marker has unsafe id" \
|
|
505
|
+
"malformed retry marker id should be rejected before target resolution"
|
|
506
|
+
assert_not_contains "$out" "BOOTSTRAP_INFO: nudged fm-../escape" \
|
|
507
|
+
"malformed retry marker id must never send through a path-traversed selector"
|
|
508
|
+
assert_present "$marker" "malformed retry marker should remain for operator inspection"
|
|
509
|
+
assert_absent "$log" "malformed retry marker should not invoke fm-send"
|
|
510
|
+
pass "T8f bootstrap nudge retry rejects malformed marker ids"
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
test_bootstrap_nudge_failure_records_retry_marker() {
|
|
514
|
+
local w c1 fakebin out marker
|
|
515
|
+
w=$(new_world nudge-failure)
|
|
516
|
+
c1=$(head_of "$w/main")
|
|
517
|
+
add_sm_worktree "$w" sm-instr "$c1"
|
|
518
|
+
bump_primary "$w" instr
|
|
519
|
+
fakebin=$(make_fake_toolchain "$w")
|
|
520
|
+
# A real local send failure on the inbox plane is an unwritable steer record
|
|
521
|
+
# (a keystroke failure alone no longer fails a durably enqueued nudge).
|
|
522
|
+
: > "$w/home/state/sm-instr.inbox"
|
|
523
|
+
|
|
524
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$w/home" FM_ROOT_OVERRIDE="$w/main" \
|
|
525
|
+
FM_SEND_SETTLE=0 \
|
|
526
|
+
"$ROOT/bin/fm-bootstrap.sh" 2>/dev/null)
|
|
527
|
+
|
|
528
|
+
assert_contains "$out" "NUDGE_SECONDMATES: secondmate sm-instr: send failed:" \
|
|
529
|
+
"failed nudge send should be surfaced as actionable bootstrap output"
|
|
530
|
+
marker="$w/home/state/.secondmate-nudge-pending/sm-instr.pending"
|
|
531
|
+
assert_present "$marker" "failed nudge should leave a retry marker"
|
|
532
|
+
assert_grep "selector=fm-sm-instr" "$marker" "retry marker should pin the stable selector"
|
|
533
|
+
assert_grep "message=firstmate was updated to the latest - please re-read your AGENTS.md to pick up the new instructions." \
|
|
534
|
+
"$marker" "retry marker should pin the exact message"
|
|
535
|
+
pass "T8c failed bootstrap nudge is surfaced and recorded for retry"
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
test_bootstrap_nudge_retry_is_idempotent() {
|
|
539
|
+
local w c1 fakebin out marker out2
|
|
540
|
+
w=$(new_world nudge-retry)
|
|
541
|
+
c1=$(head_of "$w/main")
|
|
542
|
+
add_sm_worktree "$w" sm-instr "$c1"
|
|
543
|
+
bump_primary "$w" instr
|
|
544
|
+
fakebin=$(make_fake_toolchain "$w")
|
|
545
|
+
: > "$w/home/state/sm-instr.inbox" # block the steer record: a real local failure
|
|
546
|
+
|
|
547
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$w/home" FM_ROOT_OVERRIDE="$w/main" \
|
|
548
|
+
FM_SEND_SETTLE=0 \
|
|
549
|
+
"$ROOT/bin/fm-bootstrap.sh" 2>/dev/null)
|
|
550
|
+
assert_contains "$out" "NUDGE_SECONDMATES: secondmate sm-instr: send failed:" \
|
|
551
|
+
"precondition: first nudge should fail"
|
|
552
|
+
marker="$w/home/state/.secondmate-nudge-pending/sm-instr.pending"
|
|
553
|
+
assert_present "$marker" "precondition: failed nudge should leave marker"
|
|
554
|
+
|
|
555
|
+
rm -f "$w/home/state/sm-instr.inbox" # unblock: the steer record can be written again
|
|
556
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$w/home" FM_ROOT_OVERRIDE="$w/main" \
|
|
557
|
+
FM_SEND_SETTLE=0 "$ROOT/bin/fm-bootstrap.sh" 2>/dev/null)
|
|
558
|
+
assert_contains "$out" "BOOTSTRAP_INFO: nudged fm-sm-instr with" \
|
|
559
|
+
"retry should send the pending nudge once the endpoint works"
|
|
560
|
+
assert_absent "$marker" "successful retry should clear the marker"
|
|
561
|
+
|
|
562
|
+
out2=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$w/home" FM_ROOT_OVERRIDE="$w/main" \
|
|
563
|
+
FM_SEND_SETTLE=0 "$ROOT/bin/fm-bootstrap.sh" 2>/dev/null)
|
|
564
|
+
[ -z "$out2" ] || fail "idempotent retry should converge to silence, got: $out2"
|
|
565
|
+
pass "T8d bootstrap nudge retry is idempotent after success"
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
test_bootstrap_nudge_retry_refuses_changed_home() {
|
|
569
|
+
local w c1 fakebin marker out other
|
|
570
|
+
w=$(new_world nudge-retry-home-change)
|
|
571
|
+
c1=$(head_of "$w/main")
|
|
572
|
+
add_sm_worktree "$w" sm-instr "$c1"
|
|
573
|
+
bump_primary "$w" instr
|
|
574
|
+
fakebin=$(make_fake_toolchain "$w")
|
|
575
|
+
: > "$w/home/state/sm-instr.inbox" # block the steer record: a real local failure
|
|
576
|
+
|
|
577
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$w/home" FM_ROOT_OVERRIDE="$w/main" \
|
|
578
|
+
FM_SEND_SETTLE=0 \
|
|
579
|
+
"$ROOT/bin/fm-bootstrap.sh" 2>/dev/null)
|
|
580
|
+
assert_contains "$out" "NUDGE_SECONDMATES: secondmate sm-instr: send failed:" \
|
|
581
|
+
"precondition: first nudge should fail"
|
|
582
|
+
marker="$w/home/state/.secondmate-nudge-pending/sm-instr.pending"
|
|
583
|
+
assert_present "$marker" "precondition: failed nudge should leave marker"
|
|
584
|
+
|
|
585
|
+
other="$w/sm-other"
|
|
586
|
+
git -C "$w/main" worktree add -q --detach "$other" "$(head_of "$w/sm-instr")"
|
|
587
|
+
printf '%s\n' sm-instr > "$other/.fm-secondmate-home"
|
|
588
|
+
sed -i.bak "s|^home=.*|home=$other|" "$w/home/state/sm-instr.meta" 2>/dev/null || \
|
|
589
|
+
sed -i "s|^home=.*|home=$other|" "$w/home/state/sm-instr.meta"
|
|
590
|
+
rm -f "$w/home/state/sm-instr.meta.bak"
|
|
591
|
+
|
|
592
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$w/home" FM_ROOT_OVERRIDE="$w/main" \
|
|
593
|
+
FM_SEND_SETTLE=0 "$ROOT/bin/fm-bootstrap.sh" 2>/dev/null)
|
|
594
|
+
assert_contains "$out" "NUDGE_SECONDMATES: secondmate sm-instr: send failed: retry target home changed" \
|
|
595
|
+
"retry must not infer a nudge target outside the recorded failed home"
|
|
596
|
+
assert_present "$marker" "ambiguous retry should keep marker for operator inspection"
|
|
597
|
+
pass "T8e bootstrap nudge retry refuses a changed home instead of guessing"
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
# --- T8b: stale herdr nudge failures retry through current fm-<id> metadata ---
|
|
601
|
+
# Reproduces the 2026-07-07 session-start bug: secondmate_sync used to print raw
|
|
602
|
+
# backend targets (default:w9:pY) that liveness respawn immediately replaced
|
|
603
|
+
# (default:wA:p2), so fm-send with the printed target fell back to tmux and failed
|
|
604
|
+
# while fm-<id> resolved through current meta.
|
|
605
|
+
make_nudge_herdr_fake() {
|
|
606
|
+
local dir=$1 stale=$2 fresh=$3 fakebin
|
|
607
|
+
fakebin=$(fm_fakebin "$dir")
|
|
608
|
+
cat > "$fakebin/herdr" <<SH
|
|
609
|
+
#!/usr/bin/env bash
|
|
610
|
+
set -u
|
|
611
|
+
cmd=\${1:-}; sub=\${2:-}; arg=\${3:-}
|
|
612
|
+
case "\$cmd \$sub" in
|
|
613
|
+
"status --json")
|
|
614
|
+
printf '{"client":{"version":"0.7.1","protocol":14},"server":{"running":true}}\n'
|
|
615
|
+
;;
|
|
616
|
+
"pane get")
|
|
617
|
+
if [ "\$arg" = "${stale#*:}" ]; then
|
|
618
|
+
printf '{"result":{"pane":{"pane_id":"${stale#*:}"}}}\n'
|
|
619
|
+
elif [ "\$arg" = "${fresh#*:}" ]; then
|
|
620
|
+
printf '{"result":{"pane":{"pane_id":"${fresh#*:}"}}}\n'
|
|
621
|
+
else
|
|
622
|
+
printf '{"error":{"code":"pane_not_found","message":"missing"}}\n' >&2
|
|
623
|
+
exit 0
|
|
624
|
+
fi
|
|
625
|
+
;;
|
|
626
|
+
"agent get")
|
|
627
|
+
if [ "\$arg" = "${stale#*:}" ]; then
|
|
628
|
+
printf '{"error":{"code":"agent_not_found","message":"gone"}}\n' >&2
|
|
629
|
+
elif [ "\$arg" = "${fresh#*:}" ]; then
|
|
630
|
+
printf '{"result":{"agent":{"agent_status":"idle"}}}\n'
|
|
631
|
+
else
|
|
632
|
+
printf '{"error":{"code":"agent_not_found","message":"gone"}}\n' >&2
|
|
633
|
+
fi
|
|
634
|
+
;;
|
|
635
|
+
"pane send-text"|"pane run"|"pane send-keys")
|
|
636
|
+
if [ "\$arg" = "${stale#*:}" ]; then
|
|
637
|
+
exit 1
|
|
638
|
+
fi
|
|
639
|
+
exit 0
|
|
640
|
+
;;
|
|
641
|
+
esac
|
|
642
|
+
exit 0
|
|
643
|
+
SH
|
|
644
|
+
chmod +x "$fakebin/herdr"
|
|
645
|
+
printf '%s\n' "$fakebin"
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
test_nudge_retry_uses_fresh_herdr_endpoint_after_respawn() {
|
|
649
|
+
local w c1 stale fresh fakebin herdrfb toolchain out meta window resolved stale_send fresh_send spawn_stub marker
|
|
650
|
+
stale=default:w9:pY
|
|
651
|
+
fresh=default:wA:p2
|
|
652
|
+
w=$(new_world nudge-herdr-rotate)
|
|
653
|
+
c1=$(head_of "$w/main")
|
|
654
|
+
add_sm_worktree "$w" sm-instr "$c1"
|
|
655
|
+
bump_primary "$w" instr
|
|
656
|
+
|
|
657
|
+
meta="$w/home/state/sm-instr.meta"
|
|
658
|
+
{
|
|
659
|
+
printf 'window=%s\n' "$stale"
|
|
660
|
+
printf 'backend=herdr\n'
|
|
661
|
+
printf 'kind=secondmate\n'
|
|
662
|
+
printf 'harness=claude\n'
|
|
663
|
+
printf 'home=%s/sm-instr\n' "$w"
|
|
664
|
+
} > "$meta"
|
|
665
|
+
|
|
666
|
+
spawn_stub="$w/spawn-stub.sh"
|
|
667
|
+
cat > "$spawn_stub" <<SH
|
|
668
|
+
#!/usr/bin/env bash
|
|
669
|
+
set -u
|
|
670
|
+
id=\${1:-}
|
|
671
|
+
meta="\$FM_HOME/state/\$id.meta"
|
|
672
|
+
[ -f "\$meta" ] || exit 1
|
|
673
|
+
sed -i.bak "s/^window=.*/window=$fresh/" "\$meta" 2>/dev/null || \
|
|
674
|
+
sed -i "s/^window=.*/window=$fresh/" "\$meta"
|
|
675
|
+
rm -f "\$meta.bak"
|
|
676
|
+
exit 0
|
|
677
|
+
SH
|
|
678
|
+
chmod +x "$spawn_stub"
|
|
679
|
+
cp "$spawn_stub" "$w/main/bin/fm-spawn.sh"
|
|
680
|
+
|
|
681
|
+
herdrfb=$(make_nudge_herdr_fake "$w/herdr" "$stale" "$fresh")
|
|
682
|
+
toolchain=$(make_fake_toolchain "$w")
|
|
683
|
+
if ! add_real_jq "$toolchain"; then
|
|
684
|
+
pass "T8b nudge selector herdr respawn skipped without jq"
|
|
685
|
+
return
|
|
686
|
+
fi
|
|
687
|
+
out=$(PATH="$herdrfb:$toolchain:$BASE_PATH" HERDR_ENV=1 FM_BACKEND=herdr \
|
|
688
|
+
FM_SEND_SETTLE=0 \
|
|
689
|
+
FM_HOME="$w/home" FM_ROOT_OVERRIDE="$w/main" \
|
|
690
|
+
"$ROOT/bin/fm-bootstrap.sh" 2>/dev/null)
|
|
691
|
+
|
|
692
|
+
# The nudge now rides the durable inbox: a stale endpoint can only swallow
|
|
693
|
+
# the best-effort doorbell, never the steer itself, so the nudge is SENT
|
|
694
|
+
# (recorded) rather than failed, no retry marker is owed, and the watcher's
|
|
695
|
+
# re-ring ladder owns delivery against the respawned fresh endpoint.
|
|
696
|
+
assert_contains "$out" "BOOTSTRAP_INFO: nudged fm-sm-instr" \
|
|
697
|
+
"a stale herdr endpoint must not fail a durably enqueued nudge"
|
|
698
|
+
assert_contains "$(cat "$w/home/state/sm-instr.inbox/001.msg")" \
|
|
699
|
+
"please re-read your AGENTS.md" \
|
|
700
|
+
"the nudge should be durably recorded despite the stale endpoint"
|
|
701
|
+
|
|
702
|
+
window=$(grep '^window=' "$meta" | tail -1 | cut -d= -f2-)
|
|
703
|
+
[ "$window" = "$fresh" ] || fail "respawn stub did not rotate meta window to '$fresh' (got '$window')"
|
|
704
|
+
marker="$w/home/state/.secondmate-nudge-pending/sm-instr.pending"
|
|
705
|
+
assert_absent "$marker" "a durably enqueued nudge owes no retry marker"
|
|
706
|
+
|
|
707
|
+
# shellcheck disable=SC2016 # $0/$1 belong to the inner bash -c process.
|
|
708
|
+
resolved=$(bash -c '. "$0/bin/fm-backend.sh"; fm_backend_resolve_selector fm-sm-instr "$1"' "$ROOT" "$w/home/state")
|
|
709
|
+
[ "$resolved" = "$fresh" ] || fail "fm-<id> should resolve through post-respawn meta, got '$resolved'"
|
|
710
|
+
|
|
711
|
+
# shellcheck disable=SC2016 # $0/$1 belong to the inner bash -c process.
|
|
712
|
+
stale_send=$(PATH="$herdrfb:$toolchain:$BASE_PATH" bash -c \
|
|
713
|
+
'. "$0/bin/fm-backend.sh"; fm_backend_source herdr; fm_backend_herdr_send_literal "$1" "nudge"' "$ROOT" "$stale" 2>/dev/null; printf '%s' "$?")
|
|
714
|
+
[ "$stale_send" != 0 ] || fail "explicit stale herdr endpoint send should fail"
|
|
715
|
+
|
|
716
|
+
# shellcheck disable=SC2016 # $0/$1 belong to the inner bash -c process.
|
|
717
|
+
fresh_send=$(PATH="$herdrfb:$toolchain:$BASE_PATH" bash -c \
|
|
718
|
+
'. "$0/bin/fm-backend.sh"; fm_backend_source herdr; fm_backend_herdr_send_literal "$1" "nudge"' "$ROOT" "$fresh" 2>/dev/null; printf '%s' "$?")
|
|
719
|
+
[ "$fresh_send" = 0 ] || fail "send through fm-<id>-resolved fresh endpoint should succeed"
|
|
720
|
+
|
|
721
|
+
pass "T8b a stale herdr endpoint cannot lose a durably enqueued nudge, and fm-<id> resolves through post-respawn metadata"
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
# --- T9: bootstrap surfaces a skipped dirty live secondmate home --------------
|
|
725
|
+
test_bootstrap_sweep_surfaces_skipped_home() {
|
|
726
|
+
local w c1 base before fakebin out skip_line
|
|
727
|
+
w=$(new_world boot-skip)
|
|
728
|
+
c1=$(head_of "$w/main")
|
|
729
|
+
add_sm_worktree "$w" sm-dirty "$c1"
|
|
730
|
+
bump_primary "$w" instr
|
|
731
|
+
base=$(primary_head_commit "$w/main")
|
|
732
|
+
printf 'uncommitted local edit\n' >> "$w/sm-dirty/AGENTS.md"
|
|
733
|
+
before=$(head_of "$w/sm-dirty")
|
|
734
|
+
|
|
735
|
+
fakebin=$(make_fake_toolchain "$w")
|
|
736
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$w/home" FM_ROOT_OVERRIDE="$w/main" \
|
|
737
|
+
"$ROOT/bin/fm-bootstrap.sh" 2>/dev/null)
|
|
738
|
+
|
|
739
|
+
skip_line=$(printf '%s\n' "$out" | grep '^SECONDMATE_SYNC: secondmate sm-dirty: skipped:' || true)
|
|
740
|
+
[ -n "$skip_line" ] || fail "no SECONDMATE_SYNC skip line emitted (got: $out)"
|
|
741
|
+
assert_contains "$skip_line" "dirty working tree" "dirty skipped home reports the actionable reason"
|
|
742
|
+
[ "$(head_of "$w/sm-dirty")" = "$before" ] || fail "dirty home HEAD moved"
|
|
743
|
+
[ "$(head_of "$w/main")" = "$base" ] || fail "primary HEAD changed during bootstrap"
|
|
744
|
+
grep -q 'uncommitted local edit' "$w/sm-dirty/AGENTS.md" || fail "dirty edit was discarded"
|
|
745
|
+
pass "T9 bootstrap surfaces a skipped dirty live secondmate home"
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
# --- T10: spawning a secondmate fast-forwards its worktree before launch ------
|
|
749
|
+
test_spawn_fast_forwards_before_launch() {
|
|
750
|
+
local w c1 c2 fakebin
|
|
751
|
+
w=$(new_world spawn-ff)
|
|
752
|
+
c1=$(head_of "$w/main")
|
|
753
|
+
git -C "$w/main" worktree add -q --detach "$w/sm" "$c1"
|
|
754
|
+
printf 'sm\n' > "$w/sm/.fm-secondmate-home"
|
|
755
|
+
mkdir -p "$w/sm/data"
|
|
756
|
+
printf 'charter\n' > "$w/sm/data/charter.md"
|
|
757
|
+
bump_primary "$w" instr
|
|
758
|
+
c2=$(head_of "$w/main")
|
|
759
|
+
[ "$(head_of "$w/sm")" = "$c1" ] || fail "precondition: home should start behind the primary"
|
|
760
|
+
|
|
761
|
+
# tmux stub: accept every subcommand, print nothing (so no window pre-exists).
|
|
762
|
+
fakebin="$w/fakebin"
|
|
763
|
+
mkdir -p "$fakebin"
|
|
764
|
+
cat > "$fakebin/tmux" <<'SH'
|
|
765
|
+
#!/usr/bin/env bash
|
|
766
|
+
exit 0
|
|
767
|
+
SH
|
|
768
|
+
chmod +x "$fakebin/tmux"
|
|
769
|
+
|
|
770
|
+
PATH="$fakebin:$BASE_PATH" TMUX='' \
|
|
771
|
+
FM_ROOT_OVERRIDE="$w/main" FM_HOME="$w/home" \
|
|
772
|
+
FM_STATE_OVERRIDE="$w/home/state" FM_DATA_OVERRIDE="$w/home/data" \
|
|
773
|
+
FM_PROJECTS_OVERRIDE="$w/home/projects" FM_CONFIG_OVERRIDE="$w/home/config" \
|
|
774
|
+
FM_SPAWN_NO_GUARD=1 \
|
|
775
|
+
"$ROOT/bin/fm-spawn.sh" sm "$w/sm" codex --secondmate >/dev/null 2>&1 || true
|
|
776
|
+
|
|
777
|
+
[ "$(head_of "$w/sm")" = "$c2" ] \
|
|
778
|
+
|| fail "spawn did not fast-forward the secondmate worktree to the primary's HEAD"
|
|
779
|
+
pass "T10 spawn fast-forwards a secondmate worktree to the primary's local HEAD before launch"
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
# --- T11: spawn warns when pre-launch sync is skipped ------------------------
|
|
783
|
+
test_spawn_warns_when_sync_skipped_before_launch() {
|
|
784
|
+
local w c1 before fakebin err
|
|
785
|
+
w=$(new_world spawn-skip)
|
|
786
|
+
c1=$(head_of "$w/main")
|
|
787
|
+
git -C "$w/main" worktree add -q --detach "$w/sm" "$c1"
|
|
788
|
+
printf 'sm\n' > "$w/sm/.fm-secondmate-home"
|
|
789
|
+
mkdir -p "$w/sm/data"
|
|
790
|
+
printf 'charter\n' > "$w/sm/data/charter.md"
|
|
791
|
+
bump_primary "$w" instr
|
|
792
|
+
printf 'uncommitted local edit\n' >> "$w/sm/AGENTS.md"
|
|
793
|
+
before=$(head_of "$w/sm")
|
|
794
|
+
|
|
795
|
+
fakebin="$w/fakebin"
|
|
796
|
+
err="$w/spawn.err"
|
|
797
|
+
mkdir -p "$fakebin"
|
|
798
|
+
cat > "$fakebin/tmux" <<'SH'
|
|
799
|
+
#!/usr/bin/env bash
|
|
800
|
+
exit 0
|
|
801
|
+
SH
|
|
802
|
+
chmod +x "$fakebin/tmux"
|
|
803
|
+
|
|
804
|
+
PATH="$fakebin:$BASE_PATH" TMUX='' \
|
|
805
|
+
FM_ROOT_OVERRIDE="$w/main" FM_HOME="$w/home" \
|
|
806
|
+
FM_STATE_OVERRIDE="$w/home/state" FM_DATA_OVERRIDE="$w/home/data" \
|
|
807
|
+
FM_PROJECTS_OVERRIDE="$w/home/projects" FM_CONFIG_OVERRIDE="$w/home/config" \
|
|
808
|
+
FM_SPAWN_NO_GUARD=1 \
|
|
809
|
+
"$ROOT/bin/fm-spawn.sh" sm "$w/sm" codex --secondmate >/dev/null 2>"$err" || true
|
|
810
|
+
|
|
811
|
+
assert_contains "$(cat "$err")" \
|
|
812
|
+
"warning: secondmate sm sync skipped before launch: dirty working tree" \
|
|
813
|
+
"spawn warning reports the skipped sync reason"
|
|
814
|
+
[ "$(head_of "$w/sm")" = "$before" ] || fail "dirty spawn home HEAD moved"
|
|
815
|
+
grep -q 'uncommitted local edit' "$w/sm/AGENTS.md" || fail "dirty spawn edit was discarded"
|
|
816
|
+
pass "T11 spawn warns when pre-launch sync is skipped"
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
# --- T12: a freshly seeded home reads clean once the primary ignores the marker -
|
|
820
|
+
# The seed marker used to leave every home permanently dirty: bin/fm-fleet-sync.sh
|
|
821
|
+
# and any other plain `git status --porcelain` check counts the untracked marker,
|
|
822
|
+
# so a seeded home reported STUCK/dirty forever. With the marker in .gitignore, a
|
|
823
|
+
# home seeded from a primary that carries the fix reads clean to that exact signal.
|
|
824
|
+
test_seed_marker_clean_when_gitignored() {
|
|
825
|
+
local w base
|
|
826
|
+
w=$(new_world marker-clean)
|
|
827
|
+
ignore_marker_commit "$w" # primary now ignores the marker
|
|
828
|
+
base=$(primary_head_commit "$w/main")
|
|
829
|
+
seed_marked_home "$w" sm "$base" # fresh home at the post-fix HEAD
|
|
830
|
+
|
|
831
|
+
# The exact dirtiness signal bin/fm-fleet-sync.sh reads (its line: dirty=yes when
|
|
832
|
+
# `git status --porcelain | head -1` is non-empty).
|
|
833
|
+
[ -z "$(git -C "$w/sm" status --porcelain)" ] \
|
|
834
|
+
|| fail "seed marker still dirties a fresh home: $(git -C "$w/sm" status --porcelain)"
|
|
835
|
+
# And the secondmate ff sweep sees no dirt: an at-HEAD home is a clean no-op.
|
|
836
|
+
run_ff "$w/sm" "$base"
|
|
837
|
+
[ "$FF_STATUS" = current ] || fail "fresh home not read as clean/current, got '$FF_STATUS': $FF_OUT"
|
|
838
|
+
pass "T12 gitignored marker: a freshly seeded home reads clean to fleet-sync and the ff sweep"
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
# --- T13: an existing marker-only-dirty home converges on the next sweep --------
|
|
842
|
+
# The convergence chicken-and-egg: existing homes predate the fix, so their marker
|
|
843
|
+
# is still untracked-and-unignored, and the fix itself only arrives by fast-forward.
|
|
844
|
+
# The marker-tolerant ff-skip (ignore_seed_marker=yes) bridges the gap for
|
|
845
|
+
# linked-worktree homes, which bootstrap/spawn fast-forward from the primary's local HEAD.
|
|
846
|
+
# Standalone-clone homes converge through /updatefirstmate's origin fetch instead.
|
|
847
|
+
# Once advanced, the now-ignored marker reads clean with no hand intervention.
|
|
848
|
+
test_seed_marker_converges_existing_home() {
|
|
849
|
+
local w c0 base
|
|
850
|
+
w=$(new_world marker-converge) # primary does NOT ignore the marker yet
|
|
851
|
+
c0=$(head_of "$w/main")
|
|
852
|
+
seed_marked_home "$w" sm "$c0" # existing home predates the fix
|
|
853
|
+
[ -n "$(git -C "$w/sm" status --porcelain)" ] \
|
|
854
|
+
|| fail "precondition: the untracked marker should dirty a pre-fix home"
|
|
855
|
+
ignore_marker_commit "$w" # THE FIX lands as a later commit
|
|
856
|
+
base=$(primary_head_commit "$w/main")
|
|
857
|
+
|
|
858
|
+
run_ff "$w/sm" "$base" # the marker-tolerant convergence sweep
|
|
859
|
+
|
|
860
|
+
[ "$FF_STATUS" = updated ] || fail "existing marker-only home did not converge, got '$FF_STATUS': $FF_OUT"
|
|
861
|
+
[ "$(head_of "$w/sm")" = "$base" ] || fail "home did not fast-forward to the fix commit"
|
|
862
|
+
[ -z "$(git -C "$w/sm" status --porcelain)" ] \
|
|
863
|
+
|| fail "marker still dirty after convergence: $(git -C "$w/sm" status --porcelain)"
|
|
864
|
+
pass "T13 gitignored marker: an existing marker-only-dirty home converges, then reads clean"
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
# --- T14: marker tolerance does not mask a genuinely dirty home -----------------
|
|
868
|
+
# The ff-skip only forgives the seed marker; a real uncommitted change alongside the
|
|
869
|
+
# marker must still refuse the fast-forward and leave the work untouched, exactly as
|
|
870
|
+
# before this fix.
|
|
871
|
+
test_seed_marker_does_not_mask_real_dirt() {
|
|
872
|
+
local w c0 base before
|
|
873
|
+
w=$(new_world marker-real-dirt)
|
|
874
|
+
c0=$(head_of "$w/main")
|
|
875
|
+
seed_marked_home "$w" sm "$c0"
|
|
876
|
+
printf 'real local change\n' >> "$w/sm/AGENTS.md" # genuine tracked-file edit + the marker
|
|
877
|
+
before=$(head_of "$w/sm")
|
|
878
|
+
ignore_marker_commit "$w"
|
|
879
|
+
base=$(primary_head_commit "$w/main")
|
|
880
|
+
|
|
881
|
+
run_ff "$w/sm" "$base"
|
|
882
|
+
|
|
883
|
+
[ "$FF_STATUS" = skipped ] || fail "a genuinely dirty home must skip, got '$FF_STATUS'"
|
|
884
|
+
assert_contains "$FF_OUT" "secondmate sm: skipped: dirty working tree" \
|
|
885
|
+
"a genuinely dirty home is skipped even with the marker present"
|
|
886
|
+
[ "$(head_of "$w/sm")" = "$before" ] || fail "genuinely dirty home HEAD moved (work at risk)"
|
|
887
|
+
grep -q 'real local change' "$w/sm/AGENTS.md" || fail "genuine local edit was discarded"
|
|
888
|
+
pass "T14 marker tolerance does not mask a genuinely dirty home"
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
test_ff_updated
|
|
892
|
+
test_ff_current
|
|
893
|
+
test_ff_dirty
|
|
894
|
+
test_scratchpad2_does_not_dirty_home
|
|
895
|
+
test_ff_diverged
|
|
896
|
+
test_ff_inflight_feature_branch
|
|
897
|
+
test_no_fetch_in_local_path
|
|
898
|
+
test_sweep_nudge_requires_instruction_change
|
|
899
|
+
test_bootstrap_sweep_nudges_only_instruction_change
|
|
900
|
+
test_bootstrap_nudge_send_uses_state_override
|
|
901
|
+
test_bootstrap_nudge_retry_rejects_malformed_marker_id
|
|
902
|
+
test_bootstrap_nudge_failure_records_retry_marker
|
|
903
|
+
test_bootstrap_nudge_retry_is_idempotent
|
|
904
|
+
test_bootstrap_nudge_retry_refuses_changed_home
|
|
905
|
+
test_nudge_retry_uses_fresh_herdr_endpoint_after_respawn
|
|
906
|
+
test_bootstrap_sweep_surfaces_skipped_home
|
|
907
|
+
test_spawn_fast_forwards_before_launch
|
|
908
|
+
test_spawn_warns_when_sync_skipped_before_launch
|
|
909
|
+
test_seed_marker_clean_when_gitignored
|
|
910
|
+
test_seed_marker_converges_existing_home
|
|
911
|
+
test_seed_marker_does_not_mask_real_dirt
|
|
912
|
+
|
|
913
|
+
echo "# all fm-secondmate-sync tests passed"
|