Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b490914
Add Recovery Integrity signal
safal207 Aug 15, 2026
a8fb514
Add Recovery Integrity protocol overview
safal207 Aug 15, 2026
cd96ba5
Add RecoveryIntegrityRecord schema
safal207 Aug 15, 2026
f240fbf
Add Recovery Integrity semantic validator
safal207 Aug 15, 2026
31304d3
Add sanitized Codex recovery fixture
safal207 Aug 15, 2026
985707d
Add fail-closed continuation negative control
safal207 Aug 15, 2026
d3a5672
Harden split-generation recovery invariant
safal207 Aug 15, 2026
2a1c0e5
Add Generation-N crash simulator
safal207 Aug 15, 2026
6a8cef7
Add Generation-N crash regression tests
safal207 Aug 15, 2026
13928fd
Document Generation-N crash matrix
safal207 Aug 15, 2026
94b2077
Add Recovery Integrity CI proof lane
safal207 Aug 15, 2026
f8d13d4
Record executable Generation-N recovery matrix
safal207 Aug 15, 2026
82ff7b9
Route Recovery Integrity through signals index
safal207 Aug 15, 2026
eef3608
Pin expected Generation-N matrix
safal207 Aug 15, 2026
f8262f9
Document Recovery Integrity CI proof lane
safal207 Aug 15, 2026
ad5c8fe
Add real SQLite projection crash harness
safal207 Aug 15, 2026
7223c71
Add process-crash recovery regressions
safal207 Aug 15, 2026
b617604
Gate Recovery Integrity on real process-crash harness
safal207 Aug 15, 2026
06a1883
Document process-crash evidence boundary
safal207 Aug 15, 2026
d76eccd
Pin process-crash recovery matrix
safal207 Aug 15, 2026
cd18288
Pin process-crash matrix in protocol docs
safal207 Aug 15, 2026
361b93c
Record first process-crash evidence
safal207 Aug 15, 2026
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
60 changes: 60 additions & 0 deletions .github/workflows/recovery-integrity-v0.1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Recovery Integrity v0.1

on:
pull_request:
paths:
- "protocols/recovery-integrity-v0.1/**"
- "signals/018-recovery-integrity-projection-authority-continuation.md"
- ".github/workflows/recovery-integrity-v0.1.yml"
push:
branches: [main]
paths:
- "protocols/recovery-integrity-v0.1/**"
- "signals/018-recovery-integrity-projection-authority-continuation.md"
- ".github/workflows/recovery-integrity-v0.1.yml"

permissions:
contents: read

jobs:
recovery-integrity:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Validate sanitized public fixture
run: |
python protocols/recovery-integrity-v0.1/validate.py \
protocols/recovery-integrity-v0.1/fixtures/codex-26990-sanitized.json

- name: Verify unsafe fork fails closed
run: |
if python protocols/recovery-integrity-v0.1/validate.py \
protocols/recovery-integrity-v0.1/fixtures/unsafe-fork-must-fail.json; then
echo "unsafe fork fixture unexpectedly passed"
exit 1
else
echo "unsafe fork fixture rejected as expected"
fi

- name: Run Generation-N crash simulator
working-directory: protocols/recovery-integrity-v0.1
run: python generation_crash_simulator.py

- name: Run generation regression suite
working-directory: protocols/recovery-integrity-v0.1
run: python -m unittest -v test_generation_crash_simulator.py

- name: Run SQLite + atomic projection process-crash matrix
working-directory: protocols/recovery-integrity-v0.1
run: python fault_injection_harness.py matrix

- name: Run process-crash regression suite
working-directory: protocols/recovery-integrity-v0.1
run: python -m unittest -v test_fault_injection_harness.py
86 changes: 86 additions & 0 deletions protocols/recovery-integrity-v0.1/PROCESS_CRASH_EVIDENCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Recovery Integrity v0.1 — Process-Crash Evidence

This note records the first executable on-disk crash evidence for Recovery Integrity v0.1.

## Subject

A real local fixture composed of:

```text
SQLite authority store
+
atomic JSON projection
```

The authority store runs with SQLite WAL and `synchronous=FULL`.

The projection path is:

```text
projection.json.tmp
→ flush
→ fsync(temp)
→ os.replace(..., projection.json)
→ fsync(directory) where supported
```

A child process advances generation 1 → 2 and terminates with `os._exit(91)` at a selected boundary. A fresh verifier then inspects the actual post-crash files and SQLite state.

## Observed boundaries

```text
crash point auth proj projection rebuild temp
--------------------------------------------------------------------------------
before_authority_commit 1 1 HEALTHY NO_REBUILD false
after_authority_commit 2 1 STALE ALLOW_REBUILD false
after_projection_temp_fsync 2 1 STALE ALLOW_REBUILD true
after_projection_commit 2 2 HEALTHY NO_REBUILD false
```

All four observations are converted into `RecoveryIntegrityRecord` objects and pass the semantic validator while keeping `execution_continuation=HOLD`.

## What this proves

The harness mechanically distinguishes:

1. a transaction interrupted before authority commit from a committed generation;
2. a committed authority generation from a lagging projection;
3. an fsynced temporary projection candidate from the committed projection path;
4. restored generation alignment after the projection commit completes.

It also demonstrates the recovery rule:

```text
authority commit succeeded
+
projection commit did not
→ projection STALE
→ ALLOW_REBUILD
→ execution HOLD
```

## What this does not prove

This is process-crash evidence, not arbitrary physical power-loss proof.

```text
os._exit process crash
storage-controller power failure
filesystem guarantee under every platform
torn-sector simulation
```

The harness is intentionally bounded. A future storage/power-loss lane should introduce a VM, filesystem, or product-native failure injector with explicit durability semantics.

## Reproduce

```bash
cd protocols/recovery-integrity-v0.1
python fault_injection_harness.py matrix
python -m unittest -v test_fault_injection_harness.py
```

CI runs both commands as part of the Recovery Integrity acceptance contract.
219 changes: 219 additions & 0 deletions protocols/recovery-integrity-v0.1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# Recovery Integrity Protocol v0.1

Recovery Integrity v0.1 is a small, falsifiable contract for crash/restart recovery in agentic and stateful systems.

It separates:

```text
durable authority
derived projection
execution continuation
```

The protocol exists to prevent a common failure mode: several stores are each locally readable, but belong to different logical generations or imply different recovery decisions.

## Decisions

Projection recovery:

- `ALLOW_REBUILD`
- `NO_REBUILD`
- `HOLD`

Execution recovery:

- `ALLOW_FORK`
- `NO_CONTINUATION`
- `HOLD`

`ALLOW_REBUILD` never implies `ALLOW_FORK`.

## Minimum recovery states

Projection state:

- `HEALTHY`
- `MISSING`
- `STALE`
- `CORRUPT`
- `UNPROVABLE`

Integrity state:

- `VALID`
- `INVALID`
- `UNKNOWN`

Continuation proof:

- `PROVEN`
- `NOT_PROVEN`
- `CONTRADICTED`

## Load-bearing invariants

1. **Projection is not authority.** A cache, sidebar, index, or JSON projection must not silently redefine authoritative record existence.
2. **Readable is not current.** A parseable projection may still be stale.
3. **Missing, stale, and corrupt are distinct.**
4. **Evidence survives repair.** The disputed pre-recovery artifact must be preservable/addressable before mutation.
5. **Rebuild and continuation are separate decisions.**
6. **Safe continuation requires proof.** `ALLOW_FORK` requires `rollout.continuation_proof == PROVEN`.
7. **Ambiguous pending effects fail closed.** Unknown/ambiguous external side effects cannot produce `ALLOW_FORK`.
8. **Current authority dominates recovered authority.** When mutation authority is dynamic, recovery must revalidate it rather than resurrect it from a checkpoint.
9. **Observed outcome is recorded separately from the pre-recovery verdict.**
10. **Generation mismatch is explicit.** When both authority and projection generations are known and differ, the projection cannot be `HEALTHY`.
11. **Projection-newer-than-authority fails closed.** A projection at generation `N+1` with apparent durable authority at `N` is `UNPROVABLE`; recovery must not rebuild from the apparently older source until the contradiction is reconciled.
12. **Pre-commit crash must not invent a committed generation.** A transaction interrupted before durable authority commit remains at the prior generation.
13. **Post-commit projection lag is stale, not missing history.** Once authority generation `N+1` commits while projection remains at `N`, the projection is rebuildable but execution remains independently gated.
14. **Orphan temp state is evidence, not authority.** A fully fsynced temporary projection that was never renamed does not silently replace the last committed projection.

## Files

- `schema/recovery-integrity-record.schema.json` — structural JSON Schema.
- `validate.py` — semantic invariant validator with no third-party dependencies.
- `generation_crash_simulator.py` — deterministic Generation-N crash-state classifier and verdict simulator.
- `test_generation_crash_simulator.py` — regression tests for the generation matrix and fail-closed boundaries.
- `fault_injection_harness.py` — real SQLite + atomic JSON process-crash harness using child-process termination at durability boundaries.
- `test_fault_injection_harness.py` — regressions over the observed on-disk crash states.
- `fixtures/codex-26990-sanitized.json` — first public sanitized fixture based only on public GitHub evidence.
- `fixtures/unsafe-fork-must-fail.json` — negative continuation control.
- `fixtures/generation-matrix.expected.txt` — pinned canonical simulator output.
- `fixtures/process-crash-matrix.expected.txt` — pinned canonical on-disk process-crash matrix.

## Validate the public fixture

```bash
python protocols/recovery-integrity-v0.1/validate.py \
protocols/recovery-integrity-v0.1/fixtures/codex-26990-sanitized.json
```

Expected:

```text
PASS recovery-integrity-v0.1
projection=STALE
projection_decision=ALLOW_REBUILD
execution_decision=HOLD
```

## Generation-N crash simulator

The simulator makes generation drift falsifiable without depending on a vendor implementation.

Canonical matrix:

```text
case projection rebuild execution
healthy HEALTHY NO_REBUILD HOLD
stale STALE ALLOW_REBUILD HOLD
corrupt CORRUPT ALLOW_REBUILD HOLD
split-generation UNPROVABLE HOLD HOLD
```

Run:

```bash
cd protocols/recovery-integrity-v0.1
python generation_crash_simulator.py
python -m unittest -v test_generation_crash_simulator.py
```

The split-generation case is intentionally asymmetric:

```text
authority generation 41
projection generation 42
UNPROVABLE
HOLD
```

The verifier does **not** assume the projection is wrong and overwrite it from the apparently older authority. That contradiction must be reconciled first.

## Real process-crash fault injection

`fault_injection_harness.py` advances a real SQLite authority row and an atomically written JSON projection from generation 1 to generation 2. The mutation runs in a child process and calls `os._exit(91)` at selected boundaries. A fresh parent verifier then inspects the actual files and SQLite state left on disk.

The SQLite lane uses WAL plus `synchronous=FULL`. The projection lane uses:

```text
write projection.json.tmp
flush + fsync(temp)
os.replace(temp, projection.json)
fsync(directory) where supported
```

Canonical observed matrix:

```text
crash point authority projection state rebuild
before authority commit 1 1 HEALTHY NO_REBUILD
after authority commit 2 1 STALE ALLOW_REBUILD
after projection temp fsync 2 1 STALE ALLOW_REBUILD
after full projection commit 2 2 HEALTHY NO_REBUILD
```

The temp-fsync case additionally requires the orphan `projection.json.tmp` candidate to remain observable while the committed projection stays at generation 1.

Run:

```bash
cd protocols/recovery-integrity-v0.1
python fault_injection_harness.py matrix
python -m unittest -v test_fault_injection_harness.py
```

Every observed state is converted into a `RecoveryIntegrityRecord` and passed through the same semantic validator. The harness performs classification only; it does not rebuild the projection or allow execution continuation.

### Evidence boundary

This is **process-crash fault injection**, not proof of arbitrary physical power-loss durability. `os._exit()` proves behavior across abrupt process termination with real SQLite/filesystem operations. It does not model drive write caches, controller reordering, filesystem-specific power-fail behavior, torn sectors, or all Windows directory-fsync semantics.

Therefore:

```text
process-crash PASS
power-loss durability proven
```

A stronger future lane needs VM/filesystem/storage fault injection or a product-native crash harness with explicit durability guarantees.

## Regression boundaries

The suites verify that:

- forcing `ALLOW_REBUILD` across a projection-newer-than-authority split is rejected;
- a valid projection rebuild does not grant `ALLOW_FORK`;
- unknown side effects and unproven current authority keep execution fail-closed;
- a pre-commit SQLite crash does not advance authority generation;
- a post-commit/pre-projection crash becomes `STALE`, not `HEALTHY`;
- an fsynced-but-unrenamed temp projection does not become the committed projection;
- a fully committed projection restores generation alignment.

## CI proof lane

`.github/workflows/recovery-integrity-v0.1.yml` runs the full boundary on relevant pull requests and `main` changes:

```text
sanitized public fixture must PASS
unsafe fork fixture must FAIL
Generation-N matrix must PASS
generation regressions must PASS
SQLite + atomic JSON process-crash matrix must PASS
process-crash regressions must PASS
```

This makes both negative controls and real crash-boundary observations part of the acceptance contract rather than optional manual checks.

## Non-goals

This contract does not decide which store is authoritative for a product. That is a product-specific declaration backed by native evidence.

It does not claim full power-loss safety, distributed consensus, hardware fault tolerance, or vendor adoption. The semantic validator, generation simulator, and recovery verifier are read-only; the fault-injection child mutates only its isolated test fixture and never a product recovery target.
Loading
Loading