@zerwiz/ymir 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/README.md +291 -0
- package/.agents/agents/bragi-marketer.md +111 -0
- package/.agents/agents/brokk.md +352 -0
- package/.agents/agents/forseti-reviewer.md +93 -0
- package/.agents/agents/frigg-consent.md +77 -0
- package/.agents/agents/groa-update.md +77 -0
- package/.agents/agents/hnoss-designer.md +69 -0
- package/.agents/agents/huginn-researcher.md +99 -0
- package/.agents/agents/jord-projects.md +78 -0
- package/.agents/agents/kvasir-scout.md +91 -0
- package/.agents/agents/mimir-planner.md +92 -0
- package/.agents/agents/muninn-stow.md +77 -0
- package/.agents/agents/saga-bearings.md +78 -0
- package/.agents/agents/sindri-developer.md +114 -0
- package/.agents/agents/snotra-documenter.md +105 -0
- package/.agents/agents/syn-recovery.md +78 -0
- package/.agents/agents/tyr-check.md +78 -0
- package/.agents/agents/urdh-hold.md +77 -0
- package/.agents/agents/volundr-smith.md +80 -0
- package/.agents/agents/vor-diagnostics.md +77 -0
- package/.agents/assets/agents/naming.md +50 -0
- package/.agents/assets/agents/registry.md +93 -0
- package/.agents/assets/agents/runtime.md +36 -0
- package/.agents/assets/agents/toon-tasks-cli.md +42 -0
- package/.agents/assets/templates/company_entity.template.md +37 -0
- package/.agents/backend/backends/cmux.sh +649 -0
- package/.agents/backend/backends/herdr-eventwait.py +157 -0
- package/.agents/backend/backends/herdr-workspace-move.py +114 -0
- package/.agents/backend/backends/herdr.sh +3386 -0
- package/.agents/backend/backends/orca.sh +290 -0
- package/.agents/backend/backends/tmux.sh +357 -0
- package/.agents/backend/backends/zellij.sh +700 -0
- package/.agents/backend/fm-afk-launch.sh +654 -0
- package/.agents/backend/fm-afk-return.sh +241 -0
- package/.agents/backend/fm-afk-start.sh +172 -0
- package/.agents/backend/fm-arm-command-policy.mjs +960 -0
- package/.agents/backend/fm-arm-pretool-check.sh +200 -0
- package/.agents/backend/fm-backend-hometag-lib.sh +52 -0
- package/.agents/backend/fm-backend.sh +981 -0
- package/.agents/backend/fm-backlog-handoff.sh +909 -0
- package/.agents/backend/fm-backlog-receive.sh +187 -0
- package/.agents/backend/fm-backlog-transition-lib.sh +779 -0
- package/.agents/backend/fm-bearings-board.sh +204 -0
- package/.agents/backend/fm-bearings-snapshot.sh +546 -0
- package/.agents/backend/fm-bootstrap.sh +1568 -0
- package/.agents/backend/fm-branch-outcome.sh +203 -0
- package/.agents/backend/fm-branch-prompt.sh +101 -0
- package/.agents/backend/fm-brief.sh +452 -0
- package/.agents/backend/fm-busy-event.sh +234 -0
- package/.agents/backend/fm-busy-lib.sh +986 -0
- package/.agents/backend/fm-captain-hold.sh +1001 -0
- package/.agents/backend/fm-cd-command-policy.mjs +153 -0
- package/.agents/backend/fm-cd-pretool-check.sh +190 -0
- package/.agents/backend/fm-check-lib.sh +74 -0
- package/.agents/backend/fm-check-register.sh +42 -0
- package/.agents/backend/fm-check-unregister.sh +52 -0
- package/.agents/backend/fm-classify-lib.sh +1770 -0
- package/.agents/backend/fm-claude-stop-autoarm.sh +334 -0
- package/.agents/backend/fm-composer-lib.sh +1416 -0
- package/.agents/backend/fm-config-inherit-lib.sh +1202 -0
- package/.agents/backend/fm-config-push.sh +254 -0
- package/.agents/backend/fm-control-lib.sh +251 -0
- package/.agents/backend/fm-control.sh +878 -0
- package/.agents/backend/fm-crew-state.sh +625 -0
- package/.agents/backend/fm-cursor-lib.sh +243 -0
- package/.agents/backend/fm-decision-hold.sh +232 -0
- package/.agents/backend/fm-doc-audience-check.sh +269 -0
- package/.agents/backend/fm-dod-lib.sh +67 -0
- package/.agents/backend/fm-ensure-agents-md.sh +253 -0
- package/.agents/backend/fm-extension-launch-barrier.mjs +129 -0
- package/.agents/backend/fm-extension.mjs +2577 -0
- package/.agents/backend/fm-extension.sh +16 -0
- package/.agents/backend/fm-ff-lib.sh +420 -0
- package/.agents/backend/fm-fleet-snapshot.sh +1463 -0
- package/.agents/backend/fm-fleet-sync.sh +458 -0
- package/.agents/backend/fm-fleet-view.sh +96 -0
- package/.agents/backend/fm-gate-refuse-lib.sh +102 -0
- package/.agents/backend/fm-guard.sh +242 -0
- package/.agents/backend/fm-harness.sh +195 -0
- package/.agents/backend/fm-herdr-ci-cleanup.sh +115 -0
- package/.agents/backend/fm-herdr-lab.sh +346 -0
- package/.agents/backend/fm-herdr-session-cleanup.sh +337 -0
- package/.agents/backend/fm-home-seed.sh +984 -0
- package/.agents/backend/fm-home-summary-refresh.sh +255 -0
- package/.agents/backend/fm-hook-host-lib.sh +36 -0
- package/.agents/backend/fm-inactive-reconcile.sh +531 -0
- package/.agents/backend/fm-inbox.sh +399 -0
- package/.agents/backend/fm-install-actionlint.sh +84 -0
- package/.agents/backend/fm-install-herdr.sh +96 -0
- package/.agents/backend/fm-install-shellcheck.sh +84 -0
- package/.agents/backend/fm-install-treehouse.sh +95 -0
- package/.agents/backend/fm-kimi-turnend-hook.sh +276 -0
- package/.agents/backend/fm-lease-lib.sh +218 -0
- package/.agents/backend/fm-lease.sh +189 -0
- package/.agents/backend/fm-line-cap-lib.sh +51 -0
- package/.agents/backend/fm-lint-workflows.sh +137 -0
- package/.agents/backend/fm-lint.sh +591 -0
- package/.agents/backend/fm-lock-lib.sh +104 -0
- package/.agents/backend/fm-lock.sh +107 -0
- package/.agents/backend/fm-marker-lib.sh +12 -0
- package/.agents/backend/fm-merge-local.sh +74 -0
- package/.agents/backend/fm-merge-outcome-lib.sh +138 -0
- package/.agents/backend/fm-nm-run-lib.sh +124 -0
- package/.agents/backend/fm-on.sh +125 -0
- package/.agents/backend/fm-operational-input.sh +253 -0
- package/.agents/backend/fm-peek.sh +46 -0
- package/.agents/backend/fm-pending-reply-lib.sh +1409 -0
- package/.agents/backend/fm-pr-check.sh +135 -0
- package/.agents/backend/fm-pr-lib.sh +1016 -0
- package/.agents/backend/fm-pr-merge.sh +707 -0
- package/.agents/backend/fm-pr-poll.sh +110 -0
- package/.agents/backend/fm-primary-scope-lib.sh +33 -0
- package/.agents/backend/fm-procevent-extension-capture.pl +259 -0
- package/.agents/backend/fm-procevent-lavish.sh +628 -0
- package/.agents/backend/fm-procevent-lib.sh +892 -0
- package/.agents/backend/fm-procevent-quota.sh +290 -0
- package/.agents/backend/fm-procevent-remote-reply.sh +577 -0
- package/.agents/backend/fm-procevent-when.sh +504 -0
- package/.agents/backend/fm-procevent.sh +1466 -0
- package/.agents/backend/fm-project-mode.sh +92 -0
- package/.agents/backend/fm-project-origin-lib.sh +180 -0
- package/.agents/backend/fm-promote.sh +282 -0
- package/.agents/backend/fm-public-followup-emit.sh +260 -0
- package/.agents/backend/fm-public-followup-lib.sh +398 -0
- package/.agents/backend/fm-public-followup.sh +1330 -0
- package/.agents/backend/fm-push-transition-lib.sh +168 -0
- package/.agents/backend/fm-quota-axi-lib.sh +93 -0
- package/.agents/backend/fm-quota-choose.sh +384 -0
- package/.agents/backend/fm-remote-delta-read.sh +189 -0
- package/.agents/backend/fm-remote-doctor.sh +800 -0
- package/.agents/backend/fm-remote-entrypoint.sh +193 -0
- package/.agents/backend/fm-remote-file.sh +234 -0
- package/.agents/backend/fm-remote-home-provision.sh +259 -0
- package/.agents/backend/fm-remote-home-seed.sh +259 -0
- package/.agents/backend/fm-remote-inherit-push.sh +90 -0
- package/.agents/backend/fm-remote-inherit.sh +179 -0
- package/.agents/backend/fm-remote-job-lib.sh +1241 -0
- package/.agents/backend/fm-remote-job-reap-orphans.sh +137 -0
- package/.agents/backend/fm-remote-job-worker.sh +1124 -0
- package/.agents/backend/fm-remote-readiness-lib.sh +44 -0
- package/.agents/backend/fm-remote-secondmate-control.sh +340 -0
- package/.agents/backend/fm-review-diff.sh +158 -0
- package/.agents/backend/fm-secondmate-charter-lib.sh +44 -0
- package/.agents/backend/fm-secondmate-nudge-lib.sh +68 -0
- package/.agents/backend/fm-secondmate-parent-lib.sh +70 -0
- package/.agents/backend/fm-secondmate-reconcile.sh +371 -0
- package/.agents/backend/fm-secondmate-registry-lib.sh +311 -0
- package/.agents/backend/fm-secondmate-report.sh +88 -0
- package/.agents/backend/fm-send.sh +1059 -0
- package/.agents/backend/fm-session-lock-lib.sh +176 -0
- package/.agents/backend/fm-session-start.sh +970 -0
- package/.agents/backend/fm-sessionstart-cursor.sh +40 -0
- package/.agents/backend/fm-sessionstart-nudge.sh +45 -0
- package/.agents/backend/fm-sessionstart-run.sh +145 -0
- package/.agents/backend/fm-spawn.sh +3153 -0
- package/.agents/backend/fm-startup-memory-budget-lib.sh +224 -0
- package/.agents/backend/fm-startup-memory-budget.sh +94 -0
- package/.agents/backend/fm-startup-network.sh +642 -0
- package/.agents/backend/fm-stow-cascade.sh +251 -0
- package/.agents/backend/fm-subagent-pretool-check.sh +207 -0
- package/.agents/backend/fm-supervise-daemon.sh +1732 -0
- package/.agents/backend/fm-supervision-instructions.sh +215 -0
- package/.agents/backend/fm-supervision-lib.sh +90 -0
- package/.agents/backend/fm-supervisor-target-lib.sh +78 -0
- package/.agents/backend/fm-tangle-lib.sh +53 -0
- package/.agents/backend/fm-task-inbox-lib.sh +402 -0
- package/.agents/backend/fm-tasks-axi-lib.sh +123 -0
- package/.agents/backend/fm-teardown.sh +2922 -0
- package/.agents/backend/fm-test-isolation-proof.sh +588 -0
- package/.agents/backend/fm-test-run.sh +2191 -0
- package/.agents/backend/fm-timeout-lib.sh +141 -0
- package/.agents/backend/fm-timing-lib.sh +179 -0
- package/.agents/backend/fm-tmux-lib.sh +291 -0
- package/.agents/backend/fm-tool-update-check.sh +898 -0
- package/.agents/backend/fm-trace-context-lib.sh +227 -0
- package/.agents/backend/fm-transition-lib.sh +103 -0
- package/.agents/backend/fm-turnend-guard-cursor.sh +391 -0
- package/.agents/backend/fm-turnend-guard-grok.sh +90 -0
- package/.agents/backend/fm-turnend-guard.sh +432 -0
- package/.agents/backend/fm-update.sh +110 -0
- package/.agents/backend/fm-vendor-auth-probe.sh +190 -0
- package/.agents/backend/fm-voice-client.py +1373 -0
- package/.agents/backend/fm-voice-relay.py +1256 -0
- package/.agents/backend/fm-wake-drain.sh +592 -0
- package/.agents/backend/fm-wake-grant.sh +127 -0
- package/.agents/backend/fm-wake-lib.sh +1849 -0
- package/.agents/backend/fm-watch-arm.sh +602 -0
- package/.agents/backend/fm-watch-checkpoint.sh +109 -0
- package/.agents/backend/fm-watch.sh +1962 -0
- package/.agents/backend/fm-x-dismiss.sh +116 -0
- package/.agents/backend/fm-x-followup.sh +286 -0
- package/.agents/backend/fm-x-lib.sh +1000 -0
- package/.agents/backend/fm-x-link.sh +234 -0
- package/.agents/backend/fm-x-poll.sh +185 -0
- package/.agents/backend/fm-x-reply.sh +385 -0
- package/.agents/backend/fm_voice_frame.py +166 -0
- package/.agents/backend/fm_voice_records.py +574 -0
- package/.agents/backend/model-bridge.py +290 -0
- package/.agents/backend/opencode-go-bridge.py +30 -0
- package/.agents/bus/README.md +12 -0
- package/.agents/bus/messages.json +1 -0
- package/.agents/config/agents.machine.example.yaml +16 -0
- package/.agents/config/agents.yaml +50 -0
- package/.agents/config/agents.yaml.example +72 -0
- package/.agents/config/crew-dispatch.json +22 -0
- package/.agents/config/cron.yaml +9 -0
- package/.agents/config/eindri-dispatch.json +67 -0
- package/.agents/config/eindri-harness +1 -0
- package/.agents/config/startup-memory-budget +1 -0
- package/.agents/config/tailscale-sync.yaml +27 -0
- package/.agents/filebrowser/.filebrowser.json +6 -0
- package/.agents/filebrowser/README.md +11 -0
- package/.agents/gateway/README.md +12 -0
- package/.agents/github/README.md +15 -0
- package/.agents/github/webhooks/.gitkeep +0 -0
- package/.agents/github/workflows/.gitkeep +0 -0
- package/.agents/harness/opencode/plugins/lib/rodd-operational-input.js +37 -0
- package/.agents/harness/opencode/plugins/package.json +4 -0
- package/.agents/harness/opencode/plugins/saga-sessionstart.js +77 -0
- package/.agents/harness/opencode/plugins/syn-cd-check.js +61 -0
- package/.agents/harness/opencode/plugins/syn-pretool-check.js +68 -0
- package/.agents/harness/opencode/plugins/syn-turnend-guard.js +105 -0
- package/.agents/harness/opencode/plugins/syn-watch-arm.js +521 -0
- package/.agents/memory/README.md +46 -0
- package/.agents/memory/kaia.engram +0 -0
- package/.agents/memory/kaia.engram-shm +0 -0
- package/.agents/memory/kaia.engram-wal +0 -0
- package/.agents/memory/runes_audit.md +5 -0
- package/.agents/memory/well/episodes.jsonl +367 -0
- package/.agents/memory/well/workspace.jsonl +27 -0
- package/.agents/migrations/0001-hodd-layout.sh +38 -0
- package/.agents/migrations/0002-a2a-mcp.sh +27 -0
- package/.agents/migrations/0003-private-data-separation.sh +111 -0
- package/.agents/migrations/0004-hoard-and-realms.sh +104 -0
- package/.agents/sandbox/Dockerfile.utgard +19 -0
- package/.agents/sandbox/README.md +14 -0
- package/.agents/sandbox/utgard.config.json +14 -0
- package/.agents/skills/README.md +59 -0
- package/.agents/skills/bragi-marketing/README.md +3 -0
- package/.agents/skills/bragi-marketing/SKILL.md +82 -0
- package/.agents/skills/eindri-homes/README.md +3 -0
- package/.agents/skills/eindri-homes/SKILL.md +291 -0
- package/.agents/skills/eindri-homes/assets/control-plane.md +122 -0
- package/.agents/skills/eindri-homes/assets/dispatch.md +80 -0
- package/.agents/skills/frigg-consent/README.md +3 -0
- package/.agents/skills/frigg-consent/SKILL.md +56 -0
- package/.agents/skills/galdr-ymirsystem/README.md +3 -0
- package/.agents/skills/galdr-ymirsystem/SKILL.md +158 -0
- package/.agents/skills/galdr-ymirsystem/assets/README.md +74 -0
- package/.agents/skills/galdr-ymirsystem/assets/brokk-distro-runtime.md +390 -0
- package/.agents/skills/galdr-ymirsystem/assets/build-method.md +113 -0
- package/.agents/skills/galdr-ymirsystem/assets/build-tool-categories.md +178 -0
- package/.agents/skills/galdr-ymirsystem/assets/eindri-orchestration.md +518 -0
- package/.agents/skills/galdr-ymirsystem/assets/eindri-profiles.md +136 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +673 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/claude-code.md +197 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/codex.md +191 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/cursor.md +193 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/opencode.md +262 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/pi.md +295 -0
- package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +496 -0
- package/.agents/skills/galdr-ymirsystem/assets/installation.md +571 -0
- package/.agents/skills/galdr-ymirsystem/assets/local-models/HOST-DATA.example.md +72 -0
- package/.agents/skills/galdr-ymirsystem/assets/local-models.md +274 -0
- package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +161 -0
- package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +377 -0
- package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
- package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +129 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/einherjar-spawn.schema.json +81 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/herdr-profile.toml +75 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +96 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/supervision-tree.yml +163 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot-guide.md +58 -0
- package/.agents/skills/galdr-ymirsystem/assets/porting-upstream-to-norse.md +301 -0
- package/.agents/skills/galdr-ymirsystem/assets/principles.md +385 -0
- package/.agents/skills/galdr-ymirsystem/assets/reference-adoption.md +99 -0
- package/.agents/skills/galdr-ymirsystem/assets/registry.md +379 -0
- package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +463 -0
- package/.agents/skills/galdr-ymirsystem/assets/runtime-components.md +553 -0
- package/.agents/skills/galdr-ymirsystem/assets/smidja.md +175 -0
- package/.agents/skills/galdr-ymirsystem/schemas/toon-schemas.md +190 -0
- package/.agents/skills/galdr-ymirsystem/scripts/README.md +45 -0
- package/.agents/skills/galdr-ymirsystem/scripts/bench-one.sh +248 -0
- package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +360 -0
- package/.agents/skills/galdr-ymirsystem/scripts/gpu-sample.sh +43 -0
- package/.agents/skills/galdr-ymirsystem/scripts/stop-all.sh +34 -0
- package/.agents/skills/galdr-ymirsystem/scripts/toon-check.py +188 -0
- package/.agents/skills/gjallarhorn-relay/README.md +3 -0
- package/.agents/skills/gjallarhorn-relay/SKILL.md +283 -0
- package/.agents/skills/groa-update/SKILL.md +70 -0
- package/.agents/skills/hamr-adapters/README.md +3 -0
- package/.agents/skills/hamr-adapters/SKILL.md +123 -0
- package/.agents/skills/hamr-adapters/references/common/control-and-recovery.md +37 -0
- package/.agents/skills/hamr-adapters/references/common/dispatch.md +32 -0
- package/.agents/skills/hamr-adapters/references/common/model-and-effort.md +42 -0
- package/.agents/skills/hamr-adapters/references/common/primary-hooks.md +40 -0
- package/.agents/skills/hamr-adapters/references/harness/claude.md +55 -0
- package/.agents/skills/hamr-adapters/references/harness/codex.md +43 -0
- package/.agents/skills/hamr-adapters/references/harness/cursor.md +75 -0
- package/.agents/skills/hamr-adapters/references/harness/grok.md +69 -0
- package/.agents/skills/hamr-adapters/references/harness/kimi.md +51 -0
- package/.agents/skills/hamr-adapters/references/harness/muse.md +70 -0
- package/.agents/skills/hamr-adapters/references/harness/opencode.md +42 -0
- package/.agents/skills/hamr-adapters/references/harness/pi.md +56 -0
- package/.agents/skills/herdr-panes/README.md +3 -0
- package/.agents/skills/herdr-panes/SKILL.md +92 -0
- package/.agents/skills/herdr-panes/assets/herdr-backend.md +346 -0
- package/.agents/skills/herdr-panes/assets/tmux-backend.md +115 -0
- package/.agents/skills/hnoss-design/DESIGN.md +19 -0
- package/.agents/skills/hnoss-design/README.md +3 -0
- package/.agents/skills/hnoss-design/SKILL.md +83 -0
- package/.agents/skills/hvild-afk/README.md +3 -0
- package/.agents/skills/hvild-afk/SKILL.md +241 -0
- package/.agents/skills/jord-projects/README.md +3 -0
- package/.agents/skills/jord-projects/SKILL.md +92 -0
- package/.agents/skills/lifecycle/SKILL.md +56 -0
- package/.agents/skills/lifecycle/smoke_test.sh +52 -0
- package/.agents/skills/lifecycle/start.sh +21 -0
- package/.agents/skills/lifecycle/status.sh +35 -0
- package/.agents/skills/lifecycle/stop.sh +16 -0
- package/.agents/skills/muninn-stow/README.md +3 -0
- package/.agents/skills/muninn-stow/SKILL.md +309 -0
- package/.agents/skills/no-mistakes/SKILL.md +379 -0
- package/.agents/skills/nornir-schedule/README.md +3 -0
- package/.agents/skills/nornir-schedule/SKILL.md +25 -0
- package/.agents/skills/nornir-schedule/assets/events.md +137 -0
- package/.agents/skills/nornir-schedule/assets/quota.md +131 -0
- package/.agents/skills/nsr-compliance/README.md +3 -0
- package/.agents/skills/nsr-compliance/SKILL.md +29 -0
- package/.agents/skills/nsr-compliance/assets/nsr/README.md +40 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/README.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/git-workflow.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/security.md +22 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/tech-stack.md +8 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/SKILL.md +71 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/config/core_four.yaml +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/validate_code.sh +29 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/verify_docs.py +66 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/generate.sh +109 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/telemetry/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/README.md +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/README.md +36 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/SKILL-template.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/README.md +25 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/SKILL.md +13 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/create_branch.sh +6 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/safe_commit.sh +9 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/sync_upstream.sh +6 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/README.md +27 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/SKILL.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/smoke_test.sh +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/start.sh +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/status.sh +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/stop.sh +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/core_four.yaml +13 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/README.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/api.md +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/compliance-wiring.md +155 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/frontend.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/README.md +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/README.md +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/template-README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/deploy.sh +49 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/development.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/production.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/staging.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/template.env.example +8 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/README.md +26 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/agent.md +7 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/developers/dev-1.md +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/linux.md +9 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/macos.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/shared.md +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/windows.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/README.md +25 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/_template.md +41 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/hosting-1.md +41 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/README.md +21 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/README.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/deployment.md +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/disaster-recovery.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/ops-tasks.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/features/README.md +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/project-skills.md +146 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/software-compliance-research.md +269 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/working-with-agents.md +260 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/AGENTS.md +38 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/ARCHITECTURE.md +36 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/BEST_PRACTICES.md +27 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/CI_CD.md +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/FEATURES.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/README.md +22 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/RULES.md +37 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/STRUCTURE.md +45 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/TECH_STACK.md +28 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scaffold-spec.md +63 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scripts/scaffold.sh +103 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scripts/validate.sh +52 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/SKILL.md +71 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/config/core_four.yaml +16 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/validate_code.sh +29 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/verify_docs.py +66 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/generate.sh +109 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/telemetry/README.md +11 -0
- package/.agents/skills/pr-ops/README.md +3 -0
- package/.agents/skills/pr-ops/SKILL.md +109 -0
- package/.agents/skills/ratatoskr-a2a/README.md +3 -0
- package/.agents/skills/ratatoskr-a2a/SKILL.md +148 -0
- package/.agents/skills/rules-check-drift/SKILL.md +81 -0
- package/.agents/skills/saga-bearings/README.md +3 -0
- package/.agents/skills/saga-bearings/SKILL.md +26 -0
- package/.agents/skills/saga-bearings/assets/bearings.md +161 -0
- package/.agents/skills/saga-bearings/assets/board-template.html +731 -0
- package/.agents/skills/saga-bearings/assets/recap.md +56 -0
- package/.agents/skills/syn-recovery/README.md +3 -0
- package/.agents/skills/syn-recovery/SKILL.md +54 -0
- package/.agents/skills/tyr-check/README.md +3 -0
- package/.agents/skills/tyr-check/SKILL.md +218 -0
- package/.agents/skills/urdh-hold/README.md +3 -0
- package/.agents/skills/urdh-hold/SKILL.md +25 -0
- package/.agents/skills/urdh-hold/assets/decisions.md +15 -0
- package/.agents/skills/urdh-hold/assets/hold.md +54 -0
- package/.agents/skills/vor-diagnostics/README.md +3 -0
- package/.agents/skills/vor-diagnostics/SKILL.md +68 -0
- package/.agents/skills/ymir-host/README.md +3 -0
- package/.agents/skills/ymir-host/SKILL.md +34 -0
- package/.agents/skills/ymir-host/assets/desktop.md +36 -0
- package/.agents/skills/ymir-host/assets/omarchy.md +297 -0
- package/.agents/skills/ymir-host/assets/runbooks.md +15 -0
- package/.agents/skills/ymir-host/assets/thjazi.md +220 -0
- package/.agents/skills/ymir-host/assets/update.md +64 -0
- package/.agents/tests/assets/board-render-harness.mjs +117 -0
- package/.agents/tests/cmux-test-safety.sh +51 -0
- package/.agents/tests/fixtures.sh +291 -0
- package/.agents/tests/fm-afk-inject-e2e.test.sh +428 -0
- package/.agents/tests/fm-afk-inject-herdr-e2e.test.sh +536 -0
- package/.agents/tests/fm-afk-launch.test.sh +961 -0
- package/.agents/tests/fm-afk-pi-herdr-return-e2e.test.sh +297 -0
- package/.agents/tests/fm-afk-return.test.sh +283 -0
- package/.agents/tests/fm-arm-pretool-check.test.sh +482 -0
- package/.agents/tests/fm-ask-user-authority.test.sh +42 -0
- package/.agents/tests/fm-backend-autodetect-smoke.test.sh +176 -0
- package/.agents/tests/fm-backend-cmux-smoke.test.sh +188 -0
- package/.agents/tests/fm-backend-cmux.test.sh +1166 -0
- package/.agents/tests/fm-backend-herdr-eventwait-smoke.test.sh +136 -0
- package/.agents/tests/fm-backend-herdr-eventwait.test.py +106 -0
- package/.agents/tests/fm-backend-herdr-focus-flash-e2e.test.sh +409 -0
- package/.agents/tests/fm-backend-herdr-launcher-workspace-e2e.test.sh +437 -0
- package/.agents/tests/fm-backend-herdr-presentation-e2e.test.sh +1424 -0
- package/.agents/tests/fm-backend-herdr-prune-safety-e2e.test.sh +180 -0
- package/.agents/tests/fm-backend-herdr-respawn-idem-e2e.test.sh +182 -0
- package/.agents/tests/fm-backend-herdr-smoke.test.sh +355 -0
- package/.agents/tests/fm-backend-herdr-workspace-per-home-e2e.test.sh +251 -0
- package/.agents/tests/fm-backend-herdr.test.sh +4606 -0
- package/.agents/tests/fm-backend-orca.test.sh +1357 -0
- package/.agents/tests/fm-backend-tmux-smoke.test.sh +173 -0
- package/.agents/tests/fm-backend-zellij-smoke.test.sh +205 -0
- package/.agents/tests/fm-backend-zellij.test.sh +1360 -0
- package/.agents/tests/fm-backend.test.sh +1142 -0
- package/.agents/tests/fm-backlog-atomicity.test.sh +2302 -0
- package/.agents/tests/fm-backlog-handoff.test.sh +1360 -0
- package/.agents/tests/fm-bearings-board-render.test.sh +135 -0
- package/.agents/tests/fm-bearings-board.test.sh +414 -0
- package/.agents/tests/fm-bearings-snapshot.test.sh +1990 -0
- package/.agents/tests/fm-bootstrap-network-parallel.test.sh +316 -0
- package/.agents/tests/fm-bootstrap.test.sh +1178 -0
- package/.agents/tests/fm-branch-supervision.test.sh +552 -0
- package/.agents/tests/fm-brief.test.sh +785 -0
- package/.agents/tests/fm-busy-adapter-wiring.test.sh +324 -0
- package/.agents/tests/fm-busy-state.test.sh +465 -0
- package/.agents/tests/fm-calm-pi-extension.test.sh +3979 -0
- package/.agents/tests/fm-captain-hold-lifecycle.test.sh +1187 -0
- package/.agents/tests/fm-cd-pretool-check.test.sh +400 -0
- package/.agents/tests/fm-check-unregister.test.sh +198 -0
- package/.agents/tests/fm-classify-corr-token.test.sh +543 -0
- package/.agents/tests/fm-classify-decision-key.test.sh +340 -0
- package/.agents/tests/fm-claude-stop-autoarm-live-e2e.test.sh +164 -0
- package/.agents/tests/fm-claude-stop-autoarm.test.sh +1190 -0
- package/.agents/tests/fm-cmux-claude-composer-live-e2e.test.sh +110 -0
- package/.agents/tests/fm-codex-continuity-live-e2e.test.sh +55 -0
- package/.agents/tests/fm-composer-ghost.test.sh +716 -0
- package/.agents/tests/fm-composer-lib.test.sh +666 -0
- package/.agents/tests/fm-composer-matrix-live-e2e.test.sh +220 -0
- package/.agents/tests/fm-control-herdr-smoke.test.sh +149 -0
- package/.agents/tests/fm-control-relaunch.test.sh +1530 -0
- package/.agents/tests/fm-control.test.sh +910 -0
- package/.agents/tests/fm-crew-state.test.sh +1610 -0
- package/.agents/tests/fm-cursor-harness.test.sh +404 -0
- package/.agents/tests/fm-cursor-primary-live-e2e.test.sh +217 -0
- package/.agents/tests/fm-cursor-primary.test.sh +708 -0
- package/.agents/tests/fm-daemon.test.sh +2740 -0
- package/.agents/tests/fm-documentation-audiences.test.sh +141 -0
- package/.agents/tests/fm-ensure-agents-md.test.sh +371 -0
- package/.agents/tests/fm-extension-binding.test.sh +2187 -0
- package/.agents/tests/fm-fleet-snapshot-view.test.sh +816 -0
- package/.agents/tests/fm-fleet-sync.test.sh +721 -0
- package/.agents/tests/fm-gate-refuse.test.sh +356 -0
- package/.agents/tests/fm-gitignore-config.test.sh +89 -0
- package/.agents/tests/fm-gotmp.test.sh +230 -0
- package/.agents/tests/fm-grok-continuity-live-e2e.test.sh +112 -0
- package/.agents/tests/fm-grok-harness.test.sh +117 -0
- package/.agents/tests/fm-grok-stop-live-e2e.test.sh +219 -0
- package/.agents/tests/fm-guard-stale-banner.test.sh +709 -0
- package/.agents/tests/fm-harness-adapter-instructions-live-e2e.test.sh +134 -0
- package/.agents/tests/fm-harness-adapter-references.test.sh +30 -0
- package/.agents/tests/fm-harness-liveness-drift-live-e2e.test.sh +151 -0
- package/.agents/tests/fm-herdr-lab.test.sh +243 -0
- package/.agents/tests/fm-herdr-session-cleanup-e2e.test.sh +146 -0
- package/.agents/tests/fm-herdr-session-cleanup.test.sh +329 -0
- package/.agents/tests/fm-herdr-submit-confirm-live-e2e.test.sh +125 -0
- package/.agents/tests/fm-herdr-version-floor-live-e2e.test.sh +144 -0
- package/.agents/tests/fm-home-summary-refresh.test.sh +971 -0
- package/.agents/tests/fm-inactive-reconcile.test.sh +485 -0
- package/.agents/tests/fm-kimi-harness.test.sh +687 -0
- package/.agents/tests/fm-lint-workflows.test.sh +532 -0
- package/.agents/tests/fm-lint.test.sh +1023 -0
- package/.agents/tests/fm-muse-harness.test.sh +932 -0
- package/.agents/tests/fm-muse-signals-live-e2e.test.sh +205 -0
- package/.agents/tests/fm-no-mistakes-required.test.sh +72 -0
- package/.agents/tests/fm-on.test.sh +507 -0
- package/.agents/tests/fm-opencode-primary-live-e2e.test.sh +357 -0
- package/.agents/tests/fm-operational-input.test.sh +160 -0
- package/.agents/tests/fm-peek-remote.test.sh +110 -0
- package/.agents/tests/fm-pending-reply.test.sh +1288 -0
- package/.agents/tests/fm-pi-branch-extension.test.sh +3182 -0
- package/.agents/tests/fm-pi-branch-live-e2e.test.sh +527 -0
- package/.agents/tests/fm-pi-primary-live-e2e.test.sh +345 -0
- package/.agents/tests/fm-pi-primary-types.test.sh +68 -0
- package/.agents/tests/fm-pi-watch-extension.test.sh +2843 -0
- package/.agents/tests/fm-pr-check-security.test.sh +2140 -0
- package/.agents/tests/fm-pr-merge.test.sh +2135 -0
- package/.agents/tests/fm-procevent-quota.test.sh +225 -0
- package/.agents/tests/fm-procevent-when.test.sh +406 -0
- package/.agents/tests/fm-procevent.test.sh +1745 -0
- package/.agents/tests/fm-project-origin.test.sh +110 -0
- package/.agents/tests/fm-public-followup.test.sh +2272 -0
- package/.agents/tests/fm-quota-array-dispatch-live-e2e.test.sh +526 -0
- package/.agents/tests/fm-quota-choose.test.sh +626 -0
- package/.agents/tests/fm-remote-backlog-handoff.test.sh +453 -0
- package/.agents/tests/fm-remote-doctor.test.sh +625 -0
- package/.agents/tests/fm-remote-entrypoint.test.sh +59 -0
- package/.agents/tests/fm-remote-job-orphan-reap.test.sh +205 -0
- package/.agents/tests/fm-remote-job.test.sh +768 -0
- package/.agents/tests/fm-remote-reply.test.sh +520 -0
- package/.agents/tests/fm-remote-secondmate-lifecycle-e2e.test.sh +1256 -0
- package/.agents/tests/fm-remote-secondmate-parent-binding.test.sh +326 -0
- package/.agents/tests/fm-remote-secondmate-trace-context.test.sh +310 -0
- package/.agents/tests/fm-remote-transport-lanes.test.sh +431 -0
- package/.agents/tests/fm-review-diff.test.sh +176 -0
- package/.agents/tests/fm-secondmate-harness.test.sh +2600 -0
- package/.agents/tests/fm-secondmate-lifecycle-e2e.test.sh +259 -0
- package/.agents/tests/fm-secondmate-liveness.test.sh +559 -0
- package/.agents/tests/fm-secondmate-reconcile.test.sh +732 -0
- package/.agents/tests/fm-secondmate-safety.test.sh +2972 -0
- package/.agents/tests/fm-secondmate-sync.test.sh +913 -0
- package/.agents/tests/fm-send-inbox-doorbell-live-e2e.test.sh +206 -0
- package/.agents/tests/fm-send-inbox.test.sh +352 -0
- package/.agents/tests/fm-send-popup-settle.test.sh +167 -0
- package/.agents/tests/fm-send-remote-delivery.test.sh +796 -0
- package/.agents/tests/fm-send-resolve-key.test.sh +551 -0
- package/.agents/tests/fm-send-secondmate-marker-herdr-e2e.test.sh +187 -0
- package/.agents/tests/fm-send-secondmate-marker.test.sh +278 -0
- package/.agents/tests/fm-send-settle.test.sh +147 -0
- package/.agents/tests/fm-send-strict.test.sh +241 -0
- package/.agents/tests/fm-session-lock-ancestry.test.sh +365 -0
- package/.agents/tests/fm-session-start.test.sh +2515 -0
- package/.agents/tests/fm-sessionstart-hook-live-e2e.test.sh +631 -0
- package/.agents/tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh +230 -0
- package/.agents/tests/fm-sessionstart-nudge.test.sh +1039 -0
- package/.agents/tests/fm-shared-captain-inheritance.test.sh +404 -0
- package/.agents/tests/fm-spawn-batch.test.sh +148 -0
- package/.agents/tests/fm-spawn-dispatch-profile.test.sh +827 -0
- package/.agents/tests/fm-spawn-pool-base-freshen.test.sh +440 -0
- package/.agents/tests/fm-spawn-worktree-settle.test.sh +147 -0
- package/.agents/tests/fm-startup-memory-budget.test.sh +333 -0
- package/.agents/tests/fm-startup-network.test.sh +712 -0
- package/.agents/tests/fm-stow-cascade.test.sh +370 -0
- package/.agents/tests/fm-subagent-pretool-check.test.sh +291 -0
- package/.agents/tests/fm-supervision-events.test.sh +156 -0
- package/.agents/tests/fm-supervision-instructions.test.sh +189 -0
- package/.agents/tests/fm-tangle-guard.test.sh +277 -0
- package/.agents/tests/fm-task-delivery.test.sh +420 -0
- package/.agents/tests/fm-task-inbox.test.sh +513 -0
- package/.agents/tests/fm-teardown-endpoint-safety.test.sh +374 -0
- package/.agents/tests/fm-teardown.test.sh +2665 -0
- package/.agents/tests/fm-test-fixture-cleanup.test.sh +172 -0
- package/.agents/tests/fm-test-fixtures.test.sh +132 -0
- package/.agents/tests/fm-test-isolation-proof.test.sh +278 -0
- package/.agents/tests/fm-test-run.test.sh +1196 -0
- package/.agents/tests/fm-tmux-agent-liveness.test.sh +354 -0
- package/.agents/tests/fm-tmux-submit-busy.test.sh +340 -0
- package/.agents/tests/fm-tool-update-check.test.sh +1044 -0
- package/.agents/tests/fm-trace-context-lib.test.sh +253 -0
- package/.agents/tests/fm-trace-context-spawn.test.sh +581 -0
- package/.agents/tests/fm-transition-lib.test.sh +54 -0
- package/.agents/tests/fm-turnend-guard.test.sh +1822 -0
- package/.agents/tests/fm-update.test.sh +304 -0
- package/.agents/tests/fm-vendor-auth-probe.test.sh +395 -0
- package/.agents/tests/fm-voice-relay.test.sh +4741 -0
- package/.agents/tests/fm-wake-daemon-lifecycle-e2e.test.sh +171 -0
- package/.agents/tests/fm-wake-drain-open-decisions-cursor.test.sh +356 -0
- package/.agents/tests/fm-wake-drain-open-decisions.test.sh +226 -0
- package/.agents/tests/fm-wake-drain-unread-status.test.sh +379 -0
- package/.agents/tests/fm-wake-queue.test.sh +1224 -0
- package/.agents/tests/fm-watch-arm.test.sh +821 -0
- package/.agents/tests/fm-watch-checkpoint.test.sh +87 -0
- package/.agents/tests/fm-watch-recovery-loop.test.sh +223 -0
- package/.agents/tests/fm-watch-triage.test.sh +3889 -0
- package/.agents/tests/fm-watcher-lock.test.sh +1133 -0
- package/.agents/tests/fm-x-mode.test.sh +3042 -0
- package/.agents/tests/gleipnir-guard-pretool-check.test.sh +51 -0
- package/.agents/tests/gleipnir-machine-lock.test.sh +130 -0
- package/.agents/tests/herdr-test-safety.sh +42 -0
- package/.agents/tests/lib.sh +315 -0
- package/.agents/tests/remote-herdr-fixture.sh +125 -0
- package/.agents/tests/secondmate-helpers.sh +193 -0
- package/.agents/tests/smoke.test.sh +93 -0
- package/.agents/tests/wake-helpers.sh +338 -0
- package/.agents/tests/zellij-test-safety.sh +60 -0
- package/.agents/tools/README.md +10 -0
- package/.agents/tools/bin/tasks-cli +267 -0
- package/.agents/tools/tasks-cli.ts +216 -0
- package/AGENTS.md +454 -0
- package/CHANGELOG.md +881 -0
- package/LICENSE +1 -1
- package/NOTICE +63 -0
- package/README.md +113 -59
- package/RULES/01-domains.md +51 -0
- package/RULES/02-agents.md +78 -0
- package/RULES/03-houses.md +61 -0
- package/RULES/04-hoard.md +65 -0
- package/RULES/05-platforms.md +67 -0
- package/RULES/06-append-only.md +58 -0
- package/RULES/07-config.md +45 -0
- package/RULES/08-delivery-gate.md +75 -0
- package/RULES/README.md +20 -0
- package/bin/README.md +107 -0
- package/bin/a2a-mcp.sh +98 -0
- package/bin/a2a-serve.py +103 -0
- package/bin/a2a-serve.sh +8 -0
- package/bin/a2a-talk.sh +50 -0
- package/bin/a2abridge-ensure.sh +108 -0
- package/bin/agent-run.sh +43 -0
- package/bin/agents-config.sh +233 -0
- package/bin/apodex-smoke-test.sh +92 -0
- package/bin/bifrost-bridge.sh +112 -0
- package/bin/bifrost-ingress.sh +77 -0
- package/bin/bootstrap-macos.sh +107 -0
- package/bin/bootstrap-windows.ps1 +112 -0
- package/bin/branch-guard.sh +64 -0
- package/bin/brokk +73 -0
- package/bin/brokk-classify-lib.sh +1770 -0
- package/bin/brokk-lease-lib.sh +222 -0
- package/bin/brokk-lease.sh +193 -0
- package/bin/brokk-lint.sh +65 -0
- package/bin/brokk-send.sh +38 -0
- package/bin/brokk-update.sh +9 -0
- package/bin/brokk-wake-grant.sh +127 -0
- package/bin/brokk-wake-lib.sh +1849 -0
- package/bin/changelog-guard.sh +165 -0
- package/bin/crash-sense.sh +160 -0
- package/bin/desktop-place.sh +224 -0
- package/bin/docs-guard.sh +39 -0
- package/bin/editor-place.sh +207 -0
- package/bin/eindri-acclaim.sh +34 -0
- package/bin/eindri-control.sh +28 -0
- package/bin/eindri-role.sh +83 -0
- package/bin/eindri-seen.sh +41 -0
- package/bin/eindri-send.sh +15 -0
- package/bin/eindri-start.sh +85 -0
- package/bin/eindri-watch.sh +57 -0
- package/bin/einherjar-spawn.sh +576 -0
- package/bin/eir-doctor.sh +112 -0
- package/bin/erindi-brief.sh +271 -0
- package/bin/fleet-apply.sh +68 -0
- package/bin/github-deploy.sh +81 -0
- package/bin/gjallarhorn-expose.sh +102 -0
- package/bin/gjallarhorn-notify.sh +58 -0
- package/bin/gjallarhorn-purge.sh +54 -0
- package/bin/gjallarhorn-tunnel.sh +72 -0
- package/bin/gleipnir-lock-lib.sh +284 -0
- package/bin/groa-update.sh +122 -0
- package/bin/hall-snapshot.sh +92 -0
- package/bin/hamr-harness.sh +246 -0
- package/bin/heimdall-ssh-keys.sh +374 -0
- package/bin/herdr-agents.py +44 -0
- package/bin/herdr-ensure.sh +104 -0
- package/bin/herdr-run.sh +431 -0
- package/bin/hermes-ensure.sh +88 -0
- package/bin/hoard-lib.sh +25 -0
- package/bin/hodd.sh +78 -0
- package/bin/host-sense.sh +199 -0
- package/bin/huginn-research-worker.sh +108 -0
- package/bin/local-model-lock.sh +51 -0
- package/bin/mimir-bridge.py +226 -0
- package/bin/mimir-bridge.sh +87 -0
- package/bin/mimir-ingest.sh +131 -0
- package/bin/mimir.sh +113 -0
- package/bin/mjollnir-webhook.sh +59 -0
- package/bin/mjollnir.sh +85 -0
- package/bin/model-hardware.sh +82 -0
- package/bin/model-resolve.sh +147 -0
- package/bin/models-detect.sh +178 -0
- package/bin/nornir-cron-start.sh +162 -0
- package/bin/nornir-job-daily-briefing.sh +191 -0
- package/bin/nornir-job-git-sync.sh +152 -0
- package/bin/nornir-job-memory-housekeeping.sh +114 -0
- package/bin/nornir-job-observer.sh +166 -0
- package/bin/nornir-job-skillopt-sleep.sh +76 -0
- package/bin/omarchy-hook-install.sh +60 -0
- package/bin/omarchy-install.sh +148 -0
- package/bin/omarchy-plugins.sh +174 -0
- package/bin/omarchy-sense.sh +149 -0
- package/bin/perm-guard.sh +30 -0
- package/bin/pi-agent.sh +68 -0
- package/bin/pi-ensure.sh +77 -0
- package/bin/pi-local.sh +37 -0
- package/bin/pi-seat.sh +63 -0
- package/bin/prereq-ensure.sh +166 -0
- package/bin/private-guard.sh +38 -0
- package/bin/project-git.sh +55 -0
- package/bin/public-guard.sh +70 -0
- package/bin/ratatoskr.sh +49 -0
- package/bin/realm-lib.sh +32 -0
- package/bin/repo-scrub.sh +62 -0
- package/bin/rodd-operational-input.sh +250 -0
- package/bin/runes-append.sh +198 -0
- package/bin/saga-session-start.sh +172 -0
- package/bin/saga-sessionstart-run.sh +79 -0
- package/bin/saga-wake-drain.sh +41 -0
- package/bin/secret-guard.sh +63 -0
- package/bin/sessrumnir-ensure.sh +100 -0
- package/bin/sessrumnir-sync.sh +237 -0
- package/bin/sessrumnir.sh +91 -0
- package/bin/skillopt-setup.sh +76 -0
- package/bin/skuld-branch-outcome.sh +203 -0
- package/bin/skuld-branch-prompt.sh +101 -0
- package/bin/smidja-bootstrap.sh +90 -0
- package/bin/smidja-observe.sh +86 -0
- package/bin/syn-arm-pretool-check.sh +57 -0
- package/bin/syn-asset-pretool-check.sh +76 -0
- package/bin/syn-cd-pretool-check.sh +28 -0
- package/bin/syn-guard-pretool-check.sh +83 -0
- package/bin/syn-turnend-guard.sh +36 -0
- package/bin/syn-watch-arm.sh +99 -0
- package/bin/tailscale-sync.sh +190 -0
- package/bin/telegram-bot.sh +69 -0
- package/bin/toolchain.sh +74 -0
- package/bin/utgard.sh +111 -0
- package/bin/valhalla.sh +115 -0
- package/bin/valknut-load.sh +260 -0
- package/bin/vor-crew-state.sh +124 -0
- package/bin/wedge-notify.sh +47 -0
- package/bin/workspace-provision.sh +80 -0
- package/bin/workspace-rag.sh +150 -0
- package/bin/wyrd-db.sh +47 -0
- package/bin/yggdrasil.sh +152 -0
- package/bin/ymir-edit.sh +145 -0
- package/bin/ymir-install.sh +641 -0
- package/bin/ymir-invite.sh +93 -0
- package/bin/ymir-isolation.sh +88 -0
- package/bin/ymir-migrate.sh +61 -0
- package/bin/ymir-platform.sh +348 -0
- package/bin/ymir-say.sh +97 -0
- package/bin/ymir-setup-auth.sh +108 -0
- package/bin/ymir-validate.sh +173 -0
- package/bin/ymir.js +74 -16
- package/docs/Architecture.md +339 -0
- package/docs/design.md +351 -0
- package/docs/installations/README.md +63 -0
- package/docs/installations/ymir-home.md +93 -0
- package/docs/integration.md +105 -0
- package/docs/lore.md +1056 -0
- package/docs/research/agent-harnesses.md +290 -0
- package/docs/runbooks/README.md +19 -0
- package/docs/runbooks/a2a.md +91 -0
- package/docs/runbooks/agent-permissions.md +99 -0
- package/docs/runbooks/agents.md +57 -0
- package/docs/runbooks/models.md +69 -0
- package/docs/runbooks/realm-onboarding.md +60 -0
- package/docs/runbooks/secrets-and-hoard.md +60 -0
- package/docs/runbooks/updates-and-migrations.md +110 -0
- package/docs/runbooks/worktrees-isolation.md +72 -0
- package/docs/session-start.md +105 -0
- package/docs/skillopt-integration.md +321 -0
- package/docs/sync-system.md +66 -0
- package/docs/workspaces.md +104 -0
- package/docs/ymir-rut.md +260 -0
- package/package.json +44 -14
- package/packaging/build.sh +74 -0
- package/packaging/macos/Ymir Installer.command +43 -0
- package/packaging/windows/ymir-setup.nsi +74 -0
- package/scripts/README.md +22 -0
- package/scripts/electron.sh +210 -0
- package/scripts/start.sh +167 -0
- package/scripts/stop.sh +113 -0
- package/install.sh +0 -35
|
@@ -0,0 +1,1044 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Tests for fm-tool-update-check.sh, the watched tooling update report.
|
|
3
|
+
#
|
|
4
|
+
# The case that matters most is PATH skew: a tool that has already installed its
|
|
5
|
+
# newer copy, while PATH still resolves an older one. On 2026-08-20 that exact
|
|
6
|
+
# shape broke this fleet. Herdr self-installed 0.8.2 into ~/.local/bin, a version
|
|
7
|
+
# manager kept its own 0.8.0 earlier on PATH inside a directory named "latest",
|
|
8
|
+
# and every Herdr command then failed on a protocol mismatch. A check that only
|
|
9
|
+
# asks "is a newer version published" reports everything up to date and misses
|
|
10
|
+
# it, so test_path_skew_is_reported_from_every_copy reproduces the incident and
|
|
11
|
+
# asserts the report names the older copy PATH resolves AND the newer copy that
|
|
12
|
+
# is already installed. A single `command -v` lookup cannot know the second
|
|
13
|
+
# version, so that assertion fails against any build without real per-copy
|
|
14
|
+
# probing.
|
|
15
|
+
#
|
|
16
|
+
# The fixtures use a synthetic command name and their own temporary PATH
|
|
17
|
+
# directories, so no case ever probes, launches, or otherwise touches a tool
|
|
18
|
+
# actually installed on this host.
|
|
19
|
+
set -u
|
|
20
|
+
|
|
21
|
+
# shellcheck source=tests/lib.sh
|
|
22
|
+
. "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
|
23
|
+
|
|
24
|
+
CHECK="$ROOT/bin/fm-tool-update-check.sh"
|
|
25
|
+
CHECKPOINT="$ROOT/bin/fm-watch-checkpoint.sh"
|
|
26
|
+
TMP_ROOT=$(fm_test_tmproot fm-tool-update-check)
|
|
27
|
+
|
|
28
|
+
# Exported here, at the top level, because git_fixture runs inside a command
|
|
29
|
+
# substitution and an export from that subshell never reaches the cases, which
|
|
30
|
+
# make fixture commits of their own. A host with no git identity configured
|
|
31
|
+
# would otherwise fail those commits and leave the fixture in a shape the case
|
|
32
|
+
# did not ask for.
|
|
33
|
+
fm_git_identity fmtest fmtest@example.invalid
|
|
34
|
+
|
|
35
|
+
# The incident's tool, under a name that cannot exist on this host.
|
|
36
|
+
TOOL=herdr-fixture
|
|
37
|
+
|
|
38
|
+
make_home() {
|
|
39
|
+
local name=$1 home
|
|
40
|
+
home="$TMP_ROOT/$name"
|
|
41
|
+
mkdir -p "$home/state" "$home/config"
|
|
42
|
+
printf '%s\n' "$home"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# make_copy <dir> <command> <version-output>: an executable copy that answers
|
|
46
|
+
# --version with the given text and nothing else.
|
|
47
|
+
make_copy() {
|
|
48
|
+
local dir=$1 command_name=$2 text=$3
|
|
49
|
+
mkdir -p "$dir"
|
|
50
|
+
cat > "$dir/$command_name" <<SH
|
|
51
|
+
#!/usr/bin/env bash
|
|
52
|
+
printf '%s\n' '$text'
|
|
53
|
+
SH
|
|
54
|
+
chmod 0755 "$dir/$command_name"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# make_slow_copy <dir> <command> <seconds>: a copy that answers far too late, so a
|
|
58
|
+
# case can spend the sweep budget the way a hung tool would.
|
|
59
|
+
make_slow_copy() {
|
|
60
|
+
local dir=$1 command_name=$2 seconds=$3
|
|
61
|
+
mkdir -p "$dir"
|
|
62
|
+
cat > "$dir/$command_name" <<SH
|
|
63
|
+
#!/usr/bin/env bash
|
|
64
|
+
sleep $seconds
|
|
65
|
+
printf 'herdr 0.8.2\n'
|
|
66
|
+
SH
|
|
67
|
+
chmod 0755 "$dir/$command_name"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# make_counting_copy <dir> <command> <version-output> <log>: the same copy, which
|
|
71
|
+
# also appends one line to <log> every time it runs, so a case can assert how
|
|
72
|
+
# many times the check actually probed it.
|
|
73
|
+
make_counting_copy() {
|
|
74
|
+
local dir=$1 command_name=$2 text=$3 log=$4
|
|
75
|
+
mkdir -p "$dir"
|
|
76
|
+
cat > "$dir/$command_name" <<SH
|
|
77
|
+
#!/usr/bin/env bash
|
|
78
|
+
printf 'probed\n' >> '$log'
|
|
79
|
+
printf '%s\n' '$text'
|
|
80
|
+
SH
|
|
81
|
+
chmod 0755 "$dir/$command_name"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
write_config() {
|
|
85
|
+
local home=$1
|
|
86
|
+
shift
|
|
87
|
+
printf '%s\n' "$*" > "$home/config/watched-tools.json"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# The fixture directories first, then the ambient PATH, which the check needs
|
|
91
|
+
# because it shells out to ordinary tools such as jq, git, date, grep, stat, and
|
|
92
|
+
# timeout. What keeps a tool installed on this host out of a fixture is the
|
|
93
|
+
# synthetic command name, not this PATH, so every case watches a command name
|
|
94
|
+
# that cannot exist here.
|
|
95
|
+
fixture_path() {
|
|
96
|
+
printf '%s:%s\n' "$1" "$PATH"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# The watcher check timeout is pinned to its documented default here, because the
|
|
100
|
+
# sweep budget is cut to fit it and an operator's ambient value would otherwise
|
|
101
|
+
# add a report line to cases that mean to be silent. The one case that exercises
|
|
102
|
+
# the cut sets its own value.
|
|
103
|
+
run_check() {
|
|
104
|
+
local home=$1 path=$2 out=$3
|
|
105
|
+
shift 3
|
|
106
|
+
local status=0
|
|
107
|
+
env FM_CHECK_TIMEOUT=30 "$@" FM_HOME="$home" PATH="$path" FM_TOOL_UPDATE_INTERVAL=0 "$CHECK" >"$out" 2>&1 || status=$?
|
|
108
|
+
expect_code 0 "$status" "check exit"
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# --- the regression this script exists for ----------------------------------
|
|
112
|
+
|
|
113
|
+
test_path_skew_is_reported_from_every_copy() {
|
|
114
|
+
local home stale fresh out report
|
|
115
|
+
home=$(make_home skew)
|
|
116
|
+
# The stale copy sits in a directory named "latest" on purpose: the incident's
|
|
117
|
+
# version manager did exactly that, so a directory name is no evidence at all.
|
|
118
|
+
stale="$TMP_ROOT/skew/mise/installs/herdr/latest/bin"
|
|
119
|
+
fresh="$TMP_ROOT/skew/local/bin"
|
|
120
|
+
make_copy "$stale" "$TOOL" 'herdr 0.8.0'
|
|
121
|
+
make_copy "$fresh" "$TOOL" 'herdr 0.8.2'
|
|
122
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\",\"version_args\":[\"--version\"]}]}"
|
|
123
|
+
out="$home/out.txt"
|
|
124
|
+
run_check "$home" "$(fixture_path "$stale:$fresh")" "$out"
|
|
125
|
+
report=$(cat "$out")
|
|
126
|
+
|
|
127
|
+
assert_contains "$report" "herdr update not in effect" "PATH skew was not reported as an update that is not in effect"
|
|
128
|
+
# Both sides of the comparison must be named, and the newer one can only be
|
|
129
|
+
# known by asking a copy other than the one PATH resolves.
|
|
130
|
+
assert_contains "$report" "PATH resolves 0.8.0 at $stale/$TOOL" "the report does not name the older version PATH actually resolves"
|
|
131
|
+
assert_contains "$report" "0.8.2 is installed at $fresh/$TOOL" "the report does not name the newer installed copy, so no other PATH copy was asked for its version"
|
|
132
|
+
assert_not_contains "$report" "update available" "PATH skew must not be reported as a published update"
|
|
133
|
+
assert_contains "$report" "$(printf 'tool updates:')" "the report is missing its one-line prefix"
|
|
134
|
+
[ "$(wc -l < "$out" | tr -d '[:space:]')" = 1 ] || fail "the report must be exactly one line for the wake record"
|
|
135
|
+
pass "PATH skew is reported by asking every copy on PATH for its own version"
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
test_newest_copy_first_on_path_is_silent() {
|
|
139
|
+
local home stale fresh out
|
|
140
|
+
# Control for the case above: the same two copies, the newer one resolved
|
|
141
|
+
# first, must produce no report at all.
|
|
142
|
+
home=$(make_home no-skew)
|
|
143
|
+
stale="$TMP_ROOT/no-skew/mise/installs/herdr/latest/bin"
|
|
144
|
+
fresh="$TMP_ROOT/no-skew/local/bin"
|
|
145
|
+
make_copy "$stale" "$TOOL" 'herdr 0.8.0'
|
|
146
|
+
make_copy "$fresh" "$TOOL" 'herdr 0.8.2'
|
|
147
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
148
|
+
out="$home/out.txt"
|
|
149
|
+
run_check "$home" "$(fixture_path "$fresh:$stale")" "$out"
|
|
150
|
+
[ ! -s "$out" ] || fail "check reported skew when PATH already resolves the newest copy: $(cat "$out")"
|
|
151
|
+
pass "no report when PATH already resolves the newest installed copy"
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
test_identical_versions_are_silent() {
|
|
155
|
+
local home first second out
|
|
156
|
+
home=$(make_home same-version)
|
|
157
|
+
first="$TMP_ROOT/same-version/a/bin"
|
|
158
|
+
second="$TMP_ROOT/same-version/b/bin"
|
|
159
|
+
make_copy "$first" "$TOOL" 'herdr 0.8.2'
|
|
160
|
+
make_copy "$second" "$TOOL" 'herdr 0.8.2'
|
|
161
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
162
|
+
out="$home/out.txt"
|
|
163
|
+
run_check "$home" "$(fixture_path "$first:$second")" "$out"
|
|
164
|
+
[ ! -s "$out" ] || fail "two copies of the same version reported skew: $(cat "$out")"
|
|
165
|
+
pass "two copies of the same version are not skew"
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
test_one_copy_reached_twice_is_probed_once() {
|
|
169
|
+
local home dir link out log probes
|
|
170
|
+
# A single install reachable through two PATH entries must not read as two
|
|
171
|
+
# installs, or a symlinked bin directory would report skew against itself.
|
|
172
|
+
# Silence alone does not prove that, because two answers of the same version
|
|
173
|
+
# are silent too, so count the probes: the one install must be asked once.
|
|
174
|
+
home=$(make_home one-copy)
|
|
175
|
+
dir="$TMP_ROOT/one-copy/real/bin"
|
|
176
|
+
link="$TMP_ROOT/one-copy/linked-bin"
|
|
177
|
+
log="$TMP_ROOT/one-copy/probes.log"
|
|
178
|
+
make_counting_copy "$dir" "$TOOL" 'herdr 0.8.2' "$log"
|
|
179
|
+
ln -s "$dir" "$link"
|
|
180
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
181
|
+
out="$home/out.txt"
|
|
182
|
+
: > "$log"
|
|
183
|
+
run_check "$home" "$(fixture_path "$dir:$link")" "$out"
|
|
184
|
+
[ ! -s "$out" ] || fail "one copy reached through two PATH entries reported a finding: $(cat "$out")"
|
|
185
|
+
probes=$(wc -l < "$log" | tr -d ' ')
|
|
186
|
+
[ "$probes" = 1 ] || fail "one install reached through two PATH entries was probed $probes times, so the two entries were not recognized as one install"
|
|
187
|
+
pass "one copy reached through two PATH entries is probed once as one install"
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
test_unreadable_version_is_a_failure_not_a_pass() {
|
|
191
|
+
local home dir out report
|
|
192
|
+
# A copy that will not say what it is cannot be called current.
|
|
193
|
+
home=$(make_home mute)
|
|
194
|
+
dir="$TMP_ROOT/mute/bin"
|
|
195
|
+
make_copy "$dir" "$TOOL" 'no version here'
|
|
196
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
197
|
+
out="$home/out.txt"
|
|
198
|
+
run_check "$home" "$(fixture_path "$dir")" "$out"
|
|
199
|
+
report=$(cat "$out")
|
|
200
|
+
assert_contains "$report" "herdr check failed" "a copy that reports no version was treated as current"
|
|
201
|
+
assert_contains "$report" "$dir/$TOOL did not report a version" "the failing copy was not named"
|
|
202
|
+
pass "a copy that reports no version is a check failure, not a pass"
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
test_missing_command_is_reported() {
|
|
206
|
+
local home out
|
|
207
|
+
home=$(make_home absent)
|
|
208
|
+
write_config "$home" '{"tools":[{"name":"herdr","command":"herdr-absent-fixture"}]}'
|
|
209
|
+
out="$home/out.txt"
|
|
210
|
+
run_check "$home" "$PATH" "$out"
|
|
211
|
+
assert_contains "$(cat "$out")" "herdr check failed: herdr-absent-fixture is not on PATH" "a watched command missing from PATH was not reported"
|
|
212
|
+
pass "a watched command missing from PATH is reported"
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# --- published updates ------------------------------------------------------
|
|
216
|
+
|
|
217
|
+
test_announced_update_is_reported_from_the_tool_itself() {
|
|
218
|
+
local home dir out report
|
|
219
|
+
# no-mistakes already announces its own update on stderr; read that rather
|
|
220
|
+
# than reimplementing its version lookup.
|
|
221
|
+
home=$(make_home announce)
|
|
222
|
+
dir="$TMP_ROOT/announce/bin"
|
|
223
|
+
mkdir -p "$dir"
|
|
224
|
+
cat > "$dir/no-mistakes-fixture" <<'SH'
|
|
225
|
+
#!/usr/bin/env bash
|
|
226
|
+
printf '1.46.0\n'
|
|
227
|
+
printf 'A new version of no-mistakes is available: v1.46.0 -> v1.47.0\n' >&2
|
|
228
|
+
SH
|
|
229
|
+
chmod 0755 "$dir/no-mistakes-fixture"
|
|
230
|
+
write_config "$home" '{"tools":[{"name":"no-mistakes","command":"no-mistakes-fixture","announce_pattern":"A new version of no-mistakes is available: [^ ]+ -> [^ ]+"}]}'
|
|
231
|
+
out="$home/out.txt"
|
|
232
|
+
run_check "$home" "$(fixture_path "$dir")" "$out"
|
|
233
|
+
report=$(cat "$out")
|
|
234
|
+
assert_contains "$report" "no-mistakes update available: A new version of no-mistakes is available: v1.46.0 -> v1.47.0" "the tool's own update announcement was not reported"
|
|
235
|
+
assert_not_contains "$report" "not in effect" "a published update must not be reported as PATH skew"
|
|
236
|
+
pass "a tool's own update announcement is read from its output"
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
test_announcement_is_read_from_a_second_command() {
|
|
240
|
+
local home dir out report quiet_home
|
|
241
|
+
# The real no-mistakes prints its version for --version but announces a new
|
|
242
|
+
# release only on its other commands, so the announcement has to be asked of a
|
|
243
|
+
# command of its own while the version probe keeps reporting the version.
|
|
244
|
+
home=$(make_home announce-args)
|
|
245
|
+
dir="$TMP_ROOT/announce-args/bin"
|
|
246
|
+
mkdir -p "$dir"
|
|
247
|
+
cat > "$dir/no-mistakes-fixture" <<'SH'
|
|
248
|
+
#!/usr/bin/env bash
|
|
249
|
+
if [ "${1:-}" = "--version" ]; then
|
|
250
|
+
printf 'no-mistakes version v1.46.0\n'
|
|
251
|
+
exit 0
|
|
252
|
+
fi
|
|
253
|
+
printf 'A new version of no-mistakes is available: v1.46.0 -> v1.53.0\n' >&2
|
|
254
|
+
printf 'Usage: no-mistakes <command>\n'
|
|
255
|
+
SH
|
|
256
|
+
chmod 0755 "$dir/no-mistakes-fixture"
|
|
257
|
+
out="$home/out.txt"
|
|
258
|
+
|
|
259
|
+
write_config "$home" '{"tools":[{"name":"no-mistakes","command":"no-mistakes-fixture","version_args":["--version"],"announce_args":["--help"],"announce_pattern":"A new version of no-mistakes is available: [^ ]+ -> [^ ]+"}]}'
|
|
260
|
+
run_check "$home" "$(fixture_path "$dir")" "$out"
|
|
261
|
+
report=$(cat "$out")
|
|
262
|
+
assert_contains "$report" "no-mistakes update available: A new version of no-mistakes is available: v1.46.0 -> v1.53.0" "the announcement was not read from the command that carries it"
|
|
263
|
+
assert_not_contains "$report" "check failed" "the version probe stopped reporting this copy's version"
|
|
264
|
+
|
|
265
|
+
# Control: the same tool watched without announce_args sees only the version
|
|
266
|
+
# probe, which never carries the announcement, so the update is missed.
|
|
267
|
+
quiet_home=$(make_home announce-args-control)
|
|
268
|
+
write_config "$quiet_home" '{"tools":[{"name":"no-mistakes","command":"no-mistakes-fixture","version_args":["--version"],"announce_pattern":"A new version of no-mistakes is available: [^ ]+ -> [^ ]+"}]}'
|
|
269
|
+
run_check "$quiet_home" "$(fixture_path "$dir")" "$quiet_home/out.txt"
|
|
270
|
+
[ ! -s "$quiet_home/out.txt" ] || fail "the control home reported without a second command, so this test proves nothing: $(cat "$quiet_home/out.txt")"
|
|
271
|
+
pass "an announcement carried by another command is read from that command"
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
test_unusable_announce_pattern_is_reported_not_read_as_silence() {
|
|
275
|
+
local home dir out status
|
|
276
|
+
# A pattern the search cannot use answers exactly like a tool with nothing to
|
|
277
|
+
# announce, which is the silently dead update source this check exists to
|
|
278
|
+
# prevent. It is reported as that tool's own check failure.
|
|
279
|
+
home=$(make_home bad-pattern)
|
|
280
|
+
dir="$TMP_ROOT/bad-pattern/bin"
|
|
281
|
+
make_copy "$dir" no-mistakes-fixture 'no-mistakes version v1.46.0'
|
|
282
|
+
write_config "$home" '{"tools":[{"name":"no-mistakes","command":"no-mistakes-fixture","announce_pattern":"A new version of no-mistakes is available: ([^ ]+ -> [^ ]+"}]}'
|
|
283
|
+
out="$home/out.txt"
|
|
284
|
+
run_check "$home" "$(fixture_path "$dir")" "$out"
|
|
285
|
+
assert_contains "$(cat "$out")" "no-mistakes check failed: announce_pattern is not a usable extended regular expression" "a pattern that cannot be used was read as nothing to announce"
|
|
286
|
+
|
|
287
|
+
# Arming is a deliberate operator action, so the same registry refuses it
|
|
288
|
+
# rather than arming a check with a source that can never fire.
|
|
289
|
+
status=0
|
|
290
|
+
FM_HOME="$home" "$CHECK" arm >/dev/null 2>&1 || status=$?
|
|
291
|
+
expect_code 1 "$status" "arm with an unusable announce_pattern exit"
|
|
292
|
+
assert_absent "$home/state/tool-updates.check.sh" "arm registered a check whose announcement source cannot fire"
|
|
293
|
+
pass "an announce_pattern that cannot be used is reported instead of read as silence"
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
test_one_broken_pattern_does_not_blind_the_rest_of_the_sweep() {
|
|
297
|
+
local home stale fresh dir out report
|
|
298
|
+
# A one character typo in one tool's pattern must not turn off the detector for
|
|
299
|
+
# every other tool. The PATH skew below is the whole reason this check exists,
|
|
300
|
+
# so it has to be reported in the same sweep as the pattern problem.
|
|
301
|
+
home=$(make_home pattern-blind)
|
|
302
|
+
stale="$TMP_ROOT/pattern-blind/mise/installs/herdr/latest/bin"
|
|
303
|
+
fresh="$TMP_ROOT/pattern-blind/local/bin"
|
|
304
|
+
dir="$TMP_ROOT/pattern-blind/announce/bin"
|
|
305
|
+
make_copy "$stale" "$TOOL" 'herdr 0.8.0'
|
|
306
|
+
make_copy "$fresh" "$TOOL" 'herdr 0.8.2'
|
|
307
|
+
make_copy "$dir" no-mistakes-fixture 'no-mistakes version v1.46.0'
|
|
308
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"},{\"name\":\"no-mistakes\",\"command\":\"no-mistakes-fixture\",\"announce_pattern\":\"A new version of no-mistakes is available: ([^ ]+ -> [^ ]+\"}]}"
|
|
309
|
+
out="$home/out.txt"
|
|
310
|
+
run_check "$home" "$(fixture_path "$stale:$fresh:$dir")" "$out"
|
|
311
|
+
report=$(cat "$out")
|
|
312
|
+
assert_contains "$report" "herdr update not in effect: PATH resolves 0.8.0 at $stale/$TOOL" "a broken pattern on another tool suppressed the PATH skew report"
|
|
313
|
+
assert_contains "$report" "no-mistakes check failed: announce_pattern is not a usable extended regular expression" "the tool whose pattern cannot be used was not named"
|
|
314
|
+
[ "$(wc -l < "$out" | tr -d '[:space:]')" = 1 ] || fail "the report must stay exactly one line"
|
|
315
|
+
pass "a broken pattern is reported for its own tool and the rest of the sweep still reports"
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
test_an_unchecked_announcement_source_is_not_read_as_current() {
|
|
319
|
+
local home dir out report
|
|
320
|
+
# When the budget is gone the separate announcement command cannot run, and the
|
|
321
|
+
# version probe's output never carries the announcement. Searching that output
|
|
322
|
+
# anyway would present a source that was never asked as a clean result, which is
|
|
323
|
+
# the same silently dead source announce_args was added to close.
|
|
324
|
+
home=$(make_home announce-budget)
|
|
325
|
+
dir="$TMP_ROOT/announce-budget/bin"
|
|
326
|
+
mkdir -p "$dir"
|
|
327
|
+
cat > "$dir/no-mistakes-fixture" <<'SH'
|
|
328
|
+
#!/usr/bin/env bash
|
|
329
|
+
if [ "${1:-}" = "--version" ]; then
|
|
330
|
+
printf 'no-mistakes version v1.46.0\n'
|
|
331
|
+
sleep 30
|
|
332
|
+
exit 0
|
|
333
|
+
fi
|
|
334
|
+
printf 'A new version of no-mistakes is available: v1.46.0 -> v1.53.0\n' >&2
|
|
335
|
+
SH
|
|
336
|
+
chmod 0755 "$dir/no-mistakes-fixture"
|
|
337
|
+
write_config "$home" '{"tools":[{"name":"no-mistakes","command":"no-mistakes-fixture","version_args":["--version"],"announce_args":["--help"],"announce_pattern":"A new version of no-mistakes is available: [^ ]+ -> [^ ]+"}]}'
|
|
338
|
+
out="$home/out.txt"
|
|
339
|
+
# The deadline is whole-second granular (real_epoch is `date +%s`), so a
|
|
340
|
+
# budget of 1 leaves headroom anywhere in (0, 1] seconds: when the sweep
|
|
341
|
+
# starts near the end of a second the very first budget check already reads
|
|
342
|
+
# as exhausted and the sweep reports "before every copy answered" instead of
|
|
343
|
+
# reaching the announcement step this case is about. A budget of 2 guarantees
|
|
344
|
+
# more than a full second of headroom for the millisecond-scale work before
|
|
345
|
+
# the copy loop, while the version probe below (bounded, then sleeping 30)
|
|
346
|
+
# still exhausts the budget before the announcement check.
|
|
347
|
+
run_check "$home" "$(fixture_path "$dir")" "$out" FM_TOOL_UPDATE_BUDGET_SECS=2
|
|
348
|
+
report=$(cat "$out")
|
|
349
|
+
assert_contains "$report" "no-mistakes check failed: the time budget ran out before the update announcement was checked" "an announcement source that was never asked was not reported"
|
|
350
|
+
pass "an announcement source the budget could not reach is reported, not read as current"
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
test_an_announcement_probe_that_does_not_answer_is_reported() {
|
|
354
|
+
local home dir out report
|
|
355
|
+
# no-mistakes learns about a new release from the network, so the command that
|
|
356
|
+
# carries the announcement is exactly the one that stalls on a flaky link. A
|
|
357
|
+
# source that was asked and never answered must not read as a clean sweep.
|
|
358
|
+
home=$(make_home announce-mute)
|
|
359
|
+
dir="$TMP_ROOT/announce-mute/bin"
|
|
360
|
+
mkdir -p "$dir"
|
|
361
|
+
cat > "$dir/no-mistakes-fixture" <<'SH'
|
|
362
|
+
#!/usr/bin/env bash
|
|
363
|
+
if [ "${1:-}" = "--version" ]; then
|
|
364
|
+
printf 'no-mistakes version v1.46.0\n'
|
|
365
|
+
exit 0
|
|
366
|
+
fi
|
|
367
|
+
sleep 30
|
|
368
|
+
SH
|
|
369
|
+
chmod 0755 "$dir/no-mistakes-fixture"
|
|
370
|
+
write_config "$home" '{"tools":[{"name":"no-mistakes","command":"no-mistakes-fixture","version_args":["--version"],"announce_args":["--help"],"announce_pattern":"A new version of no-mistakes is available: [^ ]+ -> [^ ]+"}]}'
|
|
371
|
+
out="$home/out.txt"
|
|
372
|
+
run_check "$home" "$(fixture_path "$dir")" "$out" FM_TOOL_UPDATE_PROBE_SECS=1
|
|
373
|
+
report=$(cat "$out")
|
|
374
|
+
assert_contains "$report" "no-mistakes check failed: $dir/no-mistakes-fixture did not answer when asked for its update announcement" "an announcement probe that never answered was read as a clean sweep"
|
|
375
|
+
pass "an announcement probe that does not answer is reported, not read as current"
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
test_quiet_tool_with_announce_pattern_is_silent() {
|
|
379
|
+
local home dir out
|
|
380
|
+
home=$(make_home announce-quiet)
|
|
381
|
+
dir="$TMP_ROOT/announce-quiet/bin"
|
|
382
|
+
make_copy "$dir" no-mistakes-fixture '1.46.0'
|
|
383
|
+
write_config "$home" '{"tools":[{"name":"no-mistakes","command":"no-mistakes-fixture","announce_pattern":"A new version of no-mistakes is available: [^ ]+ -> [^ ]+"}]}'
|
|
384
|
+
out="$home/out.txt"
|
|
385
|
+
run_check "$home" "$(fixture_path "$dir")" "$out"
|
|
386
|
+
[ ! -s "$out" ] || fail "a tool announcing nothing produced a report: $(cat "$out")"
|
|
387
|
+
pass "a tool that announces nothing stays silent"
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
# --- git sources ------------------------------------------------------------
|
|
391
|
+
|
|
392
|
+
# git_fixture <name>: a work repo whose origin branch is two commits ahead,
|
|
393
|
+
# with those commits already present locally so the count is exact.
|
|
394
|
+
git_fixture() {
|
|
395
|
+
local name=$1 bare work
|
|
396
|
+
bare="$TMP_ROOT/$name.git"
|
|
397
|
+
work="$TMP_ROOT/$name"
|
|
398
|
+
git init -q --bare --initial-branch=main "$bare"
|
|
399
|
+
git clone -q "$bare" "$work" 2>/dev/null
|
|
400
|
+
printf 'one\n' > "$work/f1"
|
|
401
|
+
git -C "$work" add f1
|
|
402
|
+
git -C "$work" commit -qm one
|
|
403
|
+
printf 'two\n' > "$work/f2"
|
|
404
|
+
git -C "$work" add f2
|
|
405
|
+
git -C "$work" commit -qm two
|
|
406
|
+
printf 'three\n' > "$work/f3"
|
|
407
|
+
git -C "$work" add f3
|
|
408
|
+
git -C "$work" commit -qm three
|
|
409
|
+
git -C "$work" push -q origin main
|
|
410
|
+
git -C "$work" remote set-head origin main >/dev/null 2>&1
|
|
411
|
+
printf '%s\n' "$work"
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
test_commits_behind_origin_are_reported() {
|
|
415
|
+
local home work out head_before
|
|
416
|
+
home=$(make_home git-behind)
|
|
417
|
+
work=$(git_fixture git-behind-repo)
|
|
418
|
+
git -C "$work" reset -q --hard HEAD~2
|
|
419
|
+
head_before=$(git -C "$work" rev-parse HEAD)
|
|
420
|
+
write_config "$home" "{\"tools\":[{\"name\":\"firstmate\",\"git\":{\"repo\":\"$work\",\"remote\":\"origin\",\"branch\":\"main\"}}]}"
|
|
421
|
+
out="$home/out.txt"
|
|
422
|
+
run_check "$home" "$PATH" "$out"
|
|
423
|
+
assert_contains "$(cat "$out")" "firstmate update available: local main is 2 commits behind origin/main" "commits behind the origin branch were not reported"
|
|
424
|
+
# The probe is read-only: the watched repository must be untouched.
|
|
425
|
+
[ "$(git -C "$work" rev-parse HEAD)" = "$head_before" ] || fail "the check moved the watched repository's HEAD"
|
|
426
|
+
git -C "$work" diff --quiet || fail "the check left changes in the watched repository"
|
|
427
|
+
pass "commits behind the origin branch are reported without touching the repository"
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
test_default_branch_is_detected_when_branch_is_omitted() {
|
|
431
|
+
local home work out
|
|
432
|
+
home=$(make_home git-default)
|
|
433
|
+
work=$(git_fixture git-default-repo)
|
|
434
|
+
git -C "$work" reset -q --hard HEAD~1
|
|
435
|
+
write_config "$home" "{\"tools\":[{\"name\":\"firstmate\",\"git\":{\"repo\":\"$work\"}}]}"
|
|
436
|
+
out="$home/out.txt"
|
|
437
|
+
run_check "$home" "$PATH" "$out"
|
|
438
|
+
assert_contains "$(cat "$out")" "firstmate update available: local main is 1 commit behind origin/main" "the default branch was not detected from the remote"
|
|
439
|
+
pass "an omitted branch is detected from the remote's default branch"
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
test_default_branch_is_asked_of_the_remote_when_the_clone_has_no_record() {
|
|
443
|
+
local home work out
|
|
444
|
+
# A --single-branch clone, or one that never ran remote set-head, has no local
|
|
445
|
+
# refs/remotes/origin/HEAD. The remote still knows its default branch, so this
|
|
446
|
+
# must report the update rather than an unactionable check failure.
|
|
447
|
+
home=$(make_home git-symref)
|
|
448
|
+
work=$(git_fixture git-symref-repo)
|
|
449
|
+
git -C "$work" remote set-head origin --delete >/dev/null 2>&1
|
|
450
|
+
git -C "$work" reset -q --hard HEAD~2
|
|
451
|
+
# Ask git what it knows rather than looking for a loose ref file, which never
|
|
452
|
+
# exists under a non-loose ref backend and would make this vacuous there.
|
|
453
|
+
! git -C "$work" symbolic-ref --quiet refs/remotes/origin/HEAD >/dev/null 2>&1 \
|
|
454
|
+
|| fail "the fixture still records the remote's default branch locally"
|
|
455
|
+
write_config "$home" "{\"tools\":[{\"name\":\"firstmate\",\"git\":{\"repo\":\"$work\"}}]}"
|
|
456
|
+
out="$home/out.txt"
|
|
457
|
+
run_check "$home" "$PATH" "$out"
|
|
458
|
+
assert_contains "$(cat "$out")" "firstmate update available: local main is 2 commits behind origin/main" "the default branch was not asked of the remote"
|
|
459
|
+
pass "the default branch is asked of the remote when the clone has no local record"
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
test_current_and_ahead_repositories_are_silent() {
|
|
463
|
+
local home work out
|
|
464
|
+
home=$(make_home git-current)
|
|
465
|
+
work=$(git_fixture git-current-repo)
|
|
466
|
+
write_config "$home" "{\"tools\":[{\"name\":\"firstmate\",\"git\":{\"repo\":\"$work\",\"branch\":\"main\"}}]}"
|
|
467
|
+
out="$home/out.txt"
|
|
468
|
+
run_check "$home" "$PATH" "$out"
|
|
469
|
+
[ ! -s "$out" ] || fail "an up to date repository produced a report: $(cat "$out")"
|
|
470
|
+
|
|
471
|
+
printf 'local only\n' > "$work/f4"
|
|
472
|
+
git -C "$work" add f4
|
|
473
|
+
git -C "$work" commit -qm four
|
|
474
|
+
rm -f "$home/state/.tool-updates"
|
|
475
|
+
run_check "$home" "$PATH" "$out"
|
|
476
|
+
[ ! -s "$out" ] || fail "a repository ahead of its origin branch produced a report: $(cat "$out")"
|
|
477
|
+
pass "a repository that is current or ahead of its origin branch is silent"
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
test_unusable_git_source_is_reported() {
|
|
481
|
+
local home out
|
|
482
|
+
home=$(make_home git-broken)
|
|
483
|
+
mkdir -p "$TMP_ROOT/git-broken/not-a-repo"
|
|
484
|
+
write_config "$home" "{\"tools\":[{\"name\":\"firstmate\",\"git\":{\"repo\":\"$TMP_ROOT/git-broken/not-a-repo\",\"branch\":\"main\"}}]}"
|
|
485
|
+
out="$home/out.txt"
|
|
486
|
+
run_check "$home" "$PATH" "$out"
|
|
487
|
+
assert_contains "$(cat "$out")" "firstmate check failed" "an unusable git source was not reported"
|
|
488
|
+
pass "an unusable git source is reported as a check failure"
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
test_unreadable_remote_is_not_reported_as_a_missing_branch() {
|
|
492
|
+
local home work out report
|
|
493
|
+
# A remote that cannot be reached at all and a branch that was deleted are
|
|
494
|
+
# different problems with different repairs. Reporting the first as the second
|
|
495
|
+
# wakes firstmate with a diagnosis that is simply wrong, so the report must
|
|
496
|
+
# name only what the probe established.
|
|
497
|
+
home=$(make_home git-unreadable)
|
|
498
|
+
work=$(git_fixture git-unreadable-repo)
|
|
499
|
+
rm -rf "$TMP_ROOT/git-unreadable-repo.git"
|
|
500
|
+
write_config "$home" "{\"tools\":[{\"name\":\"firstmate\",\"git\":{\"repo\":\"$work\",\"remote\":\"origin\",\"branch\":\"main\"}}]}"
|
|
501
|
+
out="$home/out.txt"
|
|
502
|
+
run_check "$home" "$PATH" "$out"
|
|
503
|
+
report=$(cat "$out")
|
|
504
|
+
assert_contains "$report" "firstmate check failed" "a remote that could not be read was not reported"
|
|
505
|
+
assert_contains "$report" "origin could not be reached or read" "the report does not name the condition the probe actually found"
|
|
506
|
+
assert_not_contains "$report" "has no branch" "a remote that could not be read was reported as a deleted branch"
|
|
507
|
+
pass "a remote that cannot be read is reported as unreadable, not as a missing branch"
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
test_missing_branch_on_a_readable_remote_is_still_reported() {
|
|
511
|
+
local home work out
|
|
512
|
+
# The other side of the case above: the remote answers, and it really does not
|
|
513
|
+
# have the watched branch, so that must still be reported as a missing branch.
|
|
514
|
+
home=$(make_home git-no-branch)
|
|
515
|
+
work=$(git_fixture git-no-branch-repo)
|
|
516
|
+
write_config "$home" "{\"tools\":[{\"name\":\"firstmate\",\"git\":{\"repo\":\"$work\",\"remote\":\"origin\",\"branch\":\"release\"}}]}"
|
|
517
|
+
out="$home/out.txt"
|
|
518
|
+
run_check "$home" "$PATH" "$out"
|
|
519
|
+
assert_contains "$(cat "$out")" "firstmate check failed: origin has no branch release" "a branch the remote does not have was not reported as missing"
|
|
520
|
+
pass "a branch a readable remote does not have is still reported as missing"
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
test_git_probes_stop_when_the_sweep_budget_is_gone() {
|
|
524
|
+
local home work slow out report
|
|
525
|
+
# The git probes are the several-in-a-row case, two of them over the network,
|
|
526
|
+
# so they are the ones that can push a sweep past the watcher's own timeout and
|
|
527
|
+
# leave it killed with nothing printed at all. Here the tool's command probe
|
|
528
|
+
# spends the whole budget, so its git probes must not start: the sweep says
|
|
529
|
+
# which tool it did not finish instead of quietly running on.
|
|
530
|
+
home=$(make_home git-budget)
|
|
531
|
+
work=$(git_fixture git-budget-repo)
|
|
532
|
+
git -C "$work" reset -q --hard HEAD~2
|
|
533
|
+
slow="$TMP_ROOT/git-budget/bin"
|
|
534
|
+
make_slow_copy "$slow" "$TOOL" 30
|
|
535
|
+
write_config "$home" "{\"tools\":[{\"name\":\"firstmate\",\"command\":\"$TOOL\",\"git\":{\"repo\":\"$work\",\"remote\":\"origin\",\"branch\":\"main\"}}]}"
|
|
536
|
+
out="$home/out.txt"
|
|
537
|
+
run_check "$home" "$(fixture_path "$slow")" "$out" FM_TOOL_UPDATE_BUDGET_SECS=1
|
|
538
|
+
report=$(cat "$out")
|
|
539
|
+
assert_contains "$report" "check incomplete: the time budget ran out before firstmate" "a sweep with no budget left did not say which tool it did not finish"
|
|
540
|
+
assert_not_contains "$report" "commits behind" "the git probes ran after the sweep budget was already gone"
|
|
541
|
+
pass "git probes stop and name their tool once the sweep budget is gone"
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
test_a_git_probe_that_does_not_answer_is_not_an_update() {
|
|
545
|
+
local home work dir out report head_before
|
|
546
|
+
# The local git probes are bounded too, so a bound that is hit must not be read
|
|
547
|
+
# as the answer "this clone does not have that commit". This clone is ahead of
|
|
548
|
+
# its remote branch, which is silent when the probes answer, so any claim of an
|
|
549
|
+
# available update here was never established.
|
|
550
|
+
home=$(make_home git-mute)
|
|
551
|
+
work=$(git_fixture git-mute-repo)
|
|
552
|
+
printf 'local only\n' > "$work/f4"
|
|
553
|
+
git -C "$work" add f4
|
|
554
|
+
git -C "$work" commit -qm four
|
|
555
|
+
head_before=$(git -C "$work" rev-parse HEAD)
|
|
556
|
+
|
|
557
|
+
# A git that answers everything except the object query, which never answers.
|
|
558
|
+
dir="$TMP_ROOT/git-mute/bin"
|
|
559
|
+
mkdir -p "$dir"
|
|
560
|
+
cat > "$dir/git" <<SH
|
|
561
|
+
#!/usr/bin/env bash
|
|
562
|
+
for arg in "\$@"; do
|
|
563
|
+
if [ "\$arg" = cat-file ]; then
|
|
564
|
+
sleep 30
|
|
565
|
+
exit 0
|
|
566
|
+
fi
|
|
567
|
+
done
|
|
568
|
+
exec $(command -v git) "\$@"
|
|
569
|
+
SH
|
|
570
|
+
chmod 0755 "$dir/git"
|
|
571
|
+
|
|
572
|
+
write_config "$home" "{\"tools\":[{\"name\":\"firstmate\",\"git\":{\"repo\":\"$work\",\"remote\":\"origin\",\"branch\":\"main\"}}]}"
|
|
573
|
+
out="$home/out.txt"
|
|
574
|
+
# The bound is wide enough that the earlier probes answer comfortably, so the
|
|
575
|
+
# only probe that can hit it is the object query the fixture stalls. Asserting
|
|
576
|
+
# that specific report keeps an unrelated timeout from passing this case for the
|
|
577
|
+
# wrong reason.
|
|
578
|
+
run_check "$home" "$(fixture_path "$dir")" "$out" FM_TOOL_UPDATE_PROBE_SECS=3
|
|
579
|
+
report=$(cat "$out")
|
|
580
|
+
assert_not_contains "$report" "update available" "a probe that never answered was reported as an available update"
|
|
581
|
+
assert_contains "$report" "firstmate check failed: $work did not answer whether it already has" "the stalled object query was not the reported failure"
|
|
582
|
+
[ "$(git -C "$work" rev-parse HEAD)" = "$head_before" ] || fail "the check moved the watched repository's HEAD"
|
|
583
|
+
pass "a git probe that does not answer is reported as a failure, never as an update"
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
test_a_stalled_repository_probe_is_not_reported_as_not_a_repository() {
|
|
587
|
+
local home work dir out report
|
|
588
|
+
# The very first git probe is bounded too. A clone on a stalled mount that
|
|
589
|
+
# never answers must be reported as not answering, not as not being a git
|
|
590
|
+
# repository, which is a diagnosis the probe never established.
|
|
591
|
+
home=$(make_home git-stall)
|
|
592
|
+
work=$(git_fixture git-stall-repo)
|
|
593
|
+
git -C "$work" reset -q --hard HEAD~2
|
|
594
|
+
|
|
595
|
+
dir="$TMP_ROOT/git-stall/bin"
|
|
596
|
+
mkdir -p "$dir"
|
|
597
|
+
cat > "$dir/git" <<SH
|
|
598
|
+
#!/usr/bin/env bash
|
|
599
|
+
for arg in "\$@"; do
|
|
600
|
+
if [ "\$arg" = rev-parse ]; then
|
|
601
|
+
sleep 30
|
|
602
|
+
exit 0
|
|
603
|
+
fi
|
|
604
|
+
done
|
|
605
|
+
exec $(command -v git) "\$@"
|
|
606
|
+
SH
|
|
607
|
+
chmod 0755 "$dir/git"
|
|
608
|
+
|
|
609
|
+
write_config "$home" "{\"tools\":[{\"name\":\"firstmate\",\"git\":{\"repo\":\"$work\",\"remote\":\"origin\",\"branch\":\"main\"}}]}"
|
|
610
|
+
out="$home/out.txt"
|
|
611
|
+
run_check "$home" "$(fixture_path "$dir")" "$out" FM_TOOL_UPDATE_PROBE_SECS=1
|
|
612
|
+
report=$(cat "$out")
|
|
613
|
+
assert_contains "$report" "firstmate check failed: $work did not answer whether it is a git repository" "a repository probe that never answered was not reported as such"
|
|
614
|
+
assert_not_contains "$report" "is not a git repository" "a repository probe that never answered was reported as not a repository"
|
|
615
|
+
pass "a stalled repository probe is reported as no answer, not as not a repository"
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
# --- registry and reporting contract ----------------------------------------
|
|
619
|
+
|
|
620
|
+
test_absent_registry_is_silent() {
|
|
621
|
+
local home out
|
|
622
|
+
home=$(make_home no-config)
|
|
623
|
+
out="$home/out.txt"
|
|
624
|
+
run_check "$home" "$PATH" "$out"
|
|
625
|
+
[ ! -s "$out" ] || fail "check spoke without a watched tool registry: $(cat "$out")"
|
|
626
|
+
assert_absent "$home/state/.tool-updates" "check wrote a record without a registry"
|
|
627
|
+
pass "no watched tool registry means no output at all"
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
test_malformed_registry_is_reported_not_ignored() {
|
|
631
|
+
local home out
|
|
632
|
+
home=$(make_home bad-config)
|
|
633
|
+
printf 'not json at all\n' > "$home/config/watched-tools.json"
|
|
634
|
+
out="$home/out.txt"
|
|
635
|
+
run_check "$home" "$PATH" "$out"
|
|
636
|
+
assert_contains "$(cat "$out")" "watched tool registry: the watched tool registry is not valid JSON" "a malformed registry was silently ignored"
|
|
637
|
+
|
|
638
|
+
printf '%s\n' '{"tools":[{"name":"herdr"}]}' > "$home/config/watched-tools.json"
|
|
639
|
+
rm -f "$home/state/.tool-updates"
|
|
640
|
+
run_check "$home" "$PATH" "$out"
|
|
641
|
+
assert_contains "$(cat "$out")" "tool herdr needs command, git, or both" "a tool entry with no update source was accepted"
|
|
642
|
+
|
|
643
|
+
printf '%s\n' '{"tools":[{"name":"herdr","command":"herdr; rm -rf /"}]}' > "$home/config/watched-tools.json"
|
|
644
|
+
rm -f "$home/state/.tool-updates"
|
|
645
|
+
run_check "$home" "$PATH" "$out"
|
|
646
|
+
assert_contains "$(cat "$out")" "command must be a bare executable name" "a command name with shell characters was accepted"
|
|
647
|
+
|
|
648
|
+
printf '%s\n' '{"tools":[{"name":"herdr","command":"herdr","announce_args":["--help"]}]}' > "$home/config/watched-tools.json"
|
|
649
|
+
rm -f "$home/state/.tool-updates"
|
|
650
|
+
run_check "$home" "$PATH" "$out"
|
|
651
|
+
assert_contains "$(cat "$out")" "tool herdr announce_args needs announce_pattern" "a command to search with no pattern to search for was accepted"
|
|
652
|
+
pass "a malformed registry is reported instead of quietly skipped"
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
test_findings_are_reported_once_until_they_change() {
|
|
656
|
+
local home stale fresh out path
|
|
657
|
+
home=$(make_home no-nag)
|
|
658
|
+
stale="$TMP_ROOT/no-nag/old/bin"
|
|
659
|
+
fresh="$TMP_ROOT/no-nag/new/bin"
|
|
660
|
+
make_copy "$stale" "$TOOL" 'herdr 0.8.0'
|
|
661
|
+
make_copy "$fresh" "$TOOL" 'herdr 0.8.2'
|
|
662
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
663
|
+
out="$home/out.txt"
|
|
664
|
+
path=$(fixture_path "$stale:$fresh")
|
|
665
|
+
|
|
666
|
+
run_check "$home" "$path" "$out"
|
|
667
|
+
assert_contains "$(cat "$out")" "not in effect" "the first sweep did not report the pending update"
|
|
668
|
+
run_check "$home" "$path" "$out"
|
|
669
|
+
[ ! -s "$out" ] || fail "the same pending update was reported twice: $(cat "$out")"
|
|
670
|
+
|
|
671
|
+
# A changed finding is news again.
|
|
672
|
+
make_copy "$fresh" "$TOOL" 'herdr 0.9.0'
|
|
673
|
+
run_check "$home" "$path" "$out"
|
|
674
|
+
assert_contains "$(cat "$out")" "0.9.0 is installed" "a changed finding was suppressed as a repeat"
|
|
675
|
+
|
|
676
|
+
# Once the condition clears, the report clears with it, and a later return of
|
|
677
|
+
# the same condition is reported again.
|
|
678
|
+
make_copy "$stale" "$TOOL" 'herdr 0.9.0'
|
|
679
|
+
run_check "$home" "$path" "$out"
|
|
680
|
+
[ ! -s "$out" ] || fail "a cleared finding still produced a report: $(cat "$out")"
|
|
681
|
+
make_copy "$stale" "$TOOL" 'herdr 0.8.0'
|
|
682
|
+
run_check "$home" "$path" "$out"
|
|
683
|
+
assert_contains "$(cat "$out")" "PATH resolves 0.8.0" "a returning finding was not reported again"
|
|
684
|
+
pass "the same pending update is reported once, and a change is reported again"
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
test_an_overlong_report_says_it_was_cut() {
|
|
688
|
+
local home out report i tools=
|
|
689
|
+
# Many watched tools can outgrow one line. The report must say it was cut
|
|
690
|
+
# rather than end mid-finding as if that were everything found.
|
|
691
|
+
home=$(make_home long)
|
|
692
|
+
for i in $(seq 1 30); do
|
|
693
|
+
[ -z "$tools" ] || tools="$tools,"
|
|
694
|
+
tools="$tools{\"name\":\"absent-tool-$i\",\"command\":\"fm-absent-fixture-$i\"}"
|
|
695
|
+
done
|
|
696
|
+
write_config "$home" "{\"tools\":[$tools]}"
|
|
697
|
+
out="$home/out.txt"
|
|
698
|
+
run_check "$home" "$PATH" "$out"
|
|
699
|
+
report=$(cat "$out")
|
|
700
|
+
assert_contains "$report" "[truncated]" "an over-long report was cut without saying so"
|
|
701
|
+
[ "$(wc -l < "$out" | tr -d '[:space:]')" = 1 ] || fail "the cut report must still be exactly one line"
|
|
702
|
+
pass "an over-long report is cut with the shared truncation marker"
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
test_a_finding_past_the_cut_is_still_reported() {
|
|
706
|
+
local home stale fresh out report i tools=
|
|
707
|
+
# Once a report is long enough to be cut, a new finding lands past the cut and
|
|
708
|
+
# leaves the printed line unchanged. It still has to count as news, or the PATH
|
|
709
|
+
# skew this check exists for would be suppressed for good on a busy home.
|
|
710
|
+
home=$(make_home past-cut)
|
|
711
|
+
stale="$TMP_ROOT/past-cut/mise/installs/herdr/latest/bin"
|
|
712
|
+
fresh="$TMP_ROOT/past-cut/local/bin"
|
|
713
|
+
make_copy "$stale" "$TOOL" 'herdr 0.8.0'
|
|
714
|
+
make_copy "$fresh" "$TOOL" 'herdr 0.8.2'
|
|
715
|
+
for i in $(seq 1 30); do
|
|
716
|
+
[ -z "$tools" ] || tools="$tools,"
|
|
717
|
+
tools="$tools{\"name\":\"absent-tool-$i\",\"command\":\"fm-absent-fixture-$i\"}"
|
|
718
|
+
done
|
|
719
|
+
out="$home/out.txt"
|
|
720
|
+
write_config "$home" "{\"tools\":[$tools]}"
|
|
721
|
+
run_check "$home" "$(fixture_path "$stale:$fresh")" "$out"
|
|
722
|
+
assert_contains "$(cat "$out")" "[truncated]" "the first report was not long enough to be cut, so this case proves nothing"
|
|
723
|
+
|
|
724
|
+
# The skew tool goes last, so its finding falls past the cut and the printed
|
|
725
|
+
# line is byte identical to the one the first sweep already recorded.
|
|
726
|
+
write_config "$home" "{\"tools\":[$tools,{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
727
|
+
run_check "$home" "$(fixture_path "$stale:$fresh")" "$out"
|
|
728
|
+
report=$(cat "$out")
|
|
729
|
+
[ -n "$report" ] || fail "a finding past the cut produced no report at all, so it can never reach the watcher"
|
|
730
|
+
assert_contains "$report" "[truncated]" "the second report was not cut, so the finding was not past the cut"
|
|
731
|
+
pass "a finding that lands past the cut is still reported as news"
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
test_probes_are_skipped_between_intervals() {
|
|
735
|
+
local home dir out status now
|
|
736
|
+
home=$(make_home cadence)
|
|
737
|
+
dir="$TMP_ROOT/cadence/bin"
|
|
738
|
+
make_copy "$dir" "$TOOL" 'herdr 0.8.2'
|
|
739
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
740
|
+
out="$home/out.txt"
|
|
741
|
+
now=1700000000
|
|
742
|
+
|
|
743
|
+
status=0
|
|
744
|
+
FM_HOME="$home" PATH="$(fixture_path "$dir")" FM_CHECK_TIMEOUT=30 FM_TOOL_UPDATE_INTERVAL=900 FM_TOOL_UPDATE_NOW="$now" \
|
|
745
|
+
"$CHECK" >"$out" 2>&1 || status=$?
|
|
746
|
+
expect_code 0 "$status" "first cadence run exit"
|
|
747
|
+
assert_grep 'fm-tool-updates-v1' "$home/state/.tool-updates" "the first run did not record its sweep"
|
|
748
|
+
|
|
749
|
+
# A finding appears, but the interval has not elapsed, so no probe runs.
|
|
750
|
+
make_copy "$dir" "$TOOL" 'no version here'
|
|
751
|
+
status=0
|
|
752
|
+
FM_HOME="$home" PATH="$(fixture_path "$dir")" FM_CHECK_TIMEOUT=30 FM_TOOL_UPDATE_INTERVAL=900 FM_TOOL_UPDATE_NOW="$((now + 300))" \
|
|
753
|
+
"$CHECK" >"$out" 2>&1 || status=$?
|
|
754
|
+
expect_code 0 "$status" "gated cadence run exit"
|
|
755
|
+
[ ! -s "$out" ] || fail "a run inside the interval probed and spoke: $(cat "$out")"
|
|
756
|
+
|
|
757
|
+
status=0
|
|
758
|
+
FM_HOME="$home" PATH="$(fixture_path "$dir")" FM_CHECK_TIMEOUT=30 FM_TOOL_UPDATE_INTERVAL=900 FM_TOOL_UPDATE_NOW="$((now + 901))" \
|
|
759
|
+
"$CHECK" >"$out" 2>&1 || status=$?
|
|
760
|
+
expect_code 0 "$status" "due cadence run exit"
|
|
761
|
+
assert_contains "$(cat "$out")" "did not report a version" "the run after the interval did not probe"
|
|
762
|
+
pass "probes run once per interval, not on every poll"
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
test_an_oversized_budget_is_cut_to_fit_and_reported() {
|
|
766
|
+
local home stale fresh out report status
|
|
767
|
+
# A sweep budget larger than the watcher's own per check bound lets the watcher
|
|
768
|
+
# kill the run, which prints nothing and records nothing, so the same silence
|
|
769
|
+
# repeats on every poll. Cutting it keeps the detector alive, and the cut is
|
|
770
|
+
# reported so the operator can see the setting was not used as written.
|
|
771
|
+
home=$(make_home budget-cut)
|
|
772
|
+
stale="$TMP_ROOT/budget-cut/mise/installs/herdr/latest/bin"
|
|
773
|
+
fresh="$TMP_ROOT/budget-cut/local/bin"
|
|
774
|
+
make_copy "$stale" "$TOOL" 'herdr 0.8.0'
|
|
775
|
+
make_copy "$fresh" "$TOOL" 'herdr 0.8.2'
|
|
776
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
777
|
+
out="$home/out.txt"
|
|
778
|
+
status=0
|
|
779
|
+
env FM_HOME="$home" PATH="$(fixture_path "$stale:$fresh")" FM_TOOL_UPDATE_INTERVAL=0 \
|
|
780
|
+
FM_TOOL_UPDATE_BUDGET_SECS=60 FM_CHECK_TIMEOUT=30 "$CHECK" >"$out" 2>&1 || status=$?
|
|
781
|
+
expect_code 0 "$status" "oversized budget exit"
|
|
782
|
+
report=$(cat "$out")
|
|
783
|
+
# The cut leaves room for the whole-second rounding and the kill grace as well
|
|
784
|
+
# as one probe bound, so a cut sweep really does end before the watcher bound.
|
|
785
|
+
assert_contains "$report" "sweep budget 60s cut to 27s to stay inside the watcher check timeout of 30s" "a budget that cannot fit the watcher bound was not cut and reported"
|
|
786
|
+
assert_contains "$report" "herdr update not in effect" "the detector went quiet instead of sweeping with the cut budget"
|
|
787
|
+
|
|
788
|
+
# The default budget of 20s fits the default bound, so it is used as written.
|
|
789
|
+
# The record is cleared first because the no-nag gate would otherwise suppress
|
|
790
|
+
# this run, whose bare skew line differs from the cut run's line above.
|
|
791
|
+
rm -f "$home/state/.tool-updates"
|
|
792
|
+
status=0
|
|
793
|
+
env FM_HOME="$home" PATH="$(fixture_path "$stale:$fresh")" FM_TOOL_UPDATE_INTERVAL=0 \
|
|
794
|
+
FM_CHECK_TIMEOUT=30 "$CHECK" >"$out" 2>&1 || status=$?
|
|
795
|
+
expect_code 0 "$status" "default budget exit"
|
|
796
|
+
report=$(cat "$out")
|
|
797
|
+
assert_not_contains "$report" "sweep budget" "the default budget was cut at the default watcher bound"
|
|
798
|
+
assert_contains "$report" "herdr update not in effect" "the sweep stopped reporting with the default budget"
|
|
799
|
+
|
|
800
|
+
# A budget the watcher bound has room for is used as written. The cleared
|
|
801
|
+
# record keeps the no-nag gate from hiding this run's repeat of the same line.
|
|
802
|
+
rm -f "$home/state/.tool-updates"
|
|
803
|
+
status=0
|
|
804
|
+
env FM_HOME="$home" PATH="$(fixture_path "$stale:$fresh")" FM_TOOL_UPDATE_INTERVAL=0 \
|
|
805
|
+
FM_TOOL_UPDATE_BUDGET_SECS=60 FM_CHECK_TIMEOUT=120 "$CHECK" >"$out" 2>&1 || status=$?
|
|
806
|
+
expect_code 0 "$status" "fitting budget exit"
|
|
807
|
+
report=$(cat "$out")
|
|
808
|
+
assert_not_contains "$report" "sweep budget" "a budget that fits the watcher bound was cut anyway"
|
|
809
|
+
assert_contains "$report" "herdr update not in effect" "the sweep stopped reporting with a budget that fits"
|
|
810
|
+
pass "a budget that cannot fit the watcher bound is cut and reported, and the sweep keeps working"
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
test_invalid_environment_and_action_refuse() {
|
|
814
|
+
local home status
|
|
815
|
+
home=$(make_home refuse)
|
|
816
|
+
status=0
|
|
817
|
+
FM_HOME="$home" FM_TOOL_UPDATE_INTERVAL=5 "$CHECK" >/dev/null 2>&1 || status=$?
|
|
818
|
+
expect_code 2 "$status" "too-small interval exit"
|
|
819
|
+
status=0
|
|
820
|
+
FM_HOME="$home" FM_TOOL_UPDATE_PROBE_SECS=0 "$CHECK" >/dev/null 2>&1 || status=$?
|
|
821
|
+
expect_code 2 "$status" "zero probe bound exit"
|
|
822
|
+
status=0
|
|
823
|
+
FM_HOME="$home" FM_TOOL_UPDATE_BUDGET_SECS=999 "$CHECK" >/dev/null 2>&1 || status=$?
|
|
824
|
+
expect_code 2 "$status" "oversized budget exit"
|
|
825
|
+
status=0
|
|
826
|
+
FM_HOME="$home" "$CHECK" sweep >/dev/null 2>&1 || status=$?
|
|
827
|
+
expect_code 2 "$status" "unknown action exit"
|
|
828
|
+
status=0
|
|
829
|
+
FM_HOME="$home" "$CHECK" --help >/dev/null 2>&1 || status=$?
|
|
830
|
+
expect_code 0 "$status" "help exit"
|
|
831
|
+
pass "an out of range bound or unknown action refuses instead of guessing"
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
# --- arming through the existing watcher contract ----------------------------
|
|
835
|
+
|
|
836
|
+
test_arm_registers_the_check_and_disarm_removes_it() {
|
|
837
|
+
local home dir status
|
|
838
|
+
home=$(make_home arm)
|
|
839
|
+
dir="$TMP_ROOT/arm/bin"
|
|
840
|
+
make_copy "$dir" "$TOOL" 'herdr 0.8.2'
|
|
841
|
+
status=0
|
|
842
|
+
FM_HOME="$home" "$CHECK" arm >/dev/null 2>&1 || status=$?
|
|
843
|
+
expect_code 1 "$status" "arm without a registry exit"
|
|
844
|
+
assert_absent "$home/state/tool-updates.check.sh" "arm wrote a check shim without a registry"
|
|
845
|
+
|
|
846
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
847
|
+
status=0
|
|
848
|
+
FM_HOME="$home" "$CHECK" arm >/dev/null || status=$?
|
|
849
|
+
expect_code 0 "$status" "arm exit"
|
|
850
|
+
assert_present "$home/state/tool-updates.check.sh" "arm did not write the check shim"
|
|
851
|
+
assert_present "$home/state/tool-updates.check-trust" "arm did not register the check's bytes"
|
|
852
|
+
[ "$(stat -c %a "$home/state/tool-updates.check.sh" 2>/dev/null || stat -f %Lp "$home/state/tool-updates.check.sh")" = 700 ] \
|
|
853
|
+
|| fail "the check shim is not mode 700"
|
|
854
|
+
assert_grep 'fm-custom-check-v1' "$home/state/tool-updates.check-trust" "the trust binding has the wrong schema"
|
|
855
|
+
|
|
856
|
+
# Arming twice must stay valid rather than invalidating its own binding.
|
|
857
|
+
FM_HOME="$home" "$CHECK" arm >/dev/null || fail "arming twice failed"
|
|
858
|
+
assert_grep 'fm-custom-check-v1' "$home/state/tool-updates.check-trust" "re-arming lost the trust binding"
|
|
859
|
+
|
|
860
|
+
FM_HOME="$home" "$CHECK" disarm >/dev/null || fail "disarm failed"
|
|
861
|
+
assert_absent "$home/state/tool-updates.check.sh" "disarm left the check shim behind"
|
|
862
|
+
assert_absent "$home/state/tool-updates.check-trust" "disarm left the trust binding behind"
|
|
863
|
+
assert_absent "$home/state/.tool-updates" "disarm left the report record behind"
|
|
864
|
+
pass "arm registers a trusted check and disarm removes every trace"
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
test_arm_refuses_a_symlink_at_the_shim_path() {
|
|
868
|
+
local home dir target mode status
|
|
869
|
+
# A stale or hostile symlink at the shim path must be refused rather than
|
|
870
|
+
# followed: following it would write the shim body into a file someone else
|
|
871
|
+
# owns and then make that file executable.
|
|
872
|
+
home=$(make_home arm-symlink)
|
|
873
|
+
dir="$TMP_ROOT/arm-symlink/bin"
|
|
874
|
+
make_copy "$dir" "$TOOL" 'herdr 0.8.2'
|
|
875
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
876
|
+
target="$TMP_ROOT/arm-symlink/not-the-shim.txt"
|
|
877
|
+
printf 'a file the shim must not touch\n' > "$target"
|
|
878
|
+
mode=$(stat -c %a "$target" 2>/dev/null || stat -f %Lp "$target")
|
|
879
|
+
ln -s "$target" "$home/state/tool-updates.check.sh"
|
|
880
|
+
|
|
881
|
+
status=0
|
|
882
|
+
FM_HOME="$home" "$CHECK" arm >/dev/null 2>&1 || status=$?
|
|
883
|
+
expect_code 1 "$status" "arm over a symlink exit"
|
|
884
|
+
[ "$(cat "$target")" = 'a file the shim must not touch' ] || fail "arm followed the symlink and overwrote its target"
|
|
885
|
+
[ "$(stat -c %a "$target" 2>/dev/null || stat -f %Lp "$target")" = "$mode" ] || fail "arm changed the mode of the symlink's target"
|
|
886
|
+
assert_absent "$home/state/tool-updates.check-trust" "arm registered a shim it refused to write"
|
|
887
|
+
pass "a symlink at the shim path is refused instead of followed"
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
test_a_failed_registration_leaves_no_unregistered_shim() {
|
|
891
|
+
local home dir target stale_shim status
|
|
892
|
+
# An unregistered shim in state/ is not inert: the watcher rejects it every
|
|
893
|
+
# cycle and wakes firstmate about unauthenticated state checks until someone
|
|
894
|
+
# deletes it by hand. So a home that could not be armed has to come back to the
|
|
895
|
+
# state it was in, and arm still has to say it failed.
|
|
896
|
+
home=$(make_home arm-register-fail)
|
|
897
|
+
dir="$TMP_ROOT/arm-register-fail/bin"
|
|
898
|
+
make_copy "$dir" "$TOOL" 'herdr 0.8.2'
|
|
899
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
900
|
+
# A symlink at the trust path makes registration refuse, which is the shape any
|
|
901
|
+
# register failure has from arm's side.
|
|
902
|
+
target="$TMP_ROOT/arm-register-fail/not-the-trust.txt"
|
|
903
|
+
printf 'a file the trust binding must not touch\n' > "$target"
|
|
904
|
+
ln -s "$target" "$home/state/tool-updates.check-trust"
|
|
905
|
+
|
|
906
|
+
status=0
|
|
907
|
+
FM_HOME="$home" "$CHECK" arm >/dev/null 2>&1 || status=$?
|
|
908
|
+
expect_code 1 "$status" "arm with an unusable trust path exit"
|
|
909
|
+
assert_absent "$home/state/tool-updates.check.sh" "a failed registration left an unregistered check shim behind"
|
|
910
|
+
[ "$(cat "$target")" = 'a file the trust binding must not touch' ] || fail "arm wrote through the trust symlink"
|
|
911
|
+
|
|
912
|
+
# A shim that was already there is only kept when its trust binding is still
|
|
913
|
+
# intact. Here the binding is unusable, so putting the old bytes back would
|
|
914
|
+
# leave exactly the unbound shim the watcher wakes about, and the home has to
|
|
915
|
+
# end plainly not armed instead.
|
|
916
|
+
stale_shim="$TMP_ROOT/arm-register-fail/shim-armed-earlier"
|
|
917
|
+
printf '#!/usr/bin/env bash\n# a shim armed earlier\nexit 0\n' > "$stale_shim"
|
|
918
|
+
cp "$stale_shim" "$home/state/tool-updates.check.sh"
|
|
919
|
+
chmod 0700 "$home/state/tool-updates.check.sh"
|
|
920
|
+
status=0
|
|
921
|
+
FM_HOME="$home" "$CHECK" arm >/dev/null 2>&1 || status=$?
|
|
922
|
+
expect_code 1 "$status" "arm over an existing shim with an unusable trust path exit"
|
|
923
|
+
assert_absent "$home/state/tool-updates.check.sh" "a failed arm left a shim behind that no trust binding covers"
|
|
924
|
+
pass "a failed registration never leaves a shim without a matching trust binding"
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
test_a_failed_rearm_leaves_no_shim_the_trust_binding_lost() {
|
|
928
|
+
local home dir fake status
|
|
929
|
+
# The register removes an existing trust binding when its own post-write check
|
|
930
|
+
# fails, so an arm that fails there would leave a home that WAS armed holding a
|
|
931
|
+
# shim with no binding, which the watcher rejects on every cycle. The home must
|
|
932
|
+
# end plainly not armed instead.
|
|
933
|
+
home=$(make_home arm-rearm-fail)
|
|
934
|
+
dir="$TMP_ROOT/arm-rearm-fail/bin"
|
|
935
|
+
make_copy "$dir" "$TOOL" 'herdr 0.8.2'
|
|
936
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
937
|
+
FM_HOME="$home" "$CHECK" arm >/dev/null || fail "the first arm failed"
|
|
938
|
+
assert_present "$home/state/tool-updates.check-trust" "the first arm did not bind the shim"
|
|
939
|
+
|
|
940
|
+
# A hash tool that answers with nothing makes the register write a binding it
|
|
941
|
+
# then rejects, and it removes the old binding on the way out.
|
|
942
|
+
fake="$TMP_ROOT/arm-rearm-fail/fake-hash"
|
|
943
|
+
mkdir -p "$fake"
|
|
944
|
+
printf '#!/usr/bin/env bash\nexit 1\n' > "$fake/shasum"
|
|
945
|
+
printf '#!/usr/bin/env bash\nexit 1\n' > "$fake/sha256sum"
|
|
946
|
+
chmod 0755 "$fake/shasum" "$fake/sha256sum"
|
|
947
|
+
printf '#!/usr/bin/env bash\n# a shim armed earlier\nexit 0\n' > "$home/state/tool-updates.check.sh"
|
|
948
|
+
chmod 0700 "$home/state/tool-updates.check.sh"
|
|
949
|
+
|
|
950
|
+
status=0
|
|
951
|
+
env FM_HOME="$home" PATH="$(fixture_path "$fake")" "$CHECK" arm >/dev/null 2>&1 || status=$?
|
|
952
|
+
expect_code 1 "$status" "arm whose registration cannot hash exit"
|
|
953
|
+
assert_absent "$home/state/tool-updates.check.sh" "a failed re-arm left a shim behind after the trust binding was removed"
|
|
954
|
+
assert_absent "$home/state/tool-updates.check-trust" "the failed registration left a trust binding behind"
|
|
955
|
+
pass "a re-arm that loses the trust binding leaves no shim behind"
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
test_arm_resolves_a_relative_home_into_the_shim() {
|
|
959
|
+
local home stale fresh out status
|
|
960
|
+
# The watcher runs the shim from its own working directory, so a relative home
|
|
961
|
+
# has to be resolved before it is persisted. Otherwise the shim reads whatever
|
|
962
|
+
# sits under the watcher's directory, finds no registry, and stays silent for
|
|
963
|
+
# good.
|
|
964
|
+
home=$(make_home arm-relative)
|
|
965
|
+
stale="$TMP_ROOT/arm-relative/mise/bin"
|
|
966
|
+
fresh="$TMP_ROOT/arm-relative/local/bin"
|
|
967
|
+
make_copy "$stale" "$TOOL" 'herdr 0.8.0'
|
|
968
|
+
make_copy "$fresh" "$TOOL" 'herdr 0.8.2'
|
|
969
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
970
|
+
|
|
971
|
+
status=0
|
|
972
|
+
(cd "$TMP_ROOT" && FM_HOME=arm-relative "$CHECK" arm >/dev/null 2>&1) || status=$?
|
|
973
|
+
expect_code 0 "$status" "arm with a relative home exit"
|
|
974
|
+
|
|
975
|
+
out="$home/out.txt"
|
|
976
|
+
status=0
|
|
977
|
+
(cd / && env -u FM_HOME PATH="$(fixture_path "$stale:$fresh")" FM_CHECK_TIMEOUT=30 FM_TOOL_UPDATE_INTERVAL=0 \
|
|
978
|
+
"$home/state/tool-updates.check.sh" >"$out" 2>&1) || status=$?
|
|
979
|
+
expect_code 0 "$status" "shim run from another directory exit"
|
|
980
|
+
assert_contains "$(cat "$out")" "herdr update not in effect" "the shim read a different home than the one it was armed for"
|
|
981
|
+
pass "a relative home is resolved before it is persisted into the shim"
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
test_armed_check_wakes_the_watcher_with_the_skew_report() {
|
|
985
|
+
local home stale fresh out err status
|
|
986
|
+
# End to end through the real watcher: the armed check must reach it as a
|
|
987
|
+
# `check:` wake carrying the same PATH skew line, with no new machinery.
|
|
988
|
+
home=$(make_home wake)
|
|
989
|
+
stale="$TMP_ROOT/wake/mise/installs/herdr/latest/bin"
|
|
990
|
+
fresh="$TMP_ROOT/wake/local/bin"
|
|
991
|
+
make_copy "$stale" "$TOOL" 'herdr 0.8.0'
|
|
992
|
+
make_copy "$fresh" "$TOOL" 'herdr 0.8.2'
|
|
993
|
+
write_config "$home" "{\"tools\":[{\"name\":\"herdr\",\"command\":\"$TOOL\"}]}"
|
|
994
|
+
FM_HOME="$home" "$CHECK" arm >/dev/null || fail "could not arm the watched tool check"
|
|
995
|
+
|
|
996
|
+
out="$home/out.txt"
|
|
997
|
+
err="$home/err.txt"
|
|
998
|
+
status=0
|
|
999
|
+
env FM_HOME="$home" PATH="$(fixture_path "$stale:$fresh")" FM_CHECK_TIMEOUT=30 FM_TOOL_UPDATE_INTERVAL=0 \
|
|
1000
|
+
FM_POLL=1 FM_SIGNAL_GRACE=1 FM_CHECK_INTERVAL=1 \
|
|
1001
|
+
"$CHECKPOINT" --seconds 10 >"$out" 2>"$err" || status=$?
|
|
1002
|
+
expect_code 0 "$status" "watcher checkpoint exit"
|
|
1003
|
+
assert_contains "$(cat "$out")" "check:" "the armed check did not reach the watcher as a check wake"
|
|
1004
|
+
assert_contains "$(cat "$out")" "tool updates: herdr update not in effect" "the wake did not carry the PATH skew report"
|
|
1005
|
+
pass "the armed check reaches the watcher as an ordinary check wake"
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
test_path_skew_is_reported_from_every_copy
|
|
1009
|
+
test_newest_copy_first_on_path_is_silent
|
|
1010
|
+
test_identical_versions_are_silent
|
|
1011
|
+
test_one_copy_reached_twice_is_probed_once
|
|
1012
|
+
test_unreadable_version_is_a_failure_not_a_pass
|
|
1013
|
+
test_missing_command_is_reported
|
|
1014
|
+
test_announced_update_is_reported_from_the_tool_itself
|
|
1015
|
+
test_announcement_is_read_from_a_second_command
|
|
1016
|
+
test_unusable_announce_pattern_is_reported_not_read_as_silence
|
|
1017
|
+
test_one_broken_pattern_does_not_blind_the_rest_of_the_sweep
|
|
1018
|
+
test_an_unchecked_announcement_source_is_not_read_as_current
|
|
1019
|
+
test_an_announcement_probe_that_does_not_answer_is_reported
|
|
1020
|
+
test_quiet_tool_with_announce_pattern_is_silent
|
|
1021
|
+
test_commits_behind_origin_are_reported
|
|
1022
|
+
test_default_branch_is_detected_when_branch_is_omitted
|
|
1023
|
+
test_default_branch_is_asked_of_the_remote_when_the_clone_has_no_record
|
|
1024
|
+
test_current_and_ahead_repositories_are_silent
|
|
1025
|
+
test_unusable_git_source_is_reported
|
|
1026
|
+
test_unreadable_remote_is_not_reported_as_a_missing_branch
|
|
1027
|
+
test_missing_branch_on_a_readable_remote_is_still_reported
|
|
1028
|
+
test_git_probes_stop_when_the_sweep_budget_is_gone
|
|
1029
|
+
test_a_git_probe_that_does_not_answer_is_not_an_update
|
|
1030
|
+
test_a_stalled_repository_probe_is_not_reported_as_not_a_repository
|
|
1031
|
+
test_absent_registry_is_silent
|
|
1032
|
+
test_malformed_registry_is_reported_not_ignored
|
|
1033
|
+
test_findings_are_reported_once_until_they_change
|
|
1034
|
+
test_an_overlong_report_says_it_was_cut
|
|
1035
|
+
test_a_finding_past_the_cut_is_still_reported
|
|
1036
|
+
test_probes_are_skipped_between_intervals
|
|
1037
|
+
test_an_oversized_budget_is_cut_to_fit_and_reported
|
|
1038
|
+
test_invalid_environment_and_action_refuse
|
|
1039
|
+
test_arm_registers_the_check_and_disarm_removes_it
|
|
1040
|
+
test_arm_refuses_a_symlink_at_the_shim_path
|
|
1041
|
+
test_a_failed_registration_leaves_no_unregistered_shim
|
|
1042
|
+
test_a_failed_rearm_leaves_no_shim_the_trust_binding_lost
|
|
1043
|
+
test_arm_resolves_a_relative_home_into_the_shim
|
|
1044
|
+
test_armed_check_wakes_the_watcher_with_the_skew_report
|