feat(autonomy): add host merge revocation protocol #1661
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [master] | |
| pull_request: | |
| branches: ["**"] | |
| workflow_call: | |
| permissions: | |
| contents: read | |
| # Feature-branch updates are validated through the PR merge ref, not an | |
| # equivalent push checkout. Superseded PR revisions can stop safely; default | |
| # branch pushes and reusable workflow callers retain independent full matrices. | |
| concurrency: | |
| group: ci-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| ci: | |
| # Node 22 is the hard minimum — install.sh and CONTRIBUTING.md both enforce it. | |
| # Ubuntu is the exhaustive authority, partitioned by Vitest's deterministic | |
| # file sharding so the full suite has margin below the hermetic hard cap. | |
| # Windows runs named portability contracts over fleet durability, | |
| # verification, merge authority, handoff, telemetry, and watchdog surfaces. | |
| name: CI (Node 22, ${{ matrix.label }}) | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| label: ubuntu, authority 1/3 | |
| test_args: "--shard=1/3" | |
| - os: ubuntu-latest | |
| label: ubuntu, authority 2/3 | |
| test_args: "--shard=2/3" | |
| - os: ubuntu-latest | |
| label: ubuntu, authority 3/3 | |
| test_args: "--shard=3/3" | |
| - os: windows-latest | |
| label: windows, portability 1/3 | |
| test_args: >- | |
| test/setup/home.test.ts test/classify.test.ts | |
| test/m2.doctor.test.ts test/m3.tools-registry.test.ts | |
| test/m43.verify-commands.test.ts | |
| test/m113.coordinator-wire.test.ts test/m373.directory-durability.test.ts | |
| test/m395.effect-terminal-retention.test.ts | |
| test/m403.automerge-mutation-fence.test.ts | |
| test/m404.policy-result-surfaces.test.ts | |
| test/m428.goal-source-quality.test.ts | |
| test/m409.engine-execution-mutation-fence.test.ts | |
| test/m410.policy-opposing-race.test.ts | |
| test/m414.local-store-lock-unknown-owner.test.ts | |
| test/m415.policy-durability-races.test.ts | |
| test/m422.policy-transaction-recovery.test.ts | |
| test/m425.policy-startup-recovery.test.ts | |
| test/m416.local-store-lock-handoff.test.ts | |
| - os: windows-latest | |
| label: windows, portability 2/3 | |
| test_args: >- | |
| test/m21.worktree.test.ts test/m23.apply.test.ts | |
| test/m100.web-open.test.ts test/m119.quality-metrics.test.ts | |
| test/m332.outcome-watcher.test.ts | |
| test/m405.apply-mutation-fence.test.ts | |
| test/m406.daemon-stop-quiescence.test.ts | |
| test/m411.local-merge-reconciliation.test.ts | |
| test/m412.sandbox-pre-effect-recovery.test.ts | |
| test/m413.engineer-run-mutation-fence.test.ts | |
| test/m417.sandbox-cleanup-quiescence.test.ts | |
| test/m424.legacy-swarm-mutation-fence.test.ts | |
| test/m425.persistence-private-temp.test.ts | |
| test/m426.sandbox-reservation-identity.test.ts | |
| test/sandbox-reservation-recovery.test.ts | |
| - os: windows-latest | |
| label: windows, portability 3/3 | |
| test_args: >- | |
| --reporter=dot | |
| test/m315.remote-handoff-truth.test.ts | |
| test/m372.test-ci-watchdog.test.ts | |
| test/m423.control-plane-lock-order.test.ts | |
| test/m455.external-skill-maturity.test.ts | |
| test/m456.skill-retrieval-calibration.test.ts | |
| test/m457.external-skill-artifact-firewall.test.ts | |
| test/m460.policy-assignment-receipts.test.ts | |
| test/m463.claimed-batch-admission.test.ts | |
| - os: windows-latest | |
| label: windows, portability overflow | |
| test_args: >- | |
| --reporter=dot | |
| test/m220.anticlog-verdict-feedback.test.ts | |
| test/m286.worktree-verify-env.test.ts | |
| test/m367.daemon-observer-scheduler.test.ts | |
| test/m379.private-storage.test.ts test/m385.cutoff-checkpoint-windows.test.ts | |
| test/m385.cutoff-checkpoint-scheduler.test.ts | |
| test/m407.verification-mutation-fence.test.ts | |
| test/m408.sandbox-creation-mutation-fence.test.ts | |
| test/m418.pulse-quiescence.test.ts | |
| test/m419.remote-handoff-intent.test.ts | |
| test/m420.remote-handoff-recovery.test.ts | |
| test/m421.legacy-pulse-quiescence.test.ts | |
| - os: macos-latest | |
| label: macos, shared queue authority | |
| test_args: >- | |
| test/m111.work-queue.test.ts test/m392.queue-lease-epochs.test.ts | |
| test/m395.effect-terminal-retention.test.ts | |
| test/m367.daemon-observer-scheduler.test.ts | |
| test/m455.external-skill-maturity.test.ts | |
| test/m456.skill-retrieval-calibration.test.ts | |
| test/m457.external-skill-artifact-firewall.test.ts | |
| test/m460.policy-assignment-receipts.test.ts | |
| test/m463.claimed-batch-admission.test.ts | |
| runs-on: ${{ matrix.os }} | |
| env: | |
| ASHLR_VITEST_TEST_TIMEOUT_MS: ${{ matrix.os == 'windows-latest' && '30000' || '5000' }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set up Node.js 22 | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22" | |
| cache: "npm" | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Typecheck | |
| run: npm run typecheck | |
| - name: Lint | |
| run: npm run lint | |
| - name: Build | |
| run: npm run build | |
| - name: Test (hermetic) | |
| # scripts/test-ci.mjs isolates HOME from real ~/.ashlr/ state and adds | |
| # inactivity and hard-runtime watchdogs. Windows portability partitions run | |
| # serially in independent checkouts so they cannot share mutable state. | |
| # This is the canonical CI invocation documented in CONTRIBUTING.md. | |
| run: npm run test:ci -- ${{ matrix.test_args }} | |
| - name: Test native alias authority (hermetic) | |
| if: matrix.os == 'macos-latest' || matrix.label == 'windows, portability 2/3' | |
| run: npm run test:ci -- test/m426.sandbox-reservation-identity.test.ts test/h7.rollback.test.ts | |
| - name: Test native path and lifecycle authority (hermetic) | |
| if: matrix.label == 'windows, portability 1/3' | |
| run: >- | |
| node scripts/test-native-path-lifecycle.mjs -- | |
| test/h1.fixture.test.ts test/h4.sandbox-enrollment-kill.test.ts | |
| test/m165.self-heal.test.ts test/m201.daemon-loop.test.ts | |
| test/m22.backlog.test.ts test/m229.goal-engine-trio.test.ts | |
| test/m310.queued-autonomy-work.test.ts | |
| test/m342.dispatch-production-ledger.test.ts | |
| test/m362.repair-handoff-journal.test.ts | |
| test/m360.generated-repair-lifecycle.test.ts | |
| test/m353.dispatch-manifest.test.ts test/m84.goal-direct.test.ts | |
| -t "relocates homedir\(\) to the fresh tmp HOME while active|cleanup\(\) restores the prior HOME, USERPROFILE, and ASHLR_HOME exactly|3.8 secures a fresh Windows authority root before enrollment|3.9 refuses without rewriting a pre-existing permissive Windows authority root|3.10 refuses a permissive pre-existing Windows fence directory|retires an uncommitted intent so a cross-day retry can acquire authority|materializes exact failure receipts for treatment-free capture and proposal repair lineage|recovers a failure append crash without duplicating its authoritative raw event|recovers one exact failure append beyond the analytics partition read bound|fails closed when a crashed failure append partition suffers replacement|fails closed when a crashed failure append partition suffers truncation|fails closed when a crashed failure append partition suffers mutation|bounds and crash-recovers 2,048 same-generation failures with batched assurance|recovers an interrupted retention manifest for an exact failure-receipt artifact|recovers an interrupted retention manifest for an exact failure-intent artifact|establishes exact private DACLs for attempt authority writes|exact-inspects receipt directory DACLs during pure authority reads|rejects owner-safe but non-exact Windows ACLs before parsing treatment authority|establishes exact private DACLs for treatment receipt, retention, and protocol writes|consumes protocol v5 emitted by the real dispatch writer|publishes an ordinal-2 retained proof at the writer retention cutoff|accepts an exact-inspected v2 receipt tombstone after the live receipt is retained|keeps a proven converted witness pending until exact immutable publication|retries converted publication after receipt storage is repaired|treats exact immutable publication replay as idempotent|establishes exact private DACLs for lifecycle treatment receipt and existing retention storage|journal activation authority: canonicalizes UUID casing before persistence and lookup|journal activation authority: quarantines UUID case-variant timestamp collisions|journal activation authority: reports the latest same-generation recurrence|journal activation authority: rejects recurrence authority when its immutable anchor parent is missing|journal activation authority: preserves unbound v2 rows written before activation|journal activation authority: quarantines unbound v2 rows written after activation|journal activation authority: reconciles a file-durable append crash before parent settlement|preserves the journal-wide high-water through compaction and rejects a stale writer generation|rejects a distinct writer id claiming the high-water activation instant|rejects an activation id that mutates its timestamp|degrades summary when an active-epoch recurrence loses its parent|tracks the active writer epoch without moving the immutable generation anchor|keeps the first exact route tuple as the immutable generation anchor|does not let a backdated recurrence replace the first durable anchor|quarantines a malformed claimed id across compaction and later replay|projects the immutable first v2 attempt while compaction preserves recurrence history|keeps compacted history able to quarantine a changed old replay|quarantines an exact parent with a conflicting backend/tier sibling|reports the true latest authority timestamp across current-activation generations|preserves generation proof across an intentional writer activation rollover|accepts a current physical alias only for an exactly canonical enrolled path|proves an old complete sequence from a physical attempt receipt without scanning global partitions|A0a: retargeted legacy enrollment aliases are not counted, scanned, or dispatched|A0b: a temporarily missing exact canonical enrollment degrades the tick|keeps the healthy full snapshot when an exact canonical enrollment is temporarily missing|fails closed on a legacy lexical enrollment row after its alias is retargeted|correlates a canonical proposal for a symlink-bound goal|correlates a canonical proposal for a symlink project caller|correlates a canonical swarm proposal for a lexical project caller|persists physical proposal repo identity and rejects legacy alias authority|does not derive or journal repair authority from an invalid raw repo identity|persists physical repo identity and rejects legacy lexical or linked aliases|idempotently rejects relative and secret-shaped raw repo identities without fallback rows|idempotently rejects relative and secret-shaped repo identities without unknown or cwd rows|persists physical assignment repo identity and rejects legacy alias authority" | |
| # M33: keep the published exports map honest — pack the tarball, install | |
| # it into a clean dir, and exercise the bin + the ./types entry point. | |
| # M83: run once on the first Ubuntu authority shard. The tarball-exports | |
| # check is OS-independent, and the bash heredoc is not worth porting to Windows. | |
| - name: Pack smoke (exports map) | |
| if: matrix.label == 'ubuntu, authority 1/3' | |
| shell: bash | |
| run: | | |
| npm pack | |
| SMOKE_DIR=$(mktemp -d) | |
| TARBALL="$PWD"/ashlr-hub-*.tgz | |
| cd "$SMOKE_DIR" | |
| npm init -y > /dev/null | |
| npm install $TARBALL > /dev/null | |
| ./node_modules/.bin/ashlr help > /dev/null | |
| node -e "import('@ashlr/hub/types').then(() => console.log('exports map ok'))" | |
| node -e "import('@ashlr/hub/core').then((m) => { if (typeof m.loadConfig !== 'function') throw new Error('core surface broken'); console.log('core surface ok'); })" |