Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,41 @@ All notable changes to Cairn are recorded here. Format follows
Per the [documentation policy](docs/governance/documentation-policy.md), every meaningful change
updates this file.

## [Unreleased] — Milestone M4: BYOM recovery library (in-repo, 0.x — ships nothing outward)

Cairn's recovery **mechanism**, repackaged as a **bring-your-own-model (BYOM) library**: a clean,
documented public API an outside developer drops into *their own* agent with *their own* model and
keys. No new recovery science — it extracts and exposes the validated RGR core. **No tag, no PyPI
publish, no release, no announcement**: the v1.0 hold stands and this milestone ships nothing outward
(hence `[Unreleased]`, not a version bump). C1 is **not** confirmed by M4; the library lets a user
reproduce the evidence on their own model (see the [claims registry](docs/research/claims-registry.md)).

### Added
- **Recovery primitives** (`cairn.checkpoint`, `cairn.recover`) — the full Re-grounding Recovery
protocol as public functions over `World` / `CheckpointStore` / `EffectLedger` seams; bring your
own loop. Plus `regrounded_history` (the re-grounding helper) and `Regrounded`/`Resolution`/
`ResumePlan` result types.
- **Contracts (Protocols):** `World` (`snapshot`/`restore`/`digest`; executable Worlds also
`execute`), `CheckpointStore`, `EffectLedger`. `LocalRuntime` satisfies all three, so the M1–M3
benchmark/eval paths run unchanged (regression guard).
- **Opt-in `Agent` loop** (`cairn.Agent`, `cairn.AgentRun`) — batteries-included `run()`/`resume()`
on the primitives; proven world-agnostic by an in-memory `ExecFakeWorld`.
- **Reference implementations:** `Workspace` (executable filesystem `World`), `FileCheckpointStore`,
`FileEffectLedger`.
- **Developer materials:** `examples/byom_recovery.py` (offline, deterministic — primitives + Agent +
exactly-once effect), `docs/guide/recovery-in-your-agent.md`, `docs/guide/public-api-reference.md`,
and an opt-in local-model (Ollama) path (never run in CI).
- **CI:** minimal GitHub Actions workflow running the test suite (incl. the example smoke test) on
Python 3.10/3.12 — tests only, no publish steps.
- **Public-API contract test** (`tests/test_public_api_contract.py`) locking the exact `cairn.__all__`
surface and guarding `__version__` at 0.x.

### Notes (as-built)
- `recover()` takes **no `goal`** (carried in the checkpoint); the Agent outcome type is `AgentRun`
(not the brainstorm's `RunResult`); `CodeHarness.resume`/`observe_world` were **not** force-migrated
onto `recover()` (the harness interleaves Task-specific env setup — churning the validated benchmark
was rejected). Full reconciliation: design doc §12.

## [0.2.0] — 2026-06-23 — Milestone M2: recovery-faithful live benchmark

First release to **actually exercise recovery against a real model**. M2 fixed the M1 root cause (a
Expand Down
466 changes: 466 additions & 0 deletions docs/superpowers/plans/2026-07-02-byom-contract-and-polish-ap5.md

Large diffs are not rendered by default.

58 changes: 58 additions & 0 deletions docs/superpowers/specs/2026-06-29-byom-recovery-library-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,61 @@ A new milestone **M4 — BYOM recovery library**, on branch `milestone-4-byom-li
None blocking. Public names (`Checkpoint` vs `ContinuationState`, `Workspace` vs current class name) are
finalized against the code during AP-1, preferring the existing names unless a clearer public name aids
readability.

## 12. Implementation reconciliation (as-built, 2026-07-02)

M4 shipped across five slices (AP-1…AP-5) using the superpowers spec→plan→execute workflow, all
merged to `master` (AP-1–AP-3 via PR #10 `93e03e5`; AP-4 via PR #11 `0e2ad54`; AP-5 via this PR).
This section records where the delivered code deliberately diverged from §5/§9 above. The sections
1–11 are preserved as the original approved brainstorm; §12 is authoritative for the as-built API.

### 12.1 Final public surface (locked by `tests/test_public_api_contract.py`)

`cairn.__all__` (20 names): `Action, StepRecord, Checkpoint, Regrounded, Resolution, ResumePlan,
AgentRun` (types); `World, CheckpointStore, EffectLedger` (Protocols); `EffectfulTool,
EscalationRequired` (effect safety); `Workspace, FileCheckpointStore, FileEffectLedger` (reference
implementations); `checkpoint, recover, regrounded_history` (primitives); `Agent` (opt-in loop);
plus `__version__`.

### 12.2 Deviations from §5 (documented, not silent)

- **`recover()` takes no `goal`.** As-built: `recover(world, store, ledger, *, effect_tools=None,
escalate=True) -> Regrounded`. The goal is carried inside the loaded checkpoint
(`state.durable_core.intent`), so passing it again would be redundant and could contradict the
cairn. (§5/§6 showed `recover(goal, ...)`.)
- **`RunResult` → `AgentRun`.** The opt-in loop's outcome type shipped as `AgentRun` (with
`finished, steps, checkpoints, history, final_state, resumed, recovery_tax, resolutions`).
- **`Observation` and `Model` are not exported.** `Observation` is internal to `harness.observe`.
The model seam is the existing `ModelProvider` (duck-typed `propose(goal, history) -> Action`);
it is documented as "bring any object with `propose`", not re-exported as a `Model` name.
- **`MockModel` → `ScriptableMockModel`** in `cairn.model_mock` — an example/test helper, not a
top-level export (kept out of the public contract deliberately; examples import it directly).
- **`Agent` constructor:** `Agent(model, world, *, store, ledger, effect_tools=None, max_steps=20,
escalate=True, model_version="byom", harness_version="")`. `ledger` is required and explicit;
effect tools are `effect_tools` (not `tools`); there is no hidden default store/ledger wiring.
- **Additional exports finalized:** `regrounded_history` (the re-grounding helper), `Resolution`,
`ResumePlan`, `EscalationRequired`.

### 12.3 Scope deviation — the benchmark harness was not force-migrated (AP-3)

`recover()` is the public RGR primitive, but `CodeHarness.resume` (the benchmark's validated path)
was **not** re-pointed onto it, and `observe_world` was **retained**. Reason: `CodeHarness`
interleaves `_setup_task_env(task, cwd)` between world-restore and re-observe — a *Task* concern the
task-agnostic `recover()` primitive must not reorder. Forcing the migration risked the green
benchmark (the regression guard) for no user-facing gain. The primitives and the `Agent` loop are
proven world-agnostic independently (in-memory `FakeWorld`/`ExecFakeWorld` tests); the harness stays
as the regression guard. Honest-scope (ADR-0009): reuse where proven, do not churn validated paths.

### 12.4 §9 milestone decomposition — as-run

§9 estimated "~4–5 Action Points"; M4 ran **five slices**: AP-1 contracts split; AP-2 recovery
primitives (`checkpoint`/`recover` + fake-World proof); AP-3 opt-in `Agent` loop + de-dup;
AP-4 example + guide + API reference + CI smoke; AP-5 public-API contract lock + this reconciliation
+ trackers/CHANGELOG. The detailed per-slice record lives in `docs/superpowers/plans/`.

### 12.5 Governance (unchanged)

Still **0.x**; **no PyPI publish, no release, no announcement** — M4 ships nothing outward. The v1.0
hold and the outward-action-needs-approval rule remain in force; the surface lock guards the version
at 0.x. C1 is **not** confirmed by M4 — the library lets a user reproduce the evidence on their own
model (see `docs/research/claims-registry.md`).
58 changes: 58 additions & 0 deletions project/phases/milestone-4-byom-library/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Milestone M4 — BYOM Recovery Library

> **Status: 🟢 Complete (merged; in-repo, 0.x — ships nothing outward).**
> Branch(es): `milestone-4-byom-library` (AP-1–AP-3), `milestone-4-ap4-example-docs` (AP-4),
> `milestone-4-ap5-contract-polish` (AP-5). Master only via PR (#10, #11, + AP-5's PR).
> Entered 2026-06-29. Design: [`docs/superpowers/specs/2026-06-29-byom-recovery-library-design.md`](../../../docs/superpowers/specs/2026-06-29-byom-recovery-library-design.md).

## Why this milestone

M1–M3 established that Cairn's v1.0 gate is a *powered live study* the maintainer can't yet run on a
paid API (M3: strongest live signal yet, still **NO-GO**; the free-tier confounds are measurement
artifacts, not the mechanism failing). Rather than block on buying API access, M4 **heads Cairn
toward its actual strength** — the recovery *mechanism* — and ships it as a **bring-your-own-model
(BYOM) recovery library**: a clean, documented public API an outside developer drops into *their own*
agent, with *their own* model and keys. C1 validation then becomes something a **user** can reproduce
on their own model; Cairn ships the mechanism, not a dependency on us proving it on a paid model.

This does **not** lift the v1.0 hold and ships **nothing outward** (no PyPI, no release, no
announcement). It is a 0.x, in-repo milestone. Honest scope (ADR-0009): M4 does not claim C1 is
confirmed.

## Slices (superpowers spec→plan→execute; not the legacy AP-00XX numbering)

| Slice | Title | Status | Record |
|---|---|---|---|
| AP-1 | Split the contracts (`World`/`CheckpointStore`/`EffectLedger` Protocols; `LocalRuntime`/`Workspace` satisfy them) | 🟢 Done (PR #10) | design §4; suite green |
| AP-2 | Recovery primitives — public `checkpoint()`/`recover()` over the protocols + fake-World abstraction proof | 🟢 Done (PR #10) | `src/cairn/recovery.py`; `tests/test_recovery_primitives.py` |
| AP-3 | Opt-in `Agent` loop on the primitives + de-dup (`regrounded_history` single source) | 🟢 Done (PR #10) | `src/cairn/agent.py`; [plan](../../../docs/superpowers/plans/2026-06-29-byom-agent-loop-ap3.md) |
| AP-4 | Example (primitives + Agent + exactly-once effect) + guide + API reference + CI smoke | 🟢 Done (PR #11) | `examples/byom_recovery.py`; `docs/guide/`; [plan](../../../docs/superpowers/plans/2026-07-02-byom-example-and-docs-ap4.md) |
| AP-5 | Public-API contract lock + spec reconciliation + trackers/CHANGELOG | 🟢 Done (this PR) | `tests/test_public_api_contract.py`; design §12; [plan](../../../docs/superpowers/plans/2026-07-02-byom-contract-and-polish-ap5.md) |

## What shipped

- **Contracts (Layer 1):** `World` (`snapshot`/`restore`/`digest`; executable Worlds also `execute`),
bundled `CheckpointStore` + `EffectLedger`, `EffectfulTool`. `LocalRuntime` satisfies all three so
the **benchmark, eval baselines, and prior tests run unchanged** (the regression guard).
- **Primitives (Layer 2):** `checkpoint(...)` and `recover(...)` — the full RGR as public functions;
proven world-agnostic by an in-memory `FakeWorld`.
- **Opt-in loop (Layer 3):** `Agent(model, world, *, store, ledger, ...)` with `.run()`/`.resume()`;
proven world-agnostic by an in-memory `ExecFakeWorld`.
- **Reference world + infra:** `Workspace`, `FileCheckpointStore`, `FileEffectLedger`.
- **Developer surface:** runnable offline example, the "recovery in your own agent" guide, the public
API reference, and a minimal CI (pytest 3.10/3.12) that runs the example smoke test.
- **The public surface is locked** by `tests/test_public_api_contract.py` and the version guarded at 0.x.

## As-built deviations (honest record)

Recorded in design §12: `recover()` takes **no `goal`** (carried in the checkpoint); the outcome type
is **`AgentRun`** (not `RunResult`); `Observation`/`Model`/`MockModel` are not top-level exports
(`ScriptableMockModel` is an example helper); and `CodeHarness.resume`/`observe_world` were **not**
force-migrated onto `recover()` because the harness interleaves a Task-specific env setup between
restore and observe — churning the validated benchmark for no user-facing gain was rejected.

## Governance

0.x; no publish/release/announcement; ADR-0007 (no hardcoded harness) and ADR-0009 (honest scope)
honored; branch-per-phase (master only via PR). The v1.0 hold and outward-action-needs-approval rule
remain in force.
20 changes: 19 additions & 1 deletion project/tracking/ap-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
> 2026-06-16: **AP-0043…AP-0047 `Done` (M2 complete,
> shipped as v0.2.0)** — non-batchable chain + work-unit metrics + repetition/statistics + live run; live C1
> **suggestive, not confirmed** at n=2; **v1.0 go/no-go take 2 → NO-GO** (stays 0.x). 2026-06-23.
> Last updated: 2026-06-23.
> **Milestone M3** (AP-0048 … AP-0051) complete & merged 2026-06-29: **NO-GO** take 3 (stays 0.x).
> **Milestone M4** (BYOM recovery library) complete & merged 2026-07-02 as **five superpowers slices
> (AP-1 … AP-5)**, in-repo 0.x — ships nothing outward; the recovery mechanism as a bring-your-own-model
> library; C1 **not** confirmed (user-reproducible).
> Last updated: 2026-07-02.

**Status values:** Proposed · Accepted · In Progress · In Review · Done · Blocked · Superseded

Expand Down Expand Up @@ -111,3 +115,17 @@
| [AP-0049](../phases/milestone-3-powered-live-study/action-points/AP-0049-multi-provider-transports.md) | Multi-provider OpenAI-compatible transports (Groq, ZenMux) | Done |
| [AP-0050](../phases/milestone-3-powered-live-study/action-points/AP-0050-powered-live-study.md) | Powered live study + claims update | Done *(gpt-oss-120b: 28 cells; RGR ≈ halves tax; strict verdict NOT SHOWN)* |
| [AP-0051](../phases/milestone-3-powered-live-study/action-points/AP-0051-v1-go-no-go-take3.md) | v1.0 go/no-go, take 3 | Done *(NO-GO; stays 0.x; AP-0036/0037 stay Blocked)* |

## Milestone M4 — BYOM Recovery Library *(merged 2026-07-02; superpowers slice workflow)*

> M4 used the superpowers spec→plan→execute workflow, so its units are **slices AP-1…AP-5** (not
> the legacy `AP-00XX` files). Detailed record: [`docs/superpowers/`](../../docs/superpowers/) and
> [`project/phases/milestone-4-byom-library/README.md`](../phases/milestone-4-byom-library/README.md).

| Slice | Title | Status |
|---|---|---|
| AP-1 | Split contracts (`World`/`CheckpointStore`/`EffectLedger` Protocols; `LocalRuntime` satisfies) | Done *(PR #10)* |
| AP-2 | Recovery primitives (`checkpoint`/`recover`) + fake-World abstraction proof | Done *(PR #10)* |
| AP-3 | Opt-in `Agent` loop on the primitives + `regrounded_history` de-dup | Done *(PR #10)* |
| AP-4 | BYOM example + guide + public API reference + CI smoke test | Done *(PR #11)* |
| AP-5 | Public-API contract lock + design-spec reconciliation (§12) + trackers/CHANGELOG | Done *(this PR)* |
16 changes: 15 additions & 1 deletion project/tracking/phase-tracking.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Phase Tracking

> Live phase status board. Canonical goals/criteria: [ROADMAP.md](../../ROADMAP.md).
> Last updated: 2026-06-29.
> Last updated: 2026-07-02.

| Phase | Name | Status | APs (done / total) |
|---|---|---|---|
Expand All @@ -15,9 +15,23 @@
| M1 | Live-LLM Validation | 🟢 Complete & merged (outcome: **NO-GO**; stays 0.x) | 5 / 5 |
| M2 | Recovery-faithful live benchmark | 🟢 Complete (shipped v0.2.0; **NO-GO** for v1.0, stays 0.x) | 5 / 5 |
| M3 | Powered live study (v1.0 gate) | 🟢 Complete (**NO-GO** take 3; strongest live signal yet; stays 0.x) | 4 / 4 |
| M4 | BYOM Recovery Library | 🟢 Complete (merged; in-repo 0.x — ships nothing outward) | 5 / 5 |

**Legend:** ⬜ Not started · 🟡 In Progress · 🟢 Complete · 🔴 Blocked

## Current milestone: M4 — BYOM Recovery Library (entered 2026-06-29)

Pivot to Cairn's strength — the recovery **mechanism** — packaged as a **bring-your-own-model**
library an outside dev drops into their own agent with their own model/keys. Five slices, all merged
(AP-1–AP-3 via PR #10 `93e03e5`; AP-4 via PR #11 `0e2ad54`; AP-5 via this PR): **contracts split**
(`World`/`CheckpointStore`/`EffectLedger` Protocols; `LocalRuntime` satisfies all three so the
benchmark stays green), **public primitives** (`checkpoint`/`recover`, fake-World proof), **opt-in
`Agent` loop** (+ `regrounded_history` de-dup), **example + guide + API reference + CI smoke**, and
**public-API contract lock + spec reconciliation + trackers**. Ships **nothing outward** — 0.x, v1.0
hold intact; C1 **not** confirmed (the library lets a user reproduce it on their own model). As-built
deviations recorded in design §12 (notably `recover()` has no `goal`; harness not force-migrated).
See [`project/phases/milestone-4-byom-library/README.md`](../phases/milestone-4-byom-library/README.md).

## Current milestone: M3 — Powered live study (entered 2026-06-29)

Entered on branch `milestone-3-powered-live-study` (master clean; M2 merged via PR #8, commit `1027a39`,
Expand Down
50 changes: 50 additions & 0 deletions tests/test_public_api_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""Public API contract — the lock on the top-level `cairn` surface (M4 AP-5).

If you add/remove/rename a public name, this test fails on purpose: update `EXPECTED`
deliberately (a public-surface change is a decision, not an accident) and document it in
CHANGELOG.md + docs/guide/public-api-reference.md in the same change.
"""

import cairn

# The exact, intended public surface as of Milestone M4 (BYOM recovery library).
EXPECTED = {
"__version__",
# types
"Action", "StepRecord", "Checkpoint", "Regrounded", "Resolution", "ResumePlan", "AgentRun",
# contracts (Protocols)
"World", "CheckpointStore", "EffectLedger",
# effect safety
"EffectfulTool", "EscalationRequired",
# reference implementations
"Workspace", "FileCheckpointStore", "FileEffectLedger",
# primitives
"checkpoint", "recover", "regrounded_history",
# opt-in loop
"Agent",
}


def test_all_matches_the_intended_surface_exactly():
actual = set(cairn.__all__)
missing = EXPECTED - actual
extra = actual - EXPECTED
assert not missing, f"public names declared but missing from __all__: {sorted(missing)}"
assert not extra, f"public names in __all__ not in the intended contract: {sorted(extra)}"


def test_all_has_no_duplicates():
assert len(cairn.__all__) == len(set(cairn.__all__))


def test_every_public_name_is_importable():
for name in cairn.__all__:
assert hasattr(cairn, name), f"cairn.{name} is in __all__ but not importable"


def test_version_is_still_0x():
# Guards against an accidental/unapproved v1.0 bump — the v1.0 release is HELD
# (needs explicit approval + powered live evidence; the project stays 0.x).
assert cairn.__version__.startswith("0."), (
f"__version__ is {cairn.__version__!r}; v1.0+ requires explicit approval + live evidence"
)
Loading