Interlock projection supervisor #40
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
| # Supervisor controller for the operatorstack/interlock public projection. | |
| # | |
| # The publish pipeline is otherwise open-loop: interlock-publish.yml fire-and-forgets | |
| # a sync dispatch and the public repo self-heals on a 6-hourly cron. If any link | |
| # breaks (token, dispatch, stale-guard, un-merged sync PR, red downstream verify) the | |
| # public repo silently goes stale and nothing here ever fails. This workflow closes | |
| # the loop by asserting the invariant `operatorstack/interlock == project(main)`: | |
| # | |
| # verify (Boundary B) — on push to main touching the lab: poll until the public | |
| # repo carries this projection, so a broken sync surfaces within minutes. | |
| # drift (Boundary C) — scheduled/manual: assert parity + control-plane sync in a | |
| # single shot; the deterministic backstop that also catches out-of-band drift. | |
| # | |
| # Both fail CI and open/refresh a `projection-drift` issue when the public repo lags. | |
| # This is NOT a generated file (it is hand-maintained like interlock-lab.yml). | |
| name: Interlock projection supervisor | |
| on: | |
| push: | |
| branches: [main] | |
| paths: ["labs/21-interlock/**"] | |
| schedule: | |
| # ~30 min after the public repo's "50 */6" self-sync cron, off the :00 mark. | |
| - cron: "37 */6 * * *" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| issues: write | |
| concurrency: | |
| group: interlock-projection-supervisor-${{ github.event_name }} | |
| cancel-in-progress: false | |
| jobs: | |
| verify: | |
| name: Post-merge projection converged | |
| if: github.repository == 'operatorstack/intelligence-flow' && github.event_name == 'push' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 # `labkit doctor` derives the source commit via `git log -- <lab>` | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| - uses: astral-sh/setup-uv@v7 | |
| - name: Wait for the public projection to converge, then assert parity | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| for i in $(seq 1 30); do # ~15 min budget at 30s spacing | |
| rm -rf "$RUNNER_TEMP/pub" | |
| if git clone --depth 1 https://github.com/operatorstack/interlock "$RUNNER_TEMP/pub" 2>/dev/null \ | |
| && uv run --project labkit python -m labkit doctor \ | |
| --repo-root . --project interlock --repo "$RUNNER_TEMP/pub"; then | |
| echo "public repo == project(main)" | |
| exit 0 | |
| fi | |
| echo "not converged yet (attempt $i/30); waiting for the sync PR to land..." | |
| sleep 30 | |
| done | |
| echo "::error::operatorstack/interlock did not converge to the current projection within the timeout" | |
| exit 1 | |
| - name: Open or refresh the drift issue | |
| if: failure() | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: bash .github/scripts/interlock_drift_issue.sh "post-merge verify (${{ github.sha }})" | |
| - name: Resolve any open drift issue | |
| if: success() | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: bash .github/scripts/interlock_drift_issue.sh --resolve | |
| drift: | |
| name: Public projection in sync | |
| if: github.repository == 'operatorstack/intelligence-flow' && github.event_name != 'push' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| - uses: astral-sh/setup-uv@v7 | |
| - name: Assert public == project(main) and control planes in sync | |
| env: | |
| GH_TOKEN: ${{ github.token }} # --remote reads public workflow content, not admin settings | |
| run: | | |
| git clone --depth 1 https://github.com/operatorstack/interlock "$RUNNER_TEMP/pub" | |
| uv run --project labkit python -m labkit doctor \ | |
| --repo-root . --project interlock --repo "$RUNNER_TEMP/pub" --remote | |
| - name: Open or refresh the drift issue | |
| if: failure() | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: bash .github/scripts/interlock_drift_issue.sh "scheduled drift monitor" | |
| - name: Resolve any open drift issue | |
| if: success() | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: bash .github/scripts/interlock_drift_issue.sh --resolve |