Scheduler: fail closed on an unreadable run ledger, narrow the first-run fan-out #238
Workflow file for this run
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
| # The CANON repo's own CI — Claudinite's test suite + its own conformance sweep | |
| # on every PR. Not consumer wiring (consumers ship no workflow, #385). | |
| name: CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| defaults: | |
| run: | |
| shell: bash | |
| jobs: | |
| test-and-conformance: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| # Check out the PR's real head, not the synthetic refs/pull/N/merge commit | |
| # actions/checkout uses by default — otherwise HEAD is an ephemeral merge of | |
| # the branch into main, which the work-scoped squash-merge-history check would | |
| # (correctly, for that synthetic commit) flag. This makes CI evaluate exactly | |
| # what the local Stop hook does: the branch tip. Falls back to github.sha on push. | |
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| - name: Unit tests | |
| # THE test roots — a closed set of FOLDERS, each swept to any depth. Depth is | |
| # deliberately not enumerated: the previous form spelled out one glob per level | |
| # (`packs-tests/*/skills/*/*.test.mjs` and so on), so a test at an unlisted depth | |
| # was silently never run — which is exactly how vendoring/'s two files sat outside | |
| # CI. `**` removes depth from the question; what remains is the root list, and | |
| # engine-tests/ci-test-roots.test.mjs fails the moment a tracked test lands | |
| # outside it. Adding a root is a deliberate CI edit; adding a test is not. | |
| # engine-tests/ engine/ + the other top-level canon code (migrations/, …); | |
| # mirrors engine/'s inner structure, and stays canon-side | |
| # because engine/ ships to consumers exactly as-is (#385) | |
| # packs-tests/ every canon pack's tests, mirroring packs/ | |
| # vendoring/ co-located with the vendor computer/writer they cover | |
| # .claudinite/local/packs/ this repo's own local packs — tests live inside the tree | |
| # nullglob: a root with no tests drops out instead of reaching node as a literal pattern. | |
| run: | | |
| shopt -s globstar nullglob | |
| tests=(engine-tests/**/*.test.mjs packs-tests/**/*.test.mjs vendoring/**/*.test.mjs .claudinite/local/packs/**/*.test.mjs) | |
| node --test "${tests[@]}" | |
| - name: Conformance sweep | |
| # The world sweep's home is the test/CI flow (not the Stop hook, which | |
| # carries only the work scope) — this canon repo models the pattern | |
| # bootstrap wires into consumers. World only: the work scope judges a | |
| # session's change and runs at that session's Stop hook, not here. | |
| run: node engine/checks/check_the_world.mjs | |
| - name: Promote write-surface gate | |
| # The growth promote stage (.claudinite/local/packs/canon-curation/tasks/growth-promote/task.md) | |
| # is the one change bounded to packs/ + skills/ — an ordinary engine PR legitimately edits | |
| # checks/ and engine/, so this cannot run on every PR. It keys on the promote branch prefix | |
| # (the task doc pins it) and fails the promote PR if a changed path strays. | |
| if: contains(github.head_ref, 'growth-promote') | |
| run: node .claudinite/local/packs/canon-curation/promote-scope.mjs |