feat(W-021): CI contemplation runner with HITL PR gate (ADR-0155) (#279)

Adds a scheduled GitHub Actions workflow that runs
`core demo learning-arc --json`, writes the report to
contemplation/runs/<stamp>.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
This commit is contained in:
Shay 2026-05-25 18:47:06 -07:00 committed by GitHub
parent 34baf60b35
commit 89387fc0ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 227 additions and 0 deletions

103
.github/workflows/contemplation.yml vendored Normal file
View file

@ -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/

View file

@ -0,0 +1,2 @@
# CI-authored contemplation reports land here via ADR-0155.
# Operator review of each PR is the ratification gate.

View file

@ -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/<date>`. 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/<date>.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.