From 89387fc0ff4dbfb6b281344c10dbbef75fbd24b9 Mon Sep 17 00:00:00 2001 From: Shay Date: Mon, 25 May 2026 18:47:06 -0700 Subject: [PATCH] feat(W-021): CI contemplation runner with HITL PR gate (ADR-0155) (#279) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a scheduled GitHub Actions workflow that runs `core demo learning-arc --json`, writes the report to contemplation/runs/.json, and opens a PR against main. Operator review on the PR is the ratification gate — preserves the HITL invariant from ADR-0150/0152. Workflow stays disabled until repo variable CONTEMPLATION_ENABLED is set to "true" (soft kill switch in repo settings). Default cadence is nightly; ADR includes a budget table for the 3000 Linux minutes/month available on GitHub Pro. CI never: - commits to main directly - mutates corpora/ or packs/ - ratifies proposals - registers recognizers CI only writes a report file under contemplation/runs/ and proposes the diff via PR. Determinism check (first-run verification): local + CI runs at same SHA must byte-match on proposal_id / trace_hash. Out of scope (noted in ADR): persisted engine_state across CI runs, auto-merge, cross-runner determinism, recognizer growth from CI synthetic traffic. To enable: 1. Repo Settings → Variables → CONTEMPLATION_ENABLED=true 2. Actions → contemplation → Run workflow 3. Review the resulting PR before merging --- .github/workflows/contemplation.yml | 103 +++++++++++++++ contemplation/runs/.gitkeep | 2 + .../ADR-0155-ci-contemplation-runner.md | 122 ++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 .github/workflows/contemplation.yml create mode 100644 contemplation/runs/.gitkeep create mode 100644 docs/decisions/ADR-0155-ci-contemplation-runner.md diff --git a/.github/workflows/contemplation.yml b/.github/workflows/contemplation.yml new file mode 100644 index 00000000..e9c73f22 --- /dev/null +++ b/.github/workflows/contemplation.yml @@ -0,0 +1,103 @@ +name: contemplation + +# ADR-0155 — CI-driven contemplation runner. +# +# Runs `core demo learning-arc --json` on a schedule, writes the +# report to contemplation/runs/, and opens a PR against main for +# operator review. Never commits to main directly. Soft-killed +# by repo variable CONTEMPLATION_ENABLED (set to "true" to enable). + +on: + schedule: + # 09:00 UTC = 01:00 PST. Nightly. Adjust cadence per ADR-0155 + # budget table; do not exceed 3000 Linux minutes/month on Pro. + - cron: '0 9 * * *' + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +concurrency: + group: contemplation + cancel-in-progress: false + +jobs: + contemplate: + name: engine-authored proposal cycle + runs-on: ubuntu-latest + timeout-minutes: 20 + # Soft kill switch — set repo var CONTEMPLATION_ENABLED=true to enable. + # Empty / unset = workflow exits without consuming meaningful minutes. + if: ${{ vars.CONTEMPLATION_ENABLED == 'true' }} + + steps: + - name: checkout + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: set up uv + uses: astral-sh/setup-uv@v5 + with: + python-version: '3.11' + enable-cache: true + + - name: install dependencies + run: uv pip install -e ".[dev]" pyyaml + + - name: run contemplation cycle + id: run + env: + PYTHONPATH: ${{ github.workspace }} + run: | + set -euo pipefail + mkdir -p contemplation/runs + STAMP=$(date -u +%Y-%m-%dT%H%M%SZ) + OUTPUT="contemplation/runs/${STAMP}.json" + uv run core demo learning-arc --json > "${OUTPUT}" + echo "output=${OUTPUT}" >> "${GITHUB_OUTPUT}" + echo "stamp=${STAMP}" >> "${GITHUB_OUTPUT}" + echo "## Contemplation report ${STAMP}" >> "${GITHUB_STEP_SUMMARY}" + # Operator-facing summary — top-level keys only, full report in PR. + uv run python -c " + import json, sys + r = json.load(open('${OUTPUT}')) + for k, v in r.items(): + print(f'- **{k}**: {type(v).__name__}') + " >> "${GITHUB_STEP_SUMMARY}" + + - name: open PR for operator review + uses: peter-evans/create-pull-request@v7 + with: + commit-message: | + chore(contemplation): CI run ${{ steps.run.outputs.stamp }} + + Engine-authored contemplation cycle. Operator review + required before any corpus mutation. ADR-0155. + branch: contemplation/${{ steps.run.outputs.stamp }} + base: main + title: 'contemplation: CI run ${{ steps.run.outputs.stamp }}' + body: | + Automated contemplation cycle per ADR-0155. + + **Report:** `${{ steps.run.outputs.output }}` + + **Trust boundary:** this PR proposes only the report + file under `contemplation/runs/`. No corpus, pack, or + engine_state mutation. Operator review is the + ratification gate. + + Merge to accept (becomes part of the audit trail). + Close to reject. + + Determinism check: this report's `proposal_id` and + `trace_hash` fields must byte-match a local + `core demo learning-arc --json` at the same commit SHA. + If they diverge, the workflow runner-class invariant + has broken — investigate before merging. + labels: | + contemplation + ci-authored + add-paths: | + contemplation/runs/ diff --git a/contemplation/runs/.gitkeep b/contemplation/runs/.gitkeep new file mode 100644 index 00000000..e632f166 --- /dev/null +++ b/contemplation/runs/.gitkeep @@ -0,0 +1,2 @@ +# CI-authored contemplation reports land here via ADR-0155. +# Operator review of each PR is the ratification gate. diff --git a/docs/decisions/ADR-0155-ci-contemplation-runner.md b/docs/decisions/ADR-0155-ci-contemplation-runner.md new file mode 100644 index 00000000..48c07de5 --- /dev/null +++ b/docs/decisions/ADR-0155-ci-contemplation-runner.md @@ -0,0 +1,122 @@ +# ADR-0155 — CI contemplation runner (W-021) + +Status: scoping +Date: 2026-05-25 + +## Context + +ADR-0150 (W-018) made contemplation autonomous at checkpoint. +ADR-0151 (W-017) auto-proposes from enriched candidates at load. +ADR-0152 (W-019) closes the engine-authored proposal loop. +Operator (Shay) currently runs sessions from a single workstation +with intermittent connectivity. + +A GitHub Actions runner is deterministic Linux compute that the +operator can trigger from anywhere. Running contemplation cycles on +that compute amortizes wall-clock contemplation cost without +sacrificing CORE's HITL doctrine — provided the output is gated +through pull-request review before any corpus mutation. + +Budget on GitHub Pro (Student): 3,000 Actions minutes/month on +Linux runners (1× multiplier). A 10-min contemplation run every 4 +hours costs ~1,800 min/mo (60% of budget); nightly costs ~900 min. + +## Decision + +Add `.github/workflows/contemplation.yml`: + +- Triggers: `schedule:` (nightly at 09:00 UTC = 01:00 PST) and + `workflow_dispatch:` (manual). +- Soft kill switch: skips when repo variable `CONTEMPLATION_ENABLED` + is not `"true"`. Operator toggles in repo settings without + editing the workflow. +- Runs `core demo learning-arc --json`, writes the report to + `contemplation/runs/YYYY-MM-DD-HHMMSS.json`. +- Opens a PR against `main` with the new run via + `peter-evans/create-pull-request@v7`. Operator review on the PR + is the ratification gate. +- Concurrency group prevents overlapping runs. + +The CI runner **never** commits directly to `main`, **never** +mutates `corpora/`, **never** registers recognizers, **never** +ratifies proposals. It only writes a report file under +`contemplation/runs/` and proposes the diff via PR. + +## Invariants preserved + +- ADR-0150 HITL gate: every proposal still passes through operator + review. CI just stages the candidate. +- Determinism (CLAUDE.md): `ubuntu-latest` is consistent enough + for trace_hash equality. First-run verification: compare a local + `core demo learning-arc --json` against the CI output for the + same commit SHA; they must byte-match on the + `proposal_id` / `trace_hash` fields. If they diverge, the + underlying determinism gap is a substrate bug to fix, not a + reason to relax the invariant. +- No new trust boundary on disk: CI writes only to + `contemplation/runs/` (a new directory dedicated to CI output); + existing trust boundaries are unchanged. +- Acceptable Use Policy: output is a project artifact (proposals + about CORE's corpus), so contemplation runs are defensibly + "production, testing, deployment, or publication of the software + project" per GitHub AUP §5. Idle / unbounded compute is not + scheduled. + +## Trust boundary + +The CI workflow has `contents: write` and `pull-requests: write` +on a branch named `contemplation/`. It cannot push to `main` +(protected branch). The HITL surface is the PR review UI — +identical to existing operator workflow for human-authored +proposals. + +## Out of scope + +- **Persisted engine state across CI runs.** Each run starts from + the committed corpus and produces a one-shot report. A future + ADR may track engine-state evolution across runs by committing + `engine_state/` under a CI-only branch, but only after operator + review on each step. +- **Auto-merge.** Never. Every CI proposal stays open until the + operator merges or closes. +- **Cross-runner determinism.** Pinning to `ubuntu-latest` is + acceptable; switching runner classes invalidates the + trace_hash equality check. +- **Recognizer growth.** ADR-0154 enables the registry to grow + from live traffic, but CI runs do not produce traffic the + registry should learn from (they are synthetic exercises). The + CI runtime sets the producer queue but does not persist + derived recognizers to the committed engine_state. + +## Validation + +- First run (manual `workflow_dispatch`) produces a PR with a + `contemplation/runs/.json` file. +- Local + CI runs at the same SHA produce byte-identical + `proposal_id` and `trace_hash` fields (manual check on first + enable). +- Workflow exits 0 with no PR created when no proposal is + produced (idempotent runs). +- Soft kill: setting `vars.CONTEMPLATION_ENABLED=""` causes the + job to skip on the next scheduled tick. + +## Operator runbook + +1. Repo Settings → Secrets and variables → Actions → Variables: + set `CONTEMPLATION_ENABLED=true`. +2. Actions tab → "contemplation" workflow → "Run workflow" once + to verify. +3. Watch the resulting PR; review the proposal as you would any + `core teaching proposals` entry. +4. Merge to accept (proposal becomes part of the audit trail) or + close to reject. +5. Disable: set `CONTEMPLATION_ENABLED=""` (empty); the workflow + exits early without consuming meaningful minutes. + +## Closure + +After this ADR, CORE has a remote compute path for contemplation +that preserves the operator-as-gate invariant. The operator gains +asynchronous contemplation cycles tied to the project's own audit +trail, with no new infrastructure to maintain beyond a single +workflow file.