# Retained obligation: an executed teaching fixture

A reservation retry must return the original reservation before 24 elapsed
hours. One configuration change cuts key retention to 12 hours. The code,
requirement and expected test results stay unchanged. Replaying at 12 and 23
hours creates a second reservation. Restoring 24-hour retention repairs these
observed cases.

This is a deliberately small teaching repository, not a customer result.
It uses Python's standard library, an in-memory SQLite database and an injected
logical clock. It does not wait 24 wall-clock hours. The author supplied the
requirement and the explicit configuration-to-behavior mapping. No agent
recovered or approved this intent.

## Reproduce without Proof

Requires Python 3 and Git. From this directory:

```sh
python3 run.py --output /tmp/retained-replay
```

Choose a new, empty output directory. The runner creates a disposable local Git
repository with no remote, executes all three snapshots, writes logs and a Git
bundle, then removes the temporary checkout. It creates no commit in your
calling repository and makes no network request. A failed B run is expected;
the runner itself succeeds only when the observed exits are A=0, B=1, C=0.
It also checks that only `config.json` differs in the declared behavioral
inputs and that A and C have the same behavioral basis.

The fixed Git author/committer dates are **synthetic fixture metadata** for
reproducibility. Actual execution timestamps are in each JSON result. A and C
have the same source tree but different commits because C records the repair
after B.

## Reproduce with Proof

```sh
python3 run.py --output /tmp/retained-proof-replay --proof /absolute/path/to/proof
```

The captured run used Proof source revision
`b845ff313bacb887c4e9fb78145c681a15f6b3f0`, built with Go 1.26.3.
From an existing Proof source checkout that contains this revision, create a
separate detached checkout before building. The fixed build identity below
labels that pinned source, not whichever revision you currently have open:

```sh
replay_source=$(mktemp -d /tmp/proof-retained-source.XXXXXX)
git worktree add --detach "$replay_source" b845ff313bacb887c4e9fb78145c681a15f6b3f0
(
  cd "$replay_source" || exit 1
  test "$(git rev-parse HEAD)" = b845ff313bacb887c4e9fb78145c681a15f6b3f0 || exit 1
  go build -ldflags '-X main.buildCommit=b845ff313bacb887c4e9fb78145c681a15f6b3f0 -X main.buildVersion=0.1.0-teaching-b845ff313bac' -o /tmp/proof-retained-obligation ./cmd/proof
)
```

Start these commands at the Proof source root, not this example directory.
If that source revision is unavailable, stop and obtain the matching source;
do not apply its fixed version label to another checkout. The detached
checkout remains at the printed worktree path for inspection. When testing
a newer version, record its own source revision and build identity instead.
The exact executable SHA-256 and reported version are in `results/summary.json`.
Newer versions may produce different package shapes or findings. No prebuilt
binary is bundled.

In each temporary snapshot, the runner first executes:

```sh
python3 verify.py --config config.json --output .evidence/verification.json --junit .evidence/tests.xml
```

It then removes those output files and invokes Proof, which must produce new
ones through the configured command:

```sh
proof audit --check tests_pass --no-cache --format json
```

`proof.yaml` explicitly sets `project.checks.tests_pass.affected.mode: full`.
This matters: `--no-cache` bypasses cached check results; it does not force a
nonempty affected-test plan. An initial attempt with default affected planning
on a clean committed snapshot reported `no affected tests to run`. The
published fixture uses the explicit full-command setting and asserts that
Proof's exit matches the actual behavioral outcome. Only `tests_pass` runs,
not the full set of Proof checks.

For each persisted run, the script invokes `proof evidence package assemble`
with the actual run ID and fixture Git revision. It writes a real package in
`results/A/package.stdout.json`, and equivalently for B and C. Assembly does
not rerun tests. These are local commit-scoped packages. The explicit
`https://example.invalid/teaching/retained-obligation` repository label is a
reserved teaching identifier required by the package schema, not a real
remote or hosted repository. The package's provider field is a resolver
default, not evidence of a GitHub execution.

## Inspect the captured results

| Snapshot | Retention | Cases passed | Cases failed | Direct / Proof audit exit | Package readiness |
| --- | ---: | ---: | ---: | --- | --- |
| A | 24h | 5 | 0 | 0 / 0 | `not_assessed` |
| B | 12h | 3 | 2 | 1 / 1 | `blocked` |
| C | 24h | 5 | 0 | 0 / 0 | `not_assessed` |

The five cases are retries at 0, 1, 12 and 23 hours, plus the implementation's
expiry convention at exactly 24 hours. The final case is outside the promised
window. `cases.json` states expected counts and identity comparisons directly;
it does not derive expected behavior from `retention_hours`.

- `summary.json`: exact Git commits, tree IDs, commands, input/result hashes,
  tool identity and scope.
- `A-to-B.diff`, `B-to-C.diff`: actual Git diffs. Only the configuration changes.
- `A/verification.json`: direct execution, with inputs and observations.
- `A/audit-verification.json`, `A/tests.xml`: results produced when Proof invokes
  the configured test command, not copied from the earlier direct execution.
- `A/audit.jsonl`: actual selected-check output; the format is JSON Lines.
- `A/proof-run.json`: actual persisted Proof run.
- `A/package.stdout.json`: actual sealed EvidencePackage; its canonical digest
  is a package identity, not a claim that the software is correct.
- Corresponding B and C directories retain the failure and repair evidence.
- `fixture.bundle`: all three local commits and tags. Clone this bundle and
  use `git checkout A`, `B`, or `C` to inspect exactly what ran.

## What remains unestablished

Passing the five cases does not accept the change. The actual packages retain
that distinction: B is blocked by `tests_pass`; A and C remain `not_assessed`.
The fixture does not supply approved customer intent, a complete change-intent
record, or traced per-requirement execution evidence in the package.
The package's `evidenceRecords` array is empty. The detailed behavioral results
are companion JSON/JUnit artifacts; the package includes gate outcomes and,
for B, the structured failing test names. The manually understood config
dependency is not automatically mapped into the package's requirement scope.

The script also attempts a local `proof evidence github render` on A's package
with B's head supplied. The command rejects the commit-scoped package as
`not PR-scoped`. That refusal is retained. No hosted PR, remote fetch, published
check run, acceptance decision, MCP retrieval or second-agent experiment ran.
The basis comparison in `summary.json` is a small explicit-file comparison by
this teaching runner, not a demonstration of universal Proof freshness
detection.

Concurrency, multiple replicas, restarts, conflicting payloads and production
clock behavior remain untested. `requirement.json` records those limitations.
All results are observations under the recorded setup, not a certification or
proof of absence of every retry defect.
