@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,1178 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Behavior tests for fm-bootstrap.sh reporting and session-start clone refresh bounds.
|
|
3
|
+
#
|
|
4
|
+
# Bootstrap prints one block or line per actionable problem, optional verbose
|
|
5
|
+
# BOOTSTRAP_INFO fact, or completed bootstrap no-action fact and is silent when
|
|
6
|
+
# all is well. firstmate consumes the exact 'MISSING: treehouse (install: ...)',
|
|
7
|
+
# 'MISSING: tasks-axi (install: ...)', 'MISSING: quota-axi (install: ...)',
|
|
8
|
+
# 'MISSING: gh-axi (install: ...)', 'MISSING: lavish-axi (install: ...)', and
|
|
9
|
+
# 'BOOTSTRAP_INFO: ...' lines, so those contracts are pinned verbatim. The cases
|
|
10
|
+
# are table-driven over the inputs that vary: whether `treehouse get --help`
|
|
11
|
+
# advertises --lease, which (if any) tasks-axi version is on PATH, whether
|
|
12
|
+
# tasks-axi update advertises --archive-body, whether its mv help advertises
|
|
13
|
+
# multi-ID moves, whether quota-axi is on PATH,
|
|
14
|
+
# whether the local backend config opts out of tasks-axi backlog mutations,
|
|
15
|
+
# which no-mistakes version is on PATH, which gh-axi version is on PATH, and
|
|
16
|
+
# which lavish-axi version is on PATH.
|
|
17
|
+
# Dedicated fleet-sync cases pin the computed bootstrap timeout, explicit
|
|
18
|
+
# override, blank-env defaulting, partial-output relay, and pre-launch timeout
|
|
19
|
+
# scan.
|
|
20
|
+
# Dedicated network-phase cases pin FM_BOOTSTRAP_NETWORK as a true partition of
|
|
21
|
+
# one run into its local and network halves, and the one-hop tasks-axi
|
|
22
|
+
# compatibility handoff that keeps a session start from paying for that verdict
|
|
23
|
+
# twice.
|
|
24
|
+
set -u
|
|
25
|
+
|
|
26
|
+
# shellcheck source=tests/lib.sh disable=SC1091
|
|
27
|
+
. "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
|
28
|
+
|
|
29
|
+
BASE_PATH=${FM_TEST_BASE_PATH:-/usr/bin:/bin:/usr/sbin:/sbin}
|
|
30
|
+
TMP_ROOT=$(fm_test_tmproot fm-bootstrap-tests)
|
|
31
|
+
export FM_BACKEND_CMUX_BUNDLE_BIN="$TMP_ROOT/no-bundled-cmux"
|
|
32
|
+
|
|
33
|
+
# Hermetic runtime-backend detection. These cases pin the backend per-home via
|
|
34
|
+
# config/backend; the dev shell's ambient runtime markers ($TMUX inside tmux,
|
|
35
|
+
# HERDR_ENV inside herdr, CMUX_* inside a cmux terminal) must not leak into
|
|
36
|
+
# fm_backend_name and flip a default-backend case onto a non-tmux backend. Unset
|
|
37
|
+
# them once so the suite resolves the tmux reference backend unless a case says
|
|
38
|
+
# otherwise - the same hermeticity discipline as pinning PATH via BASE_PATH.
|
|
39
|
+
unset TMUX TMUX_PANE HERDR_ENV HERDR_PANE_ID HERDR_SESSION HERDR_SOCKET_PATH \
|
|
40
|
+
CMUX_WORKSPACE_ID CMUX_SURFACE_ID CMUX_SOCKET_PATH CMUX_TAB_ID CMUX_PANEL_ID 2>/dev/null || true
|
|
41
|
+
|
|
42
|
+
# A fake toolchain where every required tool is present and gh is authenticated.
|
|
43
|
+
# treehouse's `get --help` advertises --lease only when FM_FAKE_TREEHOUSE_LEASE_HELP=1.
|
|
44
|
+
make_fake_toolchain() {
|
|
45
|
+
local dir=$1 fakebin
|
|
46
|
+
fakebin=$(fm_fakebin "$dir")
|
|
47
|
+
fm_fake_exit0 "$fakebin" tmux node chrome-devtools-axi
|
|
48
|
+
fm_fake_version_tool "$fakebin" lavish-axi FM_FAKE_LAVISH_AXI_VERSION 0.1.46
|
|
49
|
+
cat > "$fakebin/gh-axi" <<'SH'
|
|
50
|
+
#!/usr/bin/env bash
|
|
51
|
+
if [ "${1:-}" = --version ]; then
|
|
52
|
+
printf '%s\n' "${FM_FAKE_GH_AXI_VERSION:-0.1.29}"
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
exit 0
|
|
56
|
+
SH
|
|
57
|
+
chmod +x "$fakebin/gh-axi"
|
|
58
|
+
cat > "$fakebin/gh" <<'SH'
|
|
59
|
+
#!/usr/bin/env bash
|
|
60
|
+
if [ "${1:-}" = auth ] && [ "${2:-}" = status ]; then
|
|
61
|
+
exit 0
|
|
62
|
+
fi
|
|
63
|
+
exit 0
|
|
64
|
+
SH
|
|
65
|
+
chmod +x "$fakebin/gh"
|
|
66
|
+
cat > "$fakebin/treehouse" <<'SH'
|
|
67
|
+
#!/usr/bin/env bash
|
|
68
|
+
if [ "${1:-}" = get ] && [ "${2:-}" = --help ]; then
|
|
69
|
+
if [ "${FM_FAKE_TREEHOUSE_LEASE_HELP:-}" = 1 ]; then
|
|
70
|
+
printf '%s\n' 'Usage: treehouse get [--lease] [--lease-holder <holder>]'
|
|
71
|
+
else
|
|
72
|
+
printf '%s\n' 'Usage: treehouse get'
|
|
73
|
+
fi
|
|
74
|
+
exit 0
|
|
75
|
+
fi
|
|
76
|
+
exit 0
|
|
77
|
+
SH
|
|
78
|
+
chmod +x "$fakebin/treehouse"
|
|
79
|
+
cat > "$fakebin/no-mistakes" <<'SH'
|
|
80
|
+
#!/usr/bin/env bash
|
|
81
|
+
if [ "${1:-}" = --version ]; then
|
|
82
|
+
printf '%s\n' "${FM_FAKE_NO_MISTAKES_VERSION:-no-mistakes version v1.46.0 (fake) 2026-06-27T00:02:18Z}"
|
|
83
|
+
exit 0
|
|
84
|
+
fi
|
|
85
|
+
exit 0
|
|
86
|
+
SH
|
|
87
|
+
chmod +x "$fakebin/no-mistakes"
|
|
88
|
+
add_tasks_axi "$fakebin" "0.2.4"
|
|
89
|
+
add_quota_axi "$fakebin"
|
|
90
|
+
printf '%s\n' "$fakebin"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
add_quota_axi() {
|
|
94
|
+
local fakebin=$1
|
|
95
|
+
cat > "$fakebin/quota-axi" <<'SH'
|
|
96
|
+
#!/usr/bin/env bash
|
|
97
|
+
if [ "${1:-}" = --version ]; then
|
|
98
|
+
printf '%s\n' "${FM_FAKE_QUOTA_AXI_VERSION:-0.1.29}"
|
|
99
|
+
exit 0
|
|
100
|
+
fi
|
|
101
|
+
exit 0
|
|
102
|
+
SH
|
|
103
|
+
chmod +x "$fakebin/quota-axi"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
add_tasks_axi() {
|
|
107
|
+
local fakebin=$1 version=$2 archive_body=${3:-yes} multi_id=${4:-yes} archive_line mv_usage
|
|
108
|
+
archive_line=""
|
|
109
|
+
[ "$archive_body" = yes ] && archive_line=' --archive-body'
|
|
110
|
+
mv_usage='usage: tasks-axi mv <id> [<id>...] --to <path-or-dir>'
|
|
111
|
+
[ "$multi_id" = yes ] || mv_usage='usage: tasks-axi mv <id> --to <path-or-dir>'
|
|
112
|
+
cat > "$fakebin/tasks-axi" <<SH
|
|
113
|
+
#!/usr/bin/env bash
|
|
114
|
+
if [ "\${1:-}" = --version ]; then
|
|
115
|
+
printf '%s\n' '$version'
|
|
116
|
+
exit 0
|
|
117
|
+
fi
|
|
118
|
+
if [ "\${1:-}" = update ] && [ "\${2:-}" = --help ]; then
|
|
119
|
+
printf '%s\n' 'usage: tasks-axi update <id> [flags]'
|
|
120
|
+
printf '%s\n' ' --body-file <path>'
|
|
121
|
+
[ -z '$archive_line' ] || printf '%s\n' '$archive_line'
|
|
122
|
+
exit 0
|
|
123
|
+
fi
|
|
124
|
+
if [ "\${1:-}" = mv ] && [ "\${2:-}" = --help ]; then
|
|
125
|
+
printf '%s\n' '$mv_usage'
|
|
126
|
+
exit 0
|
|
127
|
+
fi
|
|
128
|
+
exit 0
|
|
129
|
+
SH
|
|
130
|
+
chmod +x "$fakebin/tasks-axi"
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
add_real_jq() {
|
|
134
|
+
local fakebin=$1 real_jq
|
|
135
|
+
real_jq=$(command -v jq 2>/dev/null) || fail "jq is required for dispatch profile validation tests"
|
|
136
|
+
cat > "$fakebin/jq" <<SH
|
|
137
|
+
#!/usr/bin/env bash
|
|
138
|
+
exec '$real_jq' "\$@"
|
|
139
|
+
SH
|
|
140
|
+
chmod +x "$fakebin/jq"
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
make_fake_fleet_sync_root() {
|
|
144
|
+
local dir=$1 fake_root
|
|
145
|
+
fake_root="$dir/fake-root"
|
|
146
|
+
mkdir -p "$fake_root/bin"
|
|
147
|
+
cat > "$fake_root/bin/fm-fleet-sync.sh" <<'SH'
|
|
148
|
+
#!/usr/bin/env bash
|
|
149
|
+
[ -z "${FM_FAKE_FLEET_SYNC_STARTED_MARKER:-}" ] || : > "$FM_FAKE_FLEET_SYNC_STARTED_MARKER"
|
|
150
|
+
printf '%s\n' 'alpha: synced'
|
|
151
|
+
printf '%s\n' 'beta: skipped: no origin remote'
|
|
152
|
+
exec perl -e 'sleep 300'
|
|
153
|
+
SH
|
|
154
|
+
chmod +x "$fake_root/bin/fm-fleet-sync.sh"
|
|
155
|
+
printf '%s\n' "$fake_root"
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
add_origin_backed_projects() {
|
|
159
|
+
local home=$1 count=$2 i repo
|
|
160
|
+
mkdir -p "$home/projects"
|
|
161
|
+
i=1
|
|
162
|
+
while [ "$i" -le "$count" ]; do
|
|
163
|
+
repo=$(printf '%s/projects/repo-%02d' "$home" "$i")
|
|
164
|
+
git init -q "$repo"
|
|
165
|
+
git -C "$repo" remote add origin "file://$home/remotes/repo-$i.git"
|
|
166
|
+
i=$((i + 1))
|
|
167
|
+
done
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
add_no_origin_projects() {
|
|
171
|
+
local home=$1 count=$2 i repo
|
|
172
|
+
mkdir -p "$home/projects"
|
|
173
|
+
i=1
|
|
174
|
+
while [ "$i" -le "$count" ]; do
|
|
175
|
+
repo=$(printf '%s/projects/local-%02d' "$home" "$i")
|
|
176
|
+
git init -q "$repo"
|
|
177
|
+
i=$((i + 1))
|
|
178
|
+
done
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
run_bootstrap_timeout_case() {
|
|
182
|
+
local home=$1 fake_root=$2 fakebin=$3 override started_marker git_record wait_for_marker
|
|
183
|
+
override=__unset__
|
|
184
|
+
started_marker=${5:-}
|
|
185
|
+
git_record=${6:-}
|
|
186
|
+
wait_for_marker=${7:-0}
|
|
187
|
+
[ "$#" -lt 4 ] || override=$4
|
|
188
|
+
(
|
|
189
|
+
# shellcheck disable=SC2317,SC2329 # Exported and invoked by the bootstrap subprocess.
|
|
190
|
+
sleep() {
|
|
191
|
+
local inc=${1:-1}
|
|
192
|
+
SECONDS=$((SECONDS + inc))
|
|
193
|
+
# Advance fake time quickly, but yield on every tick so the background
|
|
194
|
+
# fleet-sync process can deterministically write its partial output before
|
|
195
|
+
# the simulated timeout kills it, even on a busy full-suite runner.
|
|
196
|
+
command sleep 0.01
|
|
197
|
+
}
|
|
198
|
+
# shellcheck disable=SC2317,SC2329 # Exported and invoked by the bootstrap subprocess.
|
|
199
|
+
git() {
|
|
200
|
+
local tries
|
|
201
|
+
if [ "${FM_FAKE_GIT_WAIT_FOR_FLEET_START:-}" = 1 ] && [ -n "${FM_FAKE_FLEET_SYNC_STARTED_MARKER:-}" ]; then
|
|
202
|
+
tries=0
|
|
203
|
+
while [ "$tries" -lt 5 ] && [ ! -e "$FM_FAKE_FLEET_SYNC_STARTED_MARKER" ]; do
|
|
204
|
+
command sleep 0.01
|
|
205
|
+
tries=$((tries + 1))
|
|
206
|
+
done
|
|
207
|
+
fi
|
|
208
|
+
if [ -n "${FM_FAKE_GIT_SYNC_STARTED_RECORD:-}" ] && [ -n "${FM_FAKE_FLEET_SYNC_STARTED_MARKER:-}" ] && [ -e "$FM_FAKE_FLEET_SYNC_STARTED_MARKER" ]; then
|
|
209
|
+
printf '%s\n' "$*" >> "$FM_FAKE_GIT_SYNC_STARTED_RECORD"
|
|
210
|
+
fi
|
|
211
|
+
command git "$@"
|
|
212
|
+
}
|
|
213
|
+
export -f sleep
|
|
214
|
+
export -f git
|
|
215
|
+
if [ "$override" = __unset__ ]; then
|
|
216
|
+
PATH="$fakebin:$BASE_PATH" FM_HOME="$home" FM_ROOT_OVERRIDE="$fake_root" \
|
|
217
|
+
FM_FAKE_FLEET_SYNC_STARTED_MARKER="$started_marker" \
|
|
218
|
+
FM_FAKE_GIT_SYNC_STARTED_RECORD="$git_record" \
|
|
219
|
+
FM_FAKE_GIT_WAIT_FOR_FLEET_START="$wait_for_marker" \
|
|
220
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh" 2>/dev/null
|
|
221
|
+
else
|
|
222
|
+
PATH="$fakebin:$BASE_PATH" FM_HOME="$home" FM_ROOT_OVERRIDE="$fake_root" \
|
|
223
|
+
FM_FLEET_SYNC_BOOTSTRAP_TIMEOUT="$override" \
|
|
224
|
+
FM_FAKE_FLEET_SYNC_STARTED_MARKER="$started_marker" \
|
|
225
|
+
FM_FAKE_GIT_SYNC_STARTED_RECORD="$git_record" \
|
|
226
|
+
FM_FAKE_GIT_WAIT_FOR_FLEET_START="$wait_for_marker" \
|
|
227
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh" 2>/dev/null
|
|
228
|
+
fi
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
assert_timeout_report() {
|
|
233
|
+
local out=$1 expected_timeout=$2 timing timeout elapsed
|
|
234
|
+
timing=$(printf '%s\n' "$out" | sed -n 's/^FLEET_SYNC: fleet: skipped: bootstrap refresh timed out (timeout=\([0-9][0-9]*\)s elapsed=\([0-9][0-9]*\)s)$/\1 \2/p')
|
|
235
|
+
[ -n "$timing" ] || fail "missing fleet-sync timeout report"
|
|
236
|
+
timeout=${timing%% *}
|
|
237
|
+
elapsed=${timing#* }
|
|
238
|
+
[ "$timeout" -eq "$expected_timeout" ] || fail "expected timeout=${expected_timeout}s, got timeout=${timeout}s"
|
|
239
|
+
[ "$elapsed" -ge "$timeout" ] || fail "expected elapsed >= timeout, got elapsed=${elapsed}s timeout=${timeout}s"
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
# Each row (fields are '^'-separated; the install URL contains a literal '|'):
|
|
243
|
+
# <label>^<lease 1/0>^<tasks-axi version or ->^<quota 1/0>^<backend or ->^<mode>^<expect>^<notcontains>
|
|
244
|
+
# mode=empty -> output must be empty (expect/notcontains ignored)
|
|
245
|
+
# mode=exact -> output must equal <expect>
|
|
246
|
+
# mode=grep -> output must contain <expect> (fixed string); <notcontains> must not appear
|
|
247
|
+
test_bootstrap_reporting() {
|
|
248
|
+
local label lease tasks quota backend mode expect notcontains case_dir fakebin out n archive_body multi_id
|
|
249
|
+
n=0
|
|
250
|
+
while IFS='^' read -r label lease tasks quota backend mode expect notcontains; do
|
|
251
|
+
[ -n "$label" ] || continue
|
|
252
|
+
n=$((n + 1))
|
|
253
|
+
case_dir="$TMP_ROOT/case-$n"
|
|
254
|
+
mkdir -p "$case_dir/home"
|
|
255
|
+
if [ "$backend" != "-" ]; then
|
|
256
|
+
mkdir -p "$case_dir/home/config"
|
|
257
|
+
printf '%s\n' "$backend" > "$case_dir/home/config/backlog-backend"
|
|
258
|
+
fi
|
|
259
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
260
|
+
if [ "$tasks" = "-" ]; then
|
|
261
|
+
rm -f "$fakebin/tasks-axi"
|
|
262
|
+
else
|
|
263
|
+
archive_body=yes
|
|
264
|
+
multi_id=yes
|
|
265
|
+
case "$tasks" in
|
|
266
|
+
*:noarchive)
|
|
267
|
+
archive_body=no
|
|
268
|
+
tasks=${tasks%:noarchive}
|
|
269
|
+
;;
|
|
270
|
+
esac
|
|
271
|
+
case "$tasks" in
|
|
272
|
+
*:nomulti)
|
|
273
|
+
multi_id=no
|
|
274
|
+
tasks=${tasks%:nomulti}
|
|
275
|
+
;;
|
|
276
|
+
esac
|
|
277
|
+
add_tasks_axi "$fakebin" "$tasks" "$archive_body" "$multi_id"
|
|
278
|
+
fi
|
|
279
|
+
if [ "$quota" = "0" ]; then
|
|
280
|
+
rm -f "$fakebin/quota-axi"
|
|
281
|
+
fi
|
|
282
|
+
# FM_ROOT_OVERRIDE points the worktree-tangle check at the non-git home dir so
|
|
283
|
+
# it stays inert: this suite pins tool detection, not the tangle guard, and the
|
|
284
|
+
# ambient checkout (CI runs on a feature branch) must not leak a TANGLE line in.
|
|
285
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
286
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP="$lease" "$ROOT/bin/fm-bootstrap.sh")
|
|
287
|
+
case "$mode" in
|
|
288
|
+
empty)
|
|
289
|
+
[ -z "$out" ] || fail "$label: expected silence, got: $out" ;;
|
|
290
|
+
exact)
|
|
291
|
+
[ "$out" = "$expect" ] || fail "$label: expected '$expect', got: $out" ;;
|
|
292
|
+
grep)
|
|
293
|
+
printf '%s\n' "$out" | grep -Fx "$expect" >/dev/null || fail "$label: missing '$expect' (got: $out)"
|
|
294
|
+
if [ -n "$notcontains" ]; then
|
|
295
|
+
printf '%s\n' "$out" | grep -F "$notcontains" >/dev/null && fail "$label: unexpected '$notcontains' in: $out"
|
|
296
|
+
fi
|
|
297
|
+
;;
|
|
298
|
+
esac
|
|
299
|
+
done <<'ROWS'
|
|
300
|
+
treehouse --lease support is accepted silently^1^0.2.4^1^manual^empty^^
|
|
301
|
+
treehouse without --lease reports an upgrade, gh auth is fine^0^0.2.4^1^-^grep^MISSING: treehouse (install: curl -fsSL https://kunchenguid.github.io/treehouse/install.sh | sh)^NEEDS_GH_AUTH
|
|
302
|
+
compatible tasks-axi is silent by default^1^0.2.4^1^-^empty^^
|
|
303
|
+
missing tasks-axi is required by default^1^-^1^-^exact^MISSING: tasks-axi (install: npm install -g tasks-axi)^
|
|
304
|
+
incompatible tasks-axi is required by default^1^0.1.0^1^-^exact^MISSING: tasks-axi (install: npm install -g tasks-axi)^
|
|
305
|
+
tasks-axi without archive-body is required by default^1^0.2.4:noarchive^1^-^exact^MISSING: tasks-axi (install: npm install -g tasks-axi)^
|
|
306
|
+
tasks-axi without multi-id mv is required by default^1^0.2.4:nomulti^1^-^exact^MISSING: tasks-axi (install: npm install -g tasks-axi)^
|
|
307
|
+
missing quota-axi is required by default^1^0.2.4^0^manual^exact^MISSING: quota-axi (install: npm install -g quota-axi)^
|
|
308
|
+
manual backlog backend still requires missing tasks-axi^1^-^1^manual^exact^MISSING: tasks-axi (install: npm install -g tasks-axi)^
|
|
309
|
+
manual backlog backend suppresses tasks-axi availability^1^0.2.4^1^manual^empty^^
|
|
310
|
+
ROWS
|
|
311
|
+
pass "bootstrap reports treehouse lease + tasks-axi/quota-axi bootstrap contracts"
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
test_no_mistakes_min_version() {
|
|
315
|
+
local label version mode case_dir fakebin out missing n
|
|
316
|
+
missing='MISSING: no-mistakes (install: curl -fsSL https://raw.githubusercontent.com/kunchenguid/no-mistakes/main/docs/install.sh | sh)'
|
|
317
|
+
n=0
|
|
318
|
+
while IFS='^' read -r label version mode; do
|
|
319
|
+
[ -n "$label" ] || continue
|
|
320
|
+
n=$((n + 1))
|
|
321
|
+
case_dir="$TMP_ROOT/no-mistakes-$n"
|
|
322
|
+
mkdir -p "$case_dir/home"
|
|
323
|
+
mkdir -p "$case_dir/home/config"
|
|
324
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
325
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
326
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
327
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 FM_FAKE_NO_MISTAKES_VERSION="$version" "$ROOT/bin/fm-bootstrap.sh")
|
|
328
|
+
case "$mode" in
|
|
329
|
+
empty)
|
|
330
|
+
[ -z "$out" ] || fail "$label: expected silence, got: $out" ;;
|
|
331
|
+
missing)
|
|
332
|
+
[ "$out" = "$missing" ] || fail "$label: expected '$missing', got: $out" ;;
|
|
333
|
+
esac
|
|
334
|
+
done <<'ROWS'
|
|
335
|
+
minimum no-mistakes version is accepted^no-mistakes version v1.46.0 (fake)^empty
|
|
336
|
+
newer no-mistakes minor is accepted^no-mistakes version v1.47.0 (fake)^empty
|
|
337
|
+
newer no-mistakes major is accepted^no-mistakes version v2.0.0 (fake)^empty
|
|
338
|
+
older no-mistakes patch reports an upgrade^no-mistakes version v1.45.4 (fake)^missing
|
|
339
|
+
unparseable no-mistakes version reports an upgrade^no-mistakes development build^missing
|
|
340
|
+
ROWS
|
|
341
|
+
pass "bootstrap enforces no-mistakes minimum version"
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
test_gh_axi_min_version() {
|
|
345
|
+
local label version mode case_dir fakebin out missing n
|
|
346
|
+
missing='MISSING: gh-axi (install: npm install -g gh-axi && gh-axi setup hooks)'
|
|
347
|
+
n=0
|
|
348
|
+
while IFS='^' read -r label version mode; do
|
|
349
|
+
[ -n "$label" ] || continue
|
|
350
|
+
n=$((n + 1))
|
|
351
|
+
case_dir="$TMP_ROOT/gh-axi-$n"
|
|
352
|
+
mkdir -p "$case_dir/home/config"
|
|
353
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
354
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
355
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
356
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 FM_FAKE_GH_AXI_VERSION="$version" "$ROOT/bin/fm-bootstrap.sh")
|
|
357
|
+
case "$mode" in
|
|
358
|
+
empty)
|
|
359
|
+
[ -z "$out" ] || fail "$label: expected silence, got: $out" ;;
|
|
360
|
+
missing)
|
|
361
|
+
[ "$out" = "$missing" ] || fail "$label: expected '$missing', got: $out" ;;
|
|
362
|
+
esac
|
|
363
|
+
done <<'ROWS'
|
|
364
|
+
minimum gh-axi version is accepted^0.1.29^empty
|
|
365
|
+
newer gh-axi patch is accepted^0.1.30^empty
|
|
366
|
+
newer gh-axi minor is accepted^0.2.0^empty
|
|
367
|
+
newer gh-axi major is accepted^1.0.0^empty
|
|
368
|
+
older gh-axi patch reports an upgrade^0.1.19^missing
|
|
369
|
+
much older gh-axi minor reports an upgrade^0.0.9^missing
|
|
370
|
+
unparseable gh-axi version reports an upgrade^gh-axi development build^missing
|
|
371
|
+
ROWS
|
|
372
|
+
pass "bootstrap enforces gh-axi minimum version"
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
test_lavish_axi_min_version() {
|
|
376
|
+
local label version mode case_dir fakebin out missing n
|
|
377
|
+
missing='MISSING: lavish-axi (install: npm install -g lavish-axi && lavish-axi setup hooks)'
|
|
378
|
+
n=0
|
|
379
|
+
while IFS='^' read -r label version mode; do
|
|
380
|
+
[ -n "$label" ] || continue
|
|
381
|
+
n=$((n + 1))
|
|
382
|
+
case_dir="$TMP_ROOT/lavish-axi-$n"
|
|
383
|
+
mkdir -p "$case_dir/home/config"
|
|
384
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
385
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
386
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
387
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 FM_FAKE_LAVISH_AXI_VERSION="$version" "$ROOT/bin/fm-bootstrap.sh")
|
|
388
|
+
case "$mode" in
|
|
389
|
+
empty)
|
|
390
|
+
[ -z "$out" ] || fail "$label: expected silence, got: $out" ;;
|
|
391
|
+
missing)
|
|
392
|
+
[ "$out" = "$missing" ] || fail "$label: expected '$missing', got: $out" ;;
|
|
393
|
+
esac
|
|
394
|
+
done <<'ROWS'
|
|
395
|
+
minimum lavish-axi version is accepted^0.1.46^empty
|
|
396
|
+
newer lavish-axi patch is accepted^0.1.47^empty
|
|
397
|
+
newer lavish-axi minor is accepted^0.2.0^empty
|
|
398
|
+
newer lavish-axi major is accepted^1.0.0^empty
|
|
399
|
+
the patch just below the floor reports an upgrade^0.1.45^missing
|
|
400
|
+
much older lavish-axi minor reports an upgrade^0.0.9^missing
|
|
401
|
+
unparseable lavish-axi version reports an upgrade^lavish-axi development build^missing
|
|
402
|
+
ROWS
|
|
403
|
+
pass "bootstrap enforces lavish-axi minimum version"
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
test_tasks_axi_min_version() {
|
|
407
|
+
local label version mode case_dir fakebin out missing n archive_body multi_id
|
|
408
|
+
missing='MISSING: tasks-axi (install: npm install -g tasks-axi)'
|
|
409
|
+
n=0
|
|
410
|
+
while IFS='^' read -r label version mode; do
|
|
411
|
+
[ -n "$label" ] || continue
|
|
412
|
+
n=$((n + 1))
|
|
413
|
+
case_dir="$TMP_ROOT/tasks-axi-$n"
|
|
414
|
+
mkdir -p "$case_dir/home/config"
|
|
415
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
416
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
417
|
+
archive_body=yes
|
|
418
|
+
multi_id=yes
|
|
419
|
+
case "$version" in
|
|
420
|
+
*:noarchive)
|
|
421
|
+
archive_body=no
|
|
422
|
+
version=${version%:noarchive}
|
|
423
|
+
;;
|
|
424
|
+
esac
|
|
425
|
+
case "$version" in
|
|
426
|
+
*:nomulti)
|
|
427
|
+
multi_id=no
|
|
428
|
+
version=${version%:nomulti}
|
|
429
|
+
;;
|
|
430
|
+
esac
|
|
431
|
+
add_tasks_axi "$fakebin" "$version" "$archive_body" "$multi_id"
|
|
432
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
433
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
434
|
+
case "$mode" in
|
|
435
|
+
empty)
|
|
436
|
+
[ -z "$out" ] || fail "$label: expected silence, got: $out" ;;
|
|
437
|
+
missing)
|
|
438
|
+
[ "$out" = "$missing" ] || fail "$label: expected '$missing', got: $out" ;;
|
|
439
|
+
esac
|
|
440
|
+
done <<'ROWS'
|
|
441
|
+
minimum tasks-axi version is accepted^0.2.4^empty
|
|
442
|
+
newer tasks-axi patch is accepted^0.2.5^empty
|
|
443
|
+
newer tasks-axi minor is accepted^0.3.0^empty
|
|
444
|
+
newer tasks-axi major is accepted^1.0.0^empty
|
|
445
|
+
older tasks-axi with features reports an upgrade^0.1.1^missing
|
|
446
|
+
the patch just below the floor reports an upgrade^0.2.3^missing
|
|
447
|
+
unparseable tasks-axi version reports an upgrade^tasks-axi development build^missing
|
|
448
|
+
tasks-axi at floor without archive-body reports an upgrade^0.2.4:noarchive^missing
|
|
449
|
+
tasks-axi at floor without multi-id reports an upgrade^0.2.4:nomulti^missing
|
|
450
|
+
ROWS
|
|
451
|
+
pass "bootstrap enforces tasks-axi minimum version"
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
# These rows exercise the real bootstrap check with a fake quota-axi answering
|
|
455
|
+
# --version: below the floor produces MISSING, while at or above is silent.
|
|
456
|
+
test_quota_axi_min_version() {
|
|
457
|
+
local label version mode case_dir fakebin out missing n
|
|
458
|
+
missing='MISSING: quota-axi (install: npm install -g quota-axi)'
|
|
459
|
+
n=0
|
|
460
|
+
while IFS='^' read -r label version mode; do
|
|
461
|
+
[ -n "$label" ] || continue
|
|
462
|
+
n=$((n + 1))
|
|
463
|
+
case_dir="$TMP_ROOT/quota-axi-$n"
|
|
464
|
+
mkdir -p "$case_dir/home/config"
|
|
465
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
466
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
467
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
468
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 FM_FAKE_QUOTA_AXI_VERSION="$version" "$ROOT/bin/fm-bootstrap.sh")
|
|
469
|
+
case "$mode" in
|
|
470
|
+
empty)
|
|
471
|
+
[ -z "$out" ] || fail "$label: expected silence, got: $out" ;;
|
|
472
|
+
missing)
|
|
473
|
+
[ "$out" = "$missing" ] || fail "$label: expected '$missing', got: $out" ;;
|
|
474
|
+
esac
|
|
475
|
+
done <<'ROWS'
|
|
476
|
+
minimum quota-axi version is accepted^0.1.29^empty
|
|
477
|
+
newer quota-axi patch is accepted^0.1.30^empty
|
|
478
|
+
newer quota-axi minor is accepted^0.2.0^empty
|
|
479
|
+
newer quota-axi major is accepted^1.0.0^empty
|
|
480
|
+
the patch just below the floor reports an upgrade^0.1.28^missing
|
|
481
|
+
much older quota-axi minor reports an upgrade^0.0.9^missing
|
|
482
|
+
unparseable quota-axi version reports an upgrade^quota-axi development build^missing
|
|
483
|
+
ROWS
|
|
484
|
+
pass "bootstrap enforces quota-axi minimum version"
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
test_git_is_required_with_supported_install_instruction() {
|
|
488
|
+
local case_dir fakebin bash_env out expected
|
|
489
|
+
case_dir="$TMP_ROOT/git-required"
|
|
490
|
+
mkdir -p "$case_dir/home/config"
|
|
491
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
492
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
493
|
+
bash_env="$case_dir/no-git.bash"
|
|
494
|
+
cat > "$bash_env" <<'SH'
|
|
495
|
+
command() {
|
|
496
|
+
if [ "${1:-}" = -v ] && [ "${2:-}" = git ]; then
|
|
497
|
+
return 1
|
|
498
|
+
fi
|
|
499
|
+
builtin command "$@"
|
|
500
|
+
}
|
|
501
|
+
git() {
|
|
502
|
+
return 127
|
|
503
|
+
}
|
|
504
|
+
SH
|
|
505
|
+
|
|
506
|
+
out=$(PATH="$fakebin:$BASE_PATH" BASH_ENV="$bash_env" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
507
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
508
|
+
expected="MISSING: git (install: brew install git # or the platform's package manager)"
|
|
509
|
+
[ "$out" = "$expected" ] || fail "missing git should report the supported install instruction, got: $out"
|
|
510
|
+
pass "bootstrap requires git with an install instruction"
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
test_orca_backend_gates_orca_tool_only_when_selected() {
|
|
514
|
+
local case_dir fakebin out missing_orca
|
|
515
|
+
missing_orca="MISSING: orca (install: brew install orca # or the platform's package manager)"
|
|
516
|
+
|
|
517
|
+
case_dir="$TMP_ROOT/orca-backend-selected"
|
|
518
|
+
mkdir -p "$case_dir/home/config"
|
|
519
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
520
|
+
printf '%s\n' orca > "$case_dir/home/config/backend"
|
|
521
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
522
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
523
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
524
|
+
[ "$out" = "$missing_orca" ] || fail "backend=orca should require only the Orca-specific missing tool, got: $out"
|
|
525
|
+
|
|
526
|
+
case_dir="$TMP_ROOT/orca-backend-not-selected"
|
|
527
|
+
mkdir -p "$case_dir/home/config"
|
|
528
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
529
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
530
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
531
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
532
|
+
assert_not_contains "$out" "MISSING: orca" "bootstrap should not require orca unless backend=orca is selected"
|
|
533
|
+
pass "bootstrap: backend=orca gates the Orca CLI without requiring it on the default backend"
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
# Build a fake toolchain with tmux REMOVED and the named backend session CLI(s)
|
|
537
|
+
# plus jq added, so a backend that must NOT require tmux can be proven silent
|
|
538
|
+
# with tmux absent. Echoes the fakebin dir. The removed tmux is what makes these
|
|
539
|
+
# cases catch the old "everything but orca demands tmux" bug: with the buggy
|
|
540
|
+
# TOOLS list a herdr/zellij/cmux home would report MISSING: tmux here.
|
|
541
|
+
make_fake_toolchain_no_tmux() { # <case-dir> <extra-cli...>
|
|
542
|
+
local dir=$1 fakebin
|
|
543
|
+
shift
|
|
544
|
+
fakebin=$(make_fake_toolchain "$dir")
|
|
545
|
+
rm -f "$fakebin/tmux"
|
|
546
|
+
fm_fake_exit0 "$fakebin" jq "$@"
|
|
547
|
+
printf '%s\n' "$fakebin"
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
test_session_provider_backends_do_not_require_tmux() {
|
|
551
|
+
local backend cli case_dir fakebin out
|
|
552
|
+
# herdr/zellij/cmux are session providers only: they require their own CLI, jq,
|
|
553
|
+
# and treehouse, never tmux. With all genuine deps present and tmux absent,
|
|
554
|
+
# bootstrap must be silent.
|
|
555
|
+
while IFS='^' read -r backend cli; do
|
|
556
|
+
[ -n "$backend" ] || continue
|
|
557
|
+
case_dir="$TMP_ROOT/$backend-no-tmux"
|
|
558
|
+
mkdir -p "$case_dir/home/config"
|
|
559
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
560
|
+
printf '%s\n' "$backend" > "$case_dir/home/config/backend"
|
|
561
|
+
fakebin=$(make_fake_toolchain_no_tmux "$case_dir" "$cli")
|
|
562
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
563
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
564
|
+
[ -z "$out" ] || fail "backend=$backend with tmux absent but its own deps present should be silent, got: $out"
|
|
565
|
+
done <<'ROWS'
|
|
566
|
+
herdr^herdr
|
|
567
|
+
zellij^zellij
|
|
568
|
+
cmux^cmux
|
|
569
|
+
ROWS
|
|
570
|
+
pass "bootstrap: session-provider backends require their own CLI + jq + treehouse, never tmux"
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
test_session_provider_backends_gate_own_cli_not_tmux() {
|
|
574
|
+
local backend cli case_dir fakebin out missing
|
|
575
|
+
# With the backend's OWN session CLI absent (and tmux also absent), bootstrap
|
|
576
|
+
# must fail closed on the genuine dep and never substitute a false tmux demand.
|
|
577
|
+
while IFS='^' read -r backend cli; do
|
|
578
|
+
[ -n "$backend" ] || continue
|
|
579
|
+
case_dir="$TMP_ROOT/$backend-missing-cli"
|
|
580
|
+
mkdir -p "$case_dir/home/config"
|
|
581
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
582
|
+
printf '%s\n' "$backend" > "$case_dir/home/config/backend"
|
|
583
|
+
# Toolchain has jq + treehouse but NOT the session CLI and NOT tmux.
|
|
584
|
+
fakebin=$(make_fake_toolchain_no_tmux "$case_dir")
|
|
585
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
586
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
587
|
+
if [ "$backend" = herdr ]; then
|
|
588
|
+
missing="MISSING_MANUAL: herdr (instructions: https://herdr.dev)"
|
|
589
|
+
else
|
|
590
|
+
missing="MISSING: $cli"
|
|
591
|
+
fi
|
|
592
|
+
assert_contains "$out" "$missing" "backend=$backend must fail closed on its own missing session CLI"
|
|
593
|
+
if [ "$backend" = herdr ]; then
|
|
594
|
+
assert_not_contains "$out" "MISSING: herdr (install:" \
|
|
595
|
+
"backend=herdr must not advertise manual guidance as an executable install command"
|
|
596
|
+
fi
|
|
597
|
+
assert_not_contains "$out" "MISSING: tmux" "backend=$backend must not demand tmux when its own CLI is missing"
|
|
598
|
+
done <<'ROWS'
|
|
599
|
+
herdr^herdr
|
|
600
|
+
zellij^zellij
|
|
601
|
+
cmux^cmux
|
|
602
|
+
ROWS
|
|
603
|
+
pass "bootstrap: a session-provider backend gates its own CLI, never a false tmux requirement"
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
test_herdr_install_requires_manual_action() {
|
|
607
|
+
local out status
|
|
608
|
+
out=$("$ROOT/bin/fm-bootstrap.sh" install herdr 2>&1)
|
|
609
|
+
status=$?
|
|
610
|
+
[ "$status" -ne 0 ] || fail "install herdr should fail instead of evaluating its manual-install hint"
|
|
611
|
+
[ "$out" = "error: herdr requires manual installation (instructions: https://herdr.dev)" ] \
|
|
612
|
+
|| fail "install herdr should return actionable manual-install guidance, got: $out"
|
|
613
|
+
pass "bootstrap: Herdr manual-install guidance is never executed as a shell command"
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
test_cmux_bundled_cli_satisfies_dependency() {
|
|
617
|
+
local case_dir fakebin bundle out
|
|
618
|
+
case_dir="$TMP_ROOT/cmux-bundled-cli"
|
|
619
|
+
mkdir -p "$case_dir/home/config" "$case_dir/bundle"
|
|
620
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
621
|
+
printf '%s\n' cmux > "$case_dir/home/config/backend"
|
|
622
|
+
fakebin=$(make_fake_toolchain_no_tmux "$case_dir")
|
|
623
|
+
fm_fake_exit0 "$case_dir/bundle" cmux
|
|
624
|
+
bundle="$case_dir/bundle/cmux"
|
|
625
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
626
|
+
FM_BACKEND_CMUX_BUNDLE_BIN="$bundle" FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
627
|
+
[ -z "$out" ] || fail "a usable bundled cmux CLI should satisfy bootstrap without a PATH shim, got: $out"
|
|
628
|
+
pass "bootstrap: the bundled cmux CLI satisfies the active backend dependency"
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
test_unknown_backend_reports_invalid_configuration() {
|
|
632
|
+
local case_dir fakebin out
|
|
633
|
+
case_dir="$TMP_ROOT/unknown-backend"
|
|
634
|
+
mkdir -p "$case_dir/home/config"
|
|
635
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
636
|
+
printf '%s\n' bogus > "$case_dir/home/config/backend"
|
|
637
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
638
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
639
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
640
|
+
assert_contains "$out" "BACKEND_INVALID: bogus (known: tmux herdr zellij orca cmux)" \
|
|
641
|
+
"bootstrap should report an unknown resolved backend"
|
|
642
|
+
assert_not_contains "$out" "MISSING: tmux" "an unknown backend should not silently fall back to tmux dependencies"
|
|
643
|
+
pass "bootstrap: unknown resolved backends fail closed with an actionable diagnostic"
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
test_json_backends_require_jq_not_tmux() {
|
|
647
|
+
local backend case_dir fakebin bash_env out
|
|
648
|
+
# herdr/zellij/cmux parse their backend's JSON output, so jq is a genuine dep.
|
|
649
|
+
# jq lives in a system BASE_PATH dir on many hosts, so force it missing with a
|
|
650
|
+
# command()/jq() override (the same technique the git-required case uses) to keep
|
|
651
|
+
# the assertion host-independent.
|
|
652
|
+
while IFS='^' read -r backend; do
|
|
653
|
+
[ -n "$backend" ] || continue
|
|
654
|
+
case_dir="$TMP_ROOT/$backend-missing-jq"
|
|
655
|
+
mkdir -p "$case_dir/home/config"
|
|
656
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
657
|
+
printf '%s\n' "$backend" > "$case_dir/home/config/backend"
|
|
658
|
+
# Session CLI present, tmux absent, jq deliberately NOT stubbed and masked below.
|
|
659
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
660
|
+
rm -f "$fakebin/tmux"
|
|
661
|
+
fm_fake_exit0 "$fakebin" "$backend"
|
|
662
|
+
bash_env="$case_dir/no-jq.bash"
|
|
663
|
+
cat > "$bash_env" <<'SH'
|
|
664
|
+
command() {
|
|
665
|
+
if [ "${1:-}" = -v ] && [ "${2:-}" = jq ]; then
|
|
666
|
+
return 1
|
|
667
|
+
fi
|
|
668
|
+
builtin command "$@"
|
|
669
|
+
}
|
|
670
|
+
jq() {
|
|
671
|
+
return 127
|
|
672
|
+
}
|
|
673
|
+
SH
|
|
674
|
+
out=$(PATH="$fakebin:$BASE_PATH" BASH_ENV="$bash_env" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
675
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
676
|
+
assert_contains "$out" "MISSING: jq" "backend=$backend must fail closed on missing jq"
|
|
677
|
+
assert_not_contains "$out" "MISSING: tmux" "backend=$backend must not demand tmux when jq is missing"
|
|
678
|
+
done <<'ROWS'
|
|
679
|
+
herdr
|
|
680
|
+
zellij
|
|
681
|
+
cmux
|
|
682
|
+
ROWS
|
|
683
|
+
pass "bootstrap: JSON-emitting backends require jq (their genuine dep), never tmux"
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
test_treehouse_lease_check_follows_resolved_backend() {
|
|
687
|
+
local case_dir fakebin out
|
|
688
|
+
# A treehouse that lacks durable --lease support is only a problem for a backend
|
|
689
|
+
# that actually uses treehouse. Orca owns its own worktrees, so an old treehouse
|
|
690
|
+
# must NOT trip MISSING: treehouse under backend=orca...
|
|
691
|
+
case_dir="$TMP_ROOT/orca-old-treehouse"
|
|
692
|
+
mkdir -p "$case_dir/home/config"
|
|
693
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
694
|
+
printf '%s\n' orca > "$case_dir/home/config/backend"
|
|
695
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
696
|
+
rm -f "$fakebin/tmux"
|
|
697
|
+
fm_fake_exit0 "$fakebin" orca
|
|
698
|
+
# FM_FAKE_TREEHOUSE_LEASE_HELP unset: the fake treehouse advertises NO --lease.
|
|
699
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
700
|
+
"$ROOT/bin/fm-bootstrap.sh")
|
|
701
|
+
[ -z "$out" ] || fail "backend=orca must not require treehouse (even lease-less) or tmux, got: $out"
|
|
702
|
+
|
|
703
|
+
# ...but the same lease-less treehouse IS a problem for a session-provider
|
|
704
|
+
# backend that relies on treehouse for worktrees.
|
|
705
|
+
case_dir="$TMP_ROOT/herdr-old-treehouse"
|
|
706
|
+
mkdir -p "$case_dir/home/config"
|
|
707
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
708
|
+
printf '%s\n' herdr > "$case_dir/home/config/backend"
|
|
709
|
+
fakebin=$(make_fake_toolchain_no_tmux "$case_dir" herdr)
|
|
710
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
711
|
+
"$ROOT/bin/fm-bootstrap.sh")
|
|
712
|
+
assert_contains "$out" "MISSING: treehouse" "backend=herdr must still require treehouse with durable lease support"
|
|
713
|
+
assert_not_contains "$out" "MISSING: tmux" "backend=herdr must not demand tmux even when treehouse is too old"
|
|
714
|
+
pass "bootstrap: the treehouse lease check follows the resolved backend's worktree provider"
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
test_fleet_sync_timeout_scales_with_origin_backed_project_count() {
|
|
718
|
+
local case_dir home fakebin fake_root out
|
|
719
|
+
case_dir="$TMP_ROOT/fleet-timeout-scaled"
|
|
720
|
+
home="$case_dir/home"
|
|
721
|
+
mkdir -p "$home/config"
|
|
722
|
+
printf '%s\n' manual > "$home/config/backlog-backend"
|
|
723
|
+
add_origin_backed_projects "$home" 18
|
|
724
|
+
add_no_origin_projects "$home" 3
|
|
725
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
726
|
+
fake_root=$(make_fake_fleet_sync_root "$case_dir")
|
|
727
|
+
|
|
728
|
+
out=$(run_bootstrap_timeout_case "$home" "$fake_root" "$fakebin")
|
|
729
|
+
|
|
730
|
+
assert_contains "$out" $'FLEET_SYNC: alpha: synced\nFLEET_SYNC: beta: skipped: no origin remote' "bootstrap timeout should relay partial fleet-sync output first"
|
|
731
|
+
assert_timeout_report "$out" 59
|
|
732
|
+
pass "bootstrap computes a fleet-size-aware default timeout and preserves partial fleet-sync output"
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
test_fleet_sync_timeout_floor_preserves_small_fleets() {
|
|
736
|
+
local case_dir home fakebin fake_root out
|
|
737
|
+
case_dir="$TMP_ROOT/fleet-timeout-small"
|
|
738
|
+
home="$case_dir/home"
|
|
739
|
+
mkdir -p "$home/config"
|
|
740
|
+
printf '%s\n' manual > "$home/config/backlog-backend"
|
|
741
|
+
add_origin_backed_projects "$home" 2
|
|
742
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
743
|
+
fake_root=$(make_fake_fleet_sync_root "$case_dir")
|
|
744
|
+
|
|
745
|
+
out=$(run_bootstrap_timeout_case "$home" "$fake_root" "$fakebin")
|
|
746
|
+
|
|
747
|
+
assert_timeout_report "$out" 20
|
|
748
|
+
pass "bootstrap keeps the quick 20s default for small fleets"
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
test_fleet_sync_timeout_explicit_override_wins() {
|
|
752
|
+
local case_dir home fakebin fake_root out
|
|
753
|
+
case_dir="$TMP_ROOT/fleet-timeout-override"
|
|
754
|
+
home="$case_dir/home"
|
|
755
|
+
mkdir -p "$home/config"
|
|
756
|
+
printf '%s\n' manual > "$home/config/backlog-backend"
|
|
757
|
+
add_origin_backed_projects "$home" 18
|
|
758
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
759
|
+
fake_root=$(make_fake_fleet_sync_root "$case_dir")
|
|
760
|
+
|
|
761
|
+
out=$(run_bootstrap_timeout_case "$home" "$fake_root" "$fakebin" 7)
|
|
762
|
+
|
|
763
|
+
assert_timeout_report "$out" 7
|
|
764
|
+
assert_not_contains "$out" "timeout=59s" "explicit override should not be replaced by the computed timeout"
|
|
765
|
+
pass "bootstrap preserves FM_FLEET_SYNC_BOOTSTRAP_TIMEOUT as an explicit override"
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
test_fleet_sync_timeout_empty_override_uses_default() {
|
|
769
|
+
local case_dir home fakebin fake_root out
|
|
770
|
+
case_dir="$TMP_ROOT/fleet-timeout-empty-override"
|
|
771
|
+
home="$case_dir/home"
|
|
772
|
+
mkdir -p "$home/config"
|
|
773
|
+
printf '%s\n' manual > "$home/config/backlog-backend"
|
|
774
|
+
add_origin_backed_projects "$home" 18
|
|
775
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
776
|
+
fake_root=$(make_fake_fleet_sync_root "$case_dir")
|
|
777
|
+
|
|
778
|
+
out=$(run_bootstrap_timeout_case "$home" "$fake_root" "$fakebin" "")
|
|
779
|
+
|
|
780
|
+
assert_timeout_report "$out" 59
|
|
781
|
+
assert_not_contains "$out" "timeout=20s" "blank timeout env should not force the legacy floor on a large fleet"
|
|
782
|
+
pass "bootstrap treats a blank timeout override as unset"
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
test_fleet_sync_timeout_is_computed_before_launch() {
|
|
786
|
+
local case_dir home fakebin fake_root out started_marker git_record
|
|
787
|
+
case_dir="$TMP_ROOT/fleet-timeout-launch-order"
|
|
788
|
+
home="$case_dir/home"
|
|
789
|
+
started_marker="$case_dir/fleet-started"
|
|
790
|
+
git_record="$case_dir/git-after-start"
|
|
791
|
+
mkdir -p "$home/config"
|
|
792
|
+
printf '%s\n' manual > "$home/config/backlog-backend"
|
|
793
|
+
add_origin_backed_projects "$home" 3
|
|
794
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
795
|
+
fake_root=$(make_fake_fleet_sync_root "$case_dir")
|
|
796
|
+
|
|
797
|
+
out=$(run_bootstrap_timeout_case "$home" "$fake_root" "$fakebin" __unset__ "$started_marker" "$git_record" 1)
|
|
798
|
+
|
|
799
|
+
[ ! -s "$git_record" ] || fail "fleet sync launched before timeout scan finished: $(tr '\n' ';' < "$git_record")"
|
|
800
|
+
assert_contains "$out" $'FLEET_SYNC: alpha: synced\nFLEET_SYNC: beta: skipped: no origin remote' "launch-order case should relay partial fleet-sync output before reporting its timeout"
|
|
801
|
+
assert_timeout_report "$out" 20
|
|
802
|
+
pass "bootstrap computes the timeout before launching fleet sync"
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
make_routine_bootstrap_fixture() {
|
|
806
|
+
local case_dir=$1 fakebin root home sm c1
|
|
807
|
+
root="$case_dir/root"
|
|
808
|
+
home="$case_dir/home"
|
|
809
|
+
sm="$case_dir/sm"
|
|
810
|
+
fm_git_identity
|
|
811
|
+
mkdir -p "$home/config" "$home/state"
|
|
812
|
+
printf '%s\n' codex > "$home/config/crew-harness"
|
|
813
|
+
printf '%s\n' '{"rules":[{"when":"normal work","use":{"harness":"codex"}}],"default":{"harness":"claude","effort":"low"}}' \
|
|
814
|
+
> "$home/config/crew-dispatch.json"
|
|
815
|
+
git init -q -b main "$root"
|
|
816
|
+
{
|
|
817
|
+
printf '%s\n' '.fm-secondmate-home'
|
|
818
|
+
printf '%s\n' 'config/crew-harness'
|
|
819
|
+
printf '%s\n' 'config/crew-dispatch.json'
|
|
820
|
+
printf '%s\n' 'config/startup-memory-budget'
|
|
821
|
+
} > "$root/.gitignore"
|
|
822
|
+
printf '%s\n' 'instructions' > "$root/AGENTS.md"
|
|
823
|
+
mkdir -p "$root/bin" "$root/.agents/skills"
|
|
824
|
+
printf '%s\n' 'echo ok' > "$root/bin/fm-spawn.sh"
|
|
825
|
+
printf '%s\n' 'skill' > "$root/.agents/skills/example.md"
|
|
826
|
+
git -C "$root" add -A
|
|
827
|
+
git -C "$root" commit -qm initial
|
|
828
|
+
c1=$(git -C "$root" rev-parse HEAD)
|
|
829
|
+
git -C "$root" worktree add -q --detach "$sm" "$c1"
|
|
830
|
+
printf '%s\n' sm > "$sm/.fm-secondmate-home"
|
|
831
|
+
{
|
|
832
|
+
printf 'window=firstmate:fm-sm\n'
|
|
833
|
+
printf 'kind=secondmate\n'
|
|
834
|
+
printf 'harness=codex\n'
|
|
835
|
+
printf 'home=%s\n' "$sm"
|
|
836
|
+
} > "$home/state/sm.meta"
|
|
837
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
838
|
+
add_real_jq "$fakebin"
|
|
839
|
+
cat > "$fakebin/tmux" <<'SH'
|
|
840
|
+
#!/usr/bin/env bash
|
|
841
|
+
case "${1:-}" in
|
|
842
|
+
display-message)
|
|
843
|
+
case "$*" in
|
|
844
|
+
*'#{cursor_y}'*) printf '%s\n' 0 ;;
|
|
845
|
+
*) printf '%s\n' codex ;;
|
|
846
|
+
esac
|
|
847
|
+
;;
|
|
848
|
+
capture-pane) printf '❯\n' ;;
|
|
849
|
+
list-windows) printf '%s\n' fm-sm ;;
|
|
850
|
+
esac
|
|
851
|
+
exit 0
|
|
852
|
+
SH
|
|
853
|
+
chmod +x "$fakebin/tmux"
|
|
854
|
+
printf '%s|%s|%s\n' "$root" "$home" "$fakebin"
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
run_routine_bootstrap_fixture() {
|
|
858
|
+
local shell=$1 case_dir=$2 fixture root home fakebin
|
|
859
|
+
fixture=$(make_routine_bootstrap_fixture "$case_dir")
|
|
860
|
+
root=${fixture%%|*}
|
|
861
|
+
fixture=${fixture#*|}
|
|
862
|
+
home=${fixture%%|*}
|
|
863
|
+
fakebin=${fixture#*|}
|
|
864
|
+
PATH="$fakebin:$BASE_PATH" FM_BACKEND=tmux FM_HOME="$home" FM_ROOT_OVERRIDE="$root" \
|
|
865
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 \
|
|
866
|
+
"$shell" "$ROOT/bin/fm-bootstrap.sh"
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
test_routine_bootstrap_confirmations_are_silent() {
|
|
870
|
+
local out
|
|
871
|
+
out=$(run_routine_bootstrap_fixture bash "$TMP_ROOT/routine-silent")
|
|
872
|
+
[ -z "$out" ] || fail "routine bootstrap confirmations should be silent, got: $out"
|
|
873
|
+
pass "bootstrap keeps routine tasks-axi, harness, dispatch, and already-live liveness confirmations silent"
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
test_routine_bootstrap_contract_runs_under_system_bash() {
|
|
877
|
+
local out
|
|
878
|
+
[ -x /bin/bash ] || { pass "bootstrap routine contract skipped without /bin/bash"; return; }
|
|
879
|
+
out=$(run_routine_bootstrap_fixture /bin/bash "$TMP_ROOT/routine-bash")
|
|
880
|
+
[ -z "$out" ] || fail "routine bootstrap contract should be silent under /bin/bash, got: $out"
|
|
881
|
+
pass "bootstrap routine contract runs under system /bin/bash"
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
# FM_BOOTSTRAP_NETWORK splits one bootstrap run into its local and network
|
|
885
|
+
# halves so a session start can compose its digest from the local half alone and
|
|
886
|
+
# run the network half concurrently. The property that has to hold is that the
|
|
887
|
+
# split is a PARTITION: `skip` plus `only` together do exactly what `all` does,
|
|
888
|
+
# with no step dropped and no step run twice.
|
|
889
|
+
test_network_phase_partitions_the_run() {
|
|
890
|
+
local case_dir fakebin all_out skip_out only_out combined
|
|
891
|
+
case_dir="$TMP_ROOT/network-phase"
|
|
892
|
+
mkdir -p "$case_dir/home/config"
|
|
893
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
894
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
895
|
+
# Break the two diagnostics that stand for the two halves: a local tool floor
|
|
896
|
+
# and the network GitHub-auth probe.
|
|
897
|
+
rm -f "$fakebin/node"
|
|
898
|
+
cat > "$fakebin/gh" <<'SH'
|
|
899
|
+
#!/usr/bin/env bash
|
|
900
|
+
exit 1
|
|
901
|
+
SH
|
|
902
|
+
chmod +x "$fakebin/gh"
|
|
903
|
+
|
|
904
|
+
all_out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
905
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
906
|
+
assert_contains "$all_out" "MISSING: node (install:" "the unsplit run lost its local diagnostic"
|
|
907
|
+
assert_contains "$all_out" "NEEDS_GH_AUTH" "the unsplit run lost its network diagnostic"
|
|
908
|
+
|
|
909
|
+
skip_out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
910
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 FM_BOOTSTRAP_NETWORK=skip "$ROOT/bin/fm-bootstrap.sh")
|
|
911
|
+
assert_contains "$skip_out" "MISSING: node (install:" "the local half lost its own diagnostic"
|
|
912
|
+
assert_not_contains "$skip_out" "NEEDS_GH_AUTH" "the local half still made a network call"
|
|
913
|
+
|
|
914
|
+
only_out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
915
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 FM_BOOTSTRAP_NETWORK=only "$ROOT/bin/fm-bootstrap.sh")
|
|
916
|
+
assert_contains "$only_out" "NEEDS_GH_AUTH" "the network half lost its own diagnostic"
|
|
917
|
+
assert_not_contains "$only_out" "MISSING: node" "the network half repeated the local half's work"
|
|
918
|
+
|
|
919
|
+
combined=$(printf '%s\n%s\n' "$skip_out" "$only_out" | LC_ALL=C sort)
|
|
920
|
+
[ "$combined" = "$(printf '%s\n' "$all_out" | LC_ALL=C sort)" ] \
|
|
921
|
+
|| fail "skip + only is not the same set of findings as an unsplit run"$'\n'"all: $all_out"$'\n'"skip: $skip_out"$'\n'"only: $only_out"
|
|
922
|
+
|
|
923
|
+
# A typo must never silently drop a safety sweep, so anything unrecognized
|
|
924
|
+
# resolves to the complete run.
|
|
925
|
+
[ "$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
926
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 FM_BOOTSTRAP_NETWORK=sikp "$ROOT/bin/fm-bootstrap.sh")" = "$all_out" ] \
|
|
927
|
+
|| fail "an unrecognized FM_BOOTSTRAP_NETWORK value did not fall back to the complete run"
|
|
928
|
+
pass "bootstrap: FM_BOOTSTRAP_NETWORK partitions one run into local and network halves"
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
test_network_sweeps_recheck_lock_ownership() {
|
|
932
|
+
local case_dir fakebin fake_root marker out
|
|
933
|
+
case_dir="$TMP_ROOT/network-lock-handoff"
|
|
934
|
+
mkdir -p "$case_dir/home/config" "$case_dir/home/projects" "$case_dir/home/state"
|
|
935
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
936
|
+
printf '222222\n' > "$case_dir/home/state/.lock"
|
|
937
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
938
|
+
fake_root="$case_dir/root"
|
|
939
|
+
marker="$case_dir/fleet-sync.started"
|
|
940
|
+
mkdir -p "$fake_root/bin"
|
|
941
|
+
cat > "$fake_root/bin/fm-fleet-sync.sh" <<'SH'
|
|
942
|
+
#!/usr/bin/env bash
|
|
943
|
+
: > "${FM_FAKE_FLEET_SYNC_STARTED_MARKER:?}"
|
|
944
|
+
SH
|
|
945
|
+
chmod +x "$fake_root/bin/fm-fleet-sync.sh"
|
|
946
|
+
|
|
947
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$fake_root" \
|
|
948
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 FM_BOOTSTRAP_NETWORK=only \
|
|
949
|
+
FM_BOOTSTRAP_NETWORK_LOCK_PID=111111 FM_FAKE_FLEET_SYNC_STARTED_MARKER="$marker" \
|
|
950
|
+
"$ROOT/bin/fm-bootstrap.sh")
|
|
951
|
+
assert_absent "$marker" "a stale worker refreshed project clones after lock handoff"
|
|
952
|
+
assert_contains "$out" "changed before dead-secondmate relaunch" \
|
|
953
|
+
"the stale worker did not report the refused liveness sweep"
|
|
954
|
+
assert_contains "$out" "changed before secondmate convergence" \
|
|
955
|
+
"the stale worker did not report the refused convergence sweep"
|
|
956
|
+
assert_contains "$out" "changed before pending handoff delivery" \
|
|
957
|
+
"the stale worker did not report the refused handoff sweep"
|
|
958
|
+
assert_contains "$out" "changed before project clone refresh" \
|
|
959
|
+
"the stale worker did not report the refused clone refresh"
|
|
960
|
+
pass "bootstrap: every deferred mutating sweep rechecks fleet-lock ownership"
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
# The verdict costs three subprocesses, so a caller that already has it can hand
|
|
964
|
+
# it over - but only one hop, and never onward into a spawned agent's
|
|
965
|
+
# environment, where it could outlive a tasks-axi upgrade.
|
|
966
|
+
# assert_timing_record <log> <scope> <name> <detail> <msg>: one bin/fm-timing-lib.sh
|
|
967
|
+
# record with exactly these fields must exist. Field-exact rather than a substring
|
|
968
|
+
# match, so a detail that landed in the wrong column cannot pass.
|
|
969
|
+
assert_timing_record() {
|
|
970
|
+
local log=$1 scope=$2 name=$3 detail=$4 msg=$5
|
|
971
|
+
awk -F'\t' -v s="$scope" -v n="$name" -v d="$detail" '
|
|
972
|
+
$1 == "v1" && $2 == s && $3 == n && $6 == d { found = 1 }
|
|
973
|
+
END { exit found ? 0 : 1 }
|
|
974
|
+
' "$log" || fail "$msg"$'\n'"$(cat "$log")"
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
# The deferred network stage publishes ONE started/finished pair, so a slow run
|
|
978
|
+
# used to be unattributable without re-running it by hand. These are the records
|
|
979
|
+
# that make it attributable, and they must come from the real sweeps rather than
|
|
980
|
+
# a stand-in: what is being pinned is that each network owner is actually wrapped.
|
|
981
|
+
# bin/fm-timing-lib.sh stays inert unless FM_TIMING_LOG names a file, so an
|
|
982
|
+
# ordinary bootstrap run is unaffected either way, which is asserted here too.
|
|
983
|
+
test_network_phases_record_per_step_elapsed_times() {
|
|
984
|
+
local case_dir fakebin log fields
|
|
985
|
+
case_dir="$TMP_ROOT/network-timings"
|
|
986
|
+
mkdir -p "$case_dir/home/config" "$case_dir/home/state" "$case_dir/home/data" "$case_dir/home/projects"
|
|
987
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
988
|
+
printf '%s\n' $$ > "$case_dir/home/state/.lock"
|
|
989
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
990
|
+
# A real clone with a real origin, so fm-fleet-sync.sh genuinely iterates it.
|
|
991
|
+
fm_git_init_commit "$case_dir/home/projects/alpha"
|
|
992
|
+
fm_git_add_origin "$case_dir/home/projects/alpha" "$case_dir/alpha-origin"
|
|
993
|
+
# A secondmate the liveness sweep must account for. Whatever verdict it reaches
|
|
994
|
+
# is owned elsewhere; what matters here is that the step is measured.
|
|
995
|
+
fm_write_secondmate_meta "$case_dir/home/state/mate-a.meta" "$case_dir/home"
|
|
996
|
+
|
|
997
|
+
log="$case_dir/timings.tsv"
|
|
998
|
+
PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$ROOT" \
|
|
999
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 FM_BOOTSTRAP_NETWORK=only \
|
|
1000
|
+
FM_BOOTSTRAP_NETWORK_LOCK_PID=$$ FM_TIMING_LOG="$log" FM_TIMING_EPOCH_MS=0 \
|
|
1001
|
+
"$ROOT/bin/fm-bootstrap.sh" >/dev/null 2>&1
|
|
1002
|
+
|
|
1003
|
+
assert_present "$log" "the network phase recorded no elapsed times at all"
|
|
1004
|
+
assert_timing_record "$log" phase gh-auth '' "the GitHub auth probe was not timed"
|
|
1005
|
+
assert_timing_record "$log" phase secondmate-liveness '' "the dead-secondmate relaunch sweep was not timed"
|
|
1006
|
+
assert_timing_record "$log" phase secondmate-sync '' "the secondmate convergence sweep was not timed"
|
|
1007
|
+
assert_timing_record "$log" phase handoff-delivery '' "the pending handoff sweep was not timed"
|
|
1008
|
+
assert_timing_record "$log" phase fleet-sync '' "the project clone refresh was not timed"
|
|
1009
|
+
assert_timing_record "$log" secondmate liveness mate-a \
|
|
1010
|
+
"the liveness sweep was not attributed to the individual secondmate it checked"
|
|
1011
|
+
assert_timing_record "$log" clone sync alpha \
|
|
1012
|
+
"the clone refresh was not attributed to the individual clone it refreshed"
|
|
1013
|
+
|
|
1014
|
+
# Every record carries a start offset and an elapsed time, both numeric, so the
|
|
1015
|
+
# artifact can be read as a timeline rather than a bag of durations.
|
|
1016
|
+
fields=$(awk -F'\t' '$4 ~ /^[0-9]+$/ && $5 ~ /^[0-9]+$/ { n++ } END { print n+0 }' "$log")
|
|
1017
|
+
[ "$fields" = "$(grep -c . "$log")" ] \
|
|
1018
|
+
|| fail "some records lack a numeric start offset and elapsed time: $(cat "$log")"
|
|
1019
|
+
|
|
1020
|
+
# And an ordinary run - the local half, or any caller that never asked for
|
|
1021
|
+
# timings - writes nothing anywhere.
|
|
1022
|
+
rm -f "$log"
|
|
1023
|
+
PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$ROOT" \
|
|
1024
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 FM_BOOTSTRAP_NETWORK=only \
|
|
1025
|
+
FM_BOOTSTRAP_NETWORK_LOCK_PID=$$ \
|
|
1026
|
+
"$ROOT/bin/fm-bootstrap.sh" >/dev/null 2>&1
|
|
1027
|
+
assert_absent "$log" "a run that never asked for timings recorded them anyway"
|
|
1028
|
+
pass "bootstrap: each deferred network phase, secondmate, and clone records its own elapsed time"
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
test_tasks_axi_verdict_handoff_is_consumed_once() {
|
|
1032
|
+
local case_dir fakebin log out
|
|
1033
|
+
case_dir="$TMP_ROOT/tasks-axi-handoff"
|
|
1034
|
+
mkdir -p "$case_dir/home/config"
|
|
1035
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
1036
|
+
log="$case_dir/tasks-axi.log"
|
|
1037
|
+
cat > "$fakebin/tasks-axi" <<'SH'
|
|
1038
|
+
#!/usr/bin/env bash
|
|
1039
|
+
printf '%s\n' "$*" >> "${FM_FAKE_TASKS_AXI_LOG:?}"
|
|
1040
|
+
printf '0.0.1\n'
|
|
1041
|
+
exit 0
|
|
1042
|
+
SH
|
|
1043
|
+
chmod +x "$fakebin/tasks-axi"
|
|
1044
|
+
|
|
1045
|
+
# Without the handoff, the incompatible stub is probed and reported.
|
|
1046
|
+
: > "$log"
|
|
1047
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
1048
|
+
FM_FAKE_TASKS_AXI_LOG="$log" FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
1049
|
+
assert_contains "$out" "MISSING: tasks-axi (install:" "the unaided run did not probe tasks-axi"
|
|
1050
|
+
assert_grep '--version' "$log" "the unaided run never ran the probe"
|
|
1051
|
+
|
|
1052
|
+
# With it, the probe is skipped entirely and the handed-in verdict is used.
|
|
1053
|
+
: > "$log"
|
|
1054
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
1055
|
+
FM_FAKE_TASKS_AXI_LOG="$log" FM_FAKE_TREEHOUSE_LEASE_HELP=1 \
|
|
1056
|
+
FM_TASKS_AXI_COMPATIBLE=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
1057
|
+
assert_not_contains "$out" "MISSING: tasks-axi" "the handed-in verdict was ignored"
|
|
1058
|
+
[ ! -s "$log" ] || fail "the handed-in verdict did not save the probe: $(cat "$log")"
|
|
1059
|
+
|
|
1060
|
+
# A malformed value is not a verdict.
|
|
1061
|
+
: > "$log"
|
|
1062
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
1063
|
+
FM_FAKE_TASKS_AXI_LOG="$log" FM_FAKE_TREEHOUSE_LEASE_HELP=1 \
|
|
1064
|
+
FM_TASKS_AXI_COMPATIBLE=yes "$ROOT/bin/fm-bootstrap.sh")
|
|
1065
|
+
assert_contains "$out" "MISSING: tasks-axi (install:" "a malformed handoff value was trusted"
|
|
1066
|
+
|
|
1067
|
+
# And the handoff never reaches a grandchild: bootstrap spawns agents, and a
|
|
1068
|
+
# verdict cached into an agent's environment would outlive the tool it describes.
|
|
1069
|
+
out=$(FM_TASKS_AXI_COMPATIBLE=1 bash -c '. "$1"; printf "%s\n" "${FM_TASKS_AXI_COMPATIBLE-unset}"' \
|
|
1070
|
+
_ "$ROOT/bin/fm-tasks-axi-lib.sh")
|
|
1071
|
+
[ "$out" = unset ] || fail "sourcing the library left the handoff in the environment: $out"
|
|
1072
|
+
pass "bootstrap: the tasks-axi compatibility verdict travels exactly one process hop"
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
test_crew_dispatch_active_rules_are_verbose_bootstrap_info() {
|
|
1076
|
+
local case_dir fakebin out expect
|
|
1077
|
+
case_dir="$TMP_ROOT/dispatch-active"
|
|
1078
|
+
mkdir -p "$case_dir/home/config"
|
|
1079
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
1080
|
+
printf '%s\n' '{"rules":[{"when":"fresh news","use":{"harness":"grok"},"why":"current context"},{"when":"big feature","use":[{"harness":"claude","model":"claude-sonnet-5","effort":"high"},{"harness":"codex","model":"gpt-5.5","effort":"high"}]},{"when":"legacy feature","use":[{"harness":"claude"},{"harness":"codex"}],"select":"quota-balanced"}],"default":[{"harness":"pi","model":"anthropic/claude-sonnet-5","effort":"high"},{"harness":"grok","model":"grok-4.5","effort":"high"}]}' > "$case_dir/home/config/crew-dispatch.json"
|
|
1081
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
1082
|
+
add_real_jq "$fakebin"
|
|
1083
|
+
|
|
1084
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
1085
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
1086
|
+
[ -z "$out" ] || fail "active dispatch profile should be silent by default, got: $out"
|
|
1087
|
+
|
|
1088
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
1089
|
+
FM_BOOTSTRAP_VERBOSE_FACTS=1 FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
1090
|
+
|
|
1091
|
+
expect=$'BOOTSTRAP_INFO: crew dispatch active config/crew-dispatch.json\nBOOTSTRAP_INFO: crew dispatch rule: fresh news -> grok\nBOOTSTRAP_INFO: crew dispatch rule: big feature -> quota-balanced[claude/claude-sonnet-5/high, codex/gpt-5.5/high]\nBOOTSTRAP_INFO: crew dispatch rule: legacy feature -> quota-balanced[claude, codex]\nBOOTSTRAP_INFO: crew dispatch default: quota-balanced[pi/anthropic/claude-sonnet-5/high, grok/grok-4.5/high]'
|
|
1092
|
+
[ "$out" = "$expect" ] || fail "active dispatch verbose info block mismatch"$'\n'"expected: $expect"$'\n'"actual: $out"
|
|
1093
|
+
pass "bootstrap surfaces active crew-dispatch rules only as verbose BOOTSTRAP_INFO"
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
test_crew_dispatch_validation() {
|
|
1097
|
+
local label body expect mode case_dir fakebin out n
|
|
1098
|
+
n=0
|
|
1099
|
+
while IFS='^' read -r label body mode expect; do
|
|
1100
|
+
[ -n "$label" ] || continue
|
|
1101
|
+
n=$((n + 1))
|
|
1102
|
+
case_dir="$TMP_ROOT/dispatch-$n"
|
|
1103
|
+
mkdir -p "$case_dir/home/config"
|
|
1104
|
+
printf '%s\n' manual > "$case_dir/home/config/backlog-backend"
|
|
1105
|
+
printf '%s\n' "$body" > "$case_dir/home/config/crew-dispatch.json"
|
|
1106
|
+
fakebin=$(make_fake_toolchain "$case_dir")
|
|
1107
|
+
add_real_jq "$fakebin"
|
|
1108
|
+
out=$(PATH="$fakebin:$BASE_PATH" FM_HOME="$case_dir/home" FM_ROOT_OVERRIDE="$case_dir/home" \
|
|
1109
|
+
FM_FAKE_TREEHOUSE_LEASE_HELP=1 "$ROOT/bin/fm-bootstrap.sh")
|
|
1110
|
+
case "$mode" in
|
|
1111
|
+
empty)
|
|
1112
|
+
[ -z "$out" ] || fail "$label: expected silence, got: $out" ;;
|
|
1113
|
+
exact)
|
|
1114
|
+
[ "$out" = "$expect" ] || fail "$label: expected '$expect', got: $out" ;;
|
|
1115
|
+
grep)
|
|
1116
|
+
printf '%s\n' "$out" | grep -Fx "$expect" >/dev/null || fail "$label: missing '$expect' (got: $out)" ;;
|
|
1117
|
+
esac
|
|
1118
|
+
done <<'ROWS'
|
|
1119
|
+
malformed dispatch config is flagged^{"rules":[^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - malformed JSON
|
|
1120
|
+
unverified dispatch harness is flagged^{"rules":[{"when":"anything","use":{"harness":"spaceship"}}],"default":{"harness":"codex"}}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - unverified harness: spaceship
|
|
1121
|
+
unsupported codex max effort is flagged^{"rules":[{"when":"big feature","use":{"harness":"codex","model":"gpt-5","effort":"max"}}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - invalid effort: codex:max
|
|
1122
|
+
unsupported grok max effort is flagged^{"rules":[{"when":"deep current work","use":{"harness":"grok","model":"grok-4","effort":"max"}}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - invalid effort: grok:max
|
|
1123
|
+
unsupported grok xhigh effort is flagged^{"rules":[{"when":"deep current work","use":{"harness":"grok","model":"grok-4","effort":"xhigh"}}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - invalid effort: grok:xhigh
|
|
1124
|
+
pi max effort is accepted^{"rules":[{"when":"deep coding","use":{"harness":"pi","model":"openai-codex/gpt-5.6-sol","effort":"max"}}]}^empty^
|
|
1125
|
+
pi-signed max effort is accepted^{"rules":[{"when":"signed coding","use":{"harness":"pi-signed","model":"openai-codex/gpt-5.6-sol","effort":"max"}}]}^empty^
|
|
1126
|
+
muse shared efforts are accepted^{"rules":[{"when":"muse low","use":{"harness":"muse","effort":"low"}},{"when":"muse medium","use":{"harness":"muse","effort":"medium"}},{"when":"muse high","use":{"harness":"muse","effort":"high"}},{"when":"muse xhigh","use":{"harness":"muse","effort":"xhigh"}},{"when":"muse max","use":{"harness":"muse","effort":"max"}}]}^empty^
|
|
1127
|
+
unsupported muse ultra effort is flagged^{"rules":[{"when":"muse ultra","use":{"harness":"muse","effort":"ultra"}}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - invalid effort: muse:ultra
|
|
1128
|
+
unsupported opencode effort is flagged^{"rules":[{"when":"opencode work","use":{"harness":"opencode","model":"anthropic/claude-sonnet-4-5","effort":"high"}}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - invalid effort: opencode:high
|
|
1129
|
+
kimi model profile is accepted^{"rules":[{"when":"kimi work","use":{"harness":"kimi","model":"kimi-code/k3"}}]}^empty^
|
|
1130
|
+
unsupported kimi effort is flagged^{"rules":[{"when":"kimi work","use":{"harness":"kimi","model":"kimi-code/k3","effort":"high"}}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - invalid effort: kimi:high
|
|
1131
|
+
cursor model profile is accepted^{"rules":[{"when":"cursor work","use":{"harness":"cursor","model":"cursor-grok-4.5-high"}}]}^empty^
|
|
1132
|
+
unsupported cursor effort is flagged^{"rules":[{"when":"cursor work","use":{"harness":"cursor","model":"cursor-grok-4.5-high","effort":"high"}}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - invalid effort: cursor:high
|
|
1133
|
+
array use with quota-balanced is accepted^{"rules":[{"when":"big feature","use":[{"harness":"claude","model":"claude-sonnet-5","effort":"high"},{"harness":"codex","model":"gpt-5.5","effort":"high"}],"select":"quota-balanced"}]}^empty^
|
|
1134
|
+
array use without select is accepted^{"rules":[{"when":"big feature","use":[{"harness":"claude"},{"harness":"codex"}]}]}^empty^
|
|
1135
|
+
one-element array use is accepted^{"rules":[{"when":"focused feature","use":[{"harness":"claude"}]}]}^empty^
|
|
1136
|
+
default array is accepted^{"default":[{"harness":"pi","model":"anthropic/claude-sonnet-5"},{"harness":"grok"}]}^empty^
|
|
1137
|
+
one-element default array is accepted^{"default":[{"harness":"codex"}]}^empty^
|
|
1138
|
+
empty array use is flagged^{"rules":[{"when":"big feature","use":[]}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - each rule needs at least one use profile
|
|
1139
|
+
array profile without harness is flagged^{"rules":[{"when":"big feature","use":[{"model":"gpt-5.5"}]}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - each use profile needs harness
|
|
1140
|
+
array profile with malformed model is flagged^{"rules":[{"when":"big feature","use":[{"harness":"codex","model":5}]}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - use profile model and effort must be non-empty strings when present
|
|
1141
|
+
unknown select is flagged^{"rules":[{"when":"big feature","use":[{"harness":"claude"},{"harness":"codex"}],"select":"mystery"}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - unknown select: mystery
|
|
1142
|
+
array profile unsupported effort is flagged^{"rules":[{"when":"big feature","use":[{"harness":"codex","effort":"max"}]}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - invalid effort: codex:max
|
|
1143
|
+
empty default array is flagged^{"default":[]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - default needs at least one profile
|
|
1144
|
+
non-object default array entry is flagged^{"default":["codex"]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - each default profile must be an object
|
|
1145
|
+
default array profile without harness is flagged^{"default":[{"model":"gpt-5.5"}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - each default profile needs harness
|
|
1146
|
+
default array malformed effort is flagged^{"default":[{"harness":"codex","effort":3}]}^exact^CREW_DISPATCH: invalid config/crew-dispatch.json - default profile model and effort must be non-empty strings when present
|
|
1147
|
+
ROWS
|
|
1148
|
+
pass "bootstrap validates crew-dispatch.json and reports malformed or unverified configs"
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
test_bootstrap_reporting
|
|
1152
|
+
test_no_mistakes_min_version
|
|
1153
|
+
test_gh_axi_min_version
|
|
1154
|
+
test_lavish_axi_min_version
|
|
1155
|
+
test_tasks_axi_min_version
|
|
1156
|
+
test_quota_axi_min_version
|
|
1157
|
+
test_git_is_required_with_supported_install_instruction
|
|
1158
|
+
test_orca_backend_gates_orca_tool_only_when_selected
|
|
1159
|
+
test_session_provider_backends_do_not_require_tmux
|
|
1160
|
+
test_session_provider_backends_gate_own_cli_not_tmux
|
|
1161
|
+
test_herdr_install_requires_manual_action
|
|
1162
|
+
test_cmux_bundled_cli_satisfies_dependency
|
|
1163
|
+
test_unknown_backend_reports_invalid_configuration
|
|
1164
|
+
test_json_backends_require_jq_not_tmux
|
|
1165
|
+
test_treehouse_lease_check_follows_resolved_backend
|
|
1166
|
+
test_fleet_sync_timeout_scales_with_origin_backed_project_count
|
|
1167
|
+
test_fleet_sync_timeout_floor_preserves_small_fleets
|
|
1168
|
+
test_fleet_sync_timeout_explicit_override_wins
|
|
1169
|
+
test_fleet_sync_timeout_empty_override_uses_default
|
|
1170
|
+
test_fleet_sync_timeout_is_computed_before_launch
|
|
1171
|
+
test_routine_bootstrap_confirmations_are_silent
|
|
1172
|
+
test_routine_bootstrap_contract_runs_under_system_bash
|
|
1173
|
+
test_network_phase_partitions_the_run
|
|
1174
|
+
test_network_sweeps_recheck_lock_ownership
|
|
1175
|
+
test_network_phases_record_per_step_elapsed_times
|
|
1176
|
+
test_tasks_axi_verdict_handoff_is_consumed_once
|
|
1177
|
+
test_crew_dispatch_active_rules_are_verbose_bootstrap_info
|
|
1178
|
+
test_crew_dispatch_validation
|