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:
parent
34baf60b35
commit
89387fc0ff
3 changed files with 227 additions and 0 deletions
103
.github/workflows/contemplation.yml
vendored
Normal file
103
.github/workflows/contemplation.yml
vendored
Normal 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/
|
||||
2
contemplation/runs/.gitkeep
Normal file
2
contemplation/runs/.gitkeep
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# CI-authored contemplation reports land here via ADR-0155.
|
||||
# Operator review of each PR is the ratification gate.
|
||||
122
docs/decisions/ADR-0155-ci-contemplation-runner.md
Normal file
122
docs/decisions/ADR-0155-ci-contemplation-runner.md
Normal 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.
|
||||
Loading…
Reference in a new issue