Complete bounded recovery checks and reconcile blocked workplans

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0e738-9e15-7600-87e6-90dec3a7beef
This commit is contained in:
tegwick 2026-09-28 11:06:35 +02:00
parent aea9796908
commit 9baf6f1829
21 changed files with 2301 additions and 53 deletions

View file

@ -1,11 +1,29 @@
{
"schema_version": 1,
"assessed_at": "2026-09-27",
"assessed_at": "2026-09-28",
"assessor": "Codex",
"status": "provisional author-model classification",
"basis_revision": "9ac6f6925be6743113338ad140f8465b2d99a404",
"basis_revision": "aea9796",
"counting_rule": "Count a candidate once per assigned label; labels overlap. Responsibilities describe the direct mechanism, not every prerequisite or downstream effect. Forces are selected trade-offs, not exhaustive coverage or measured outcomes.",
"candidates": [
{
"path": "research/candidates/CapabilityContractBoundary.md",
"sha256": "a678f88e9b53d7273e29e3aea2d23f2297055253dcf4518444d3089fa6da4331",
"title": "Capability Contract Boundary",
"responsibilities": [
"composition"
],
"forces": {
"improves": [
"portability"
],
"worsens": [
"implementation-complexity",
"compatibility-pressure"
]
},
"rationale": "Separates a semantic capability interface from its runtime carrier to support host portability; versioning, enforcement and cross-host compatibility add work. Declaring the contract alone supplies neither runtime isolation nor independent release."
},
{
"path": "research/candidates/ConflictCopy.md",
"sha256": "24f250d89ac17d96df287d67c275b5320b48c8356b57ae126a6c941e92a1c6e9",

View file

@ -1,8 +1,8 @@
# Classification coverage
Generated by `python3 scripts/classification_coverage.py`; assessment: 2026-09-27.
Generated by `python3 scripts/classification_coverage.py`; assessment: 2026-09-28.
17 developed drafts; 16 original seeds at `2a5dabd`. 7 original seeds developed, 9 without dedicated drafts, 10 later additions. All current candidates remain hypotheses; classification confers no admission or new evidence.
18 developed drafts; 16 original seeds at `2a5dabd`. 7 original seeds developed, 9 without dedicated drafts, 11 later additions. All current candidates remain hypotheses; classification confers no admission or new evidence.
The original comparison above is the compact 16-entry table. The broader initial [patterns README](../../patterns/README.md) lists 59 seed names; it is a separate discovery inventory, not 59 developed records or a release quota.
@ -22,8 +22,8 @@ A zero means no assigned direct coverage, not absence of discussion. Force direc
| affordance | 2 | 6 |
| mutation | 0 | 4 |
| synchronization | 2 | 5 |
| composition | 2 | 4 |
| execution | 0 | 3 |
| composition | 2 | 5 |
| execution | 0 | 4 |
| quality | 2 | 3 |
## Scales
@ -34,7 +34,7 @@ A zero means no assigned direct coverage, not absence of discussion. Force direc
| L1 | 0 | 1 |
| L2 | 3 | 6 |
| L3 | 7 | 5 |
| L4 | 2 | 3 |
| L4 | 2 | 4 |
| L5 | 1 | 2 |
| L6 | 4 | 13 |
| L7 | 0 | 0 |
@ -53,7 +53,7 @@ These are direct-mechanism assignments, not family aliases. In particular, recon
| mutation | 4 |
| synchronization | 2 |
| reconciliation | 4 |
| composition | 3 |
| composition | 4 |
| execution | 3 |
## Forces
@ -92,14 +92,14 @@ Selected trade-offs only. Counts are unique candidates per force, even if a forc
| independent-deployability | 1 | 0 | 1 |
| team-autonomy | 1 | 0 | 1 |
| release-coupling | 0 | 0 | 0 |
| compatibility-pressure | 0 | 2 | 2 |
| implementation-complexity | 0 | 9 | 9 |
| compatibility-pressure | 0 | 3 | 3 |
| implementation-complexity | 0 | 10 | 10 |
| reconciliation-complexity | 0 | 2 | 2 |
| cognitive-load | 0 | 3 | 3 |
| testability | 1 | 0 | 1 |
| observability | 0 | 0 | 0 |
| debuggability | 0 | 0 | 0 |
| portability | 1 | 0 | 1 |
| portability | 2 | 0 | 2 |
| security-exposure | 0 | 0 | 0 |
| scalability | 0 | 0 | 0 |
| fault-containment | 1 | 0 | 1 |
@ -112,6 +112,7 @@ Selected trade-offs only. Counts are unique candidates per force, even if a forc
| Candidate | Responsibilities | Intended benefit | Cost / tension | Rationale |
| --- | --- | --- | --- | --- |
| [Capability Contract Boundary](../candidates/CapabilityContractBoundary.md) | composition | portability | implementation-complexity, compatibility-pressure | Separates a semantic capability interface from its runtime carrier to support host portability; versioning, enforcement and cross-host compatibility add work. Declaring the contract alone supplies neither runtime isolation nor independent release. |
| [Conflict Copy](../candidates/ConflictCopy.md) | reconciliation | recoverability, information-completeness | cognitive-load, reconciliation-complexity | Preserves divergent content for later semantic resolution; unresolved copies impose review and merge work. |
| [Derived State](../candidates/DerivedState.md) | projection | authoritative-consistency | client-compute-cost, cache-invalidation-complexity | Projects coherent inputs without separately mutable derived copies; recomputation and cached dependency tracking cost work. |
| [Explicit Reconciliation](../candidates/ExplicitReconciliation.md) | reconciliation, affordance | information-completeness, recoverability | cognitive-load, continuity-of-interaction | Makes semantic resolution an explicit comparison task; deciding among versions interrupts ordinary work. |

View file

@ -1,6 +1,6 @@
# Language evolution report
Generated by `make evolution-report`; curated assessment date: 2026-09-27.
Generated by `make evolution-report`; curated assessment date: 2026-09-28.
This is an evidence profile, not a completion percentage. Evidence presence does not establish full-contract recurrence or production fitness.
@ -14,7 +14,7 @@ The baseline profiles are author-model assessments by Codex; they do not confer
| --- | ---: |
| documentary | 18 |
| static-code | 7 |
| runtime | 8 |
| runtime | 10 |
Categories overlap. Runtime includes authored fixtures and negative controls. Family labels group related evidence; they are not counts of independent applications.
@ -51,13 +51,13 @@ Ten selected retrospective outcomes (not a historical census), followed by prosp
| Outcome | Entries (not independent discoveries) |
| --- | ---: |
| corroboration | 4 |
| corroboration | 5 |
| inconclusive | 0 |
| merge | 0 |
| rejection | 0 |
| revision | 13 |
Cycles with recorded research effort: 6/17. Unknown effort is neither zero nor estimated from commits, tokens or assertion counts.
Cycles with recorded research effort: 7/18. Unknown effort is neither zero nor estimated from commits, tokens or assertion counts.
### Stream B / B-independent-navigation-source-v1
@ -76,6 +76,14 @@ One bounded question counts at most one consequential outcome. Corroboration and
One bounded question counts at most one consequential outcome. Corroboration and inconclusive work remain visible separately. Fewer than three comparable cycles: no trend assessment.
### Stream D / D-sqlite-git-recovery-v1
| Cycle | Outcome | Research minutes | Consequential outcome/hour |
| --- | --- | ---: | ---: |
| LEARN-0018 | corroboration | 4.123 | 0.00 |
One bounded question counts at most one consequential outcome. Corroboration and inconclusive work remain visible separately. Fewer than three comparable cycles: no trend assessment.
### Stream F / F-navigation-focus-v1
| Cycle | Outcome | Research minutes | Consequential outcome/hour |
@ -145,8 +153,9 @@ Define when a local change is durably accepted.
| --- | --- | --- | --- |
| documentary | Ink & Switch | Local-first motivation. | [LocalFirstEvidenceExpansion](../../research/reviews/LocalFirstEvidenceExpansion.md) |
| documentary | Joplin | Local storage and synchronization architecture. | [LocalFirstEvidenceExpansion](../../research/reviews/LocalFirstEvidenceExpansion.md) |
| runtime | authored SQLite / Git recovery fixture | SQLite WAL/FULL: pre-commit process kill rolls back; post-commit kill preserves document and outbox. No power-loss/backup proof. | [StorageReceiverRecovery](../../research/reviews/StorageReceiverRecovery.md) |
Open limits: No executed storage crash or backup test. Local acceptance is not off-device recovery.
Open limits: Process-kill recovery observed in SQLite fixture; power-loss, device-loss backup and full frontend contract remain unverified. Local acceptance is not off-device recovery.
Superseded by: none recorded.
@ -159,8 +168,9 @@ Keep accepted local intent visible until synchronization resolves.
| Evidence | Family | Scope | Review |
| --- | --- | --- | --- |
| documentary | Joplin | Per-item synchronization metadata. | [LocalFirstEvidenceExpansion](../../research/reviews/LocalFirstEvidenceExpansion.md) |
| runtime | authored SQLite / Git recovery fixture | SQLite atomic supersession/restart plus Git identity replay after lost local acknowledgement; one authored integration, no frontend recurrence. | [StorageReceiverRecovery](../../research/reviews/StorageReceiverRecovery.md) |
Open limits: Atomic supersession and retry contract proposed, not storage-tested.
Open limits: Atomic supersession observed in SQLite fixture; concurrent in-flight supersession, UI states and general receiver retention remain unverified.
Superseded by: none recorded.
@ -207,6 +217,7 @@ Reject an operation whose observed base is stale.
| documentary | Kubernetes | Conditional PUT; precise version mechanism. | [TransactionalEvidenceExpansion](../../research/reviews/TransactionalEvidenceExpansion.md) |
| documentary | GitLab | Optional last_commit_id; different failure surface. | [TransactionalEvidenceExpansion](../../research/reviews/TransactionalEvidenceExpansion.md) |
| runtime | authored release fixture | Related conditional activation-map mechanism in one process, not the full frontend pattern. | [ActivationConflictsAndReload](../../research/reviews/ActivationConflictsAndReload.md) |
| runtime | authored SQLite / Git recovery fixture | Actual Git receive-pack rejects a forced divergent branch update; related ancestry mechanism, not exact-version frontend semantics. | [StorageReceiverRecovery](../../research/reviews/StorageReceiverRecovery.md) |
Open limits: Distributed atomicity and complete frontend recurrence remain unverified. Refetch does not authorize overwriting conflicting intent.
@ -225,6 +236,7 @@ Retain operation identity when transport outcome is uncertain.
| static-code | Zulip | Pinned send/recovery state. | [FrontendRecoveryImplementations](../../research/reviews/FrontendRecoveryImplementations.md) |
| static-code | Element / Matrix | Pinned client and SDK transaction/echo paths. | [FrontendRecoveryImplementations](../../research/reviews/FrontendRecoveryImplementations.md) |
| runtime | Element / Matrix | Real SDK with synthetic transport: lost acknowledgement, identity reuse and authoritative echo. | [MatrixRecoveryExperiment](../../research/reviews/MatrixRecoveryExperiment.md) |
| runtime | authored SQLite / Git recovery fixture | Actual Git same-commit replay is a no-op, reachable identity survives GC, fresh identity repeats intent; no arbitrary operation ledger. | [StorageReceiverRecovery](../../research/reviews/StorageReceiverRecovery.md) |
Open limits: No production server deduplication or complete retention-window guarantee. Cancelling local UI is not remote compensation.
@ -645,3 +657,17 @@ Evidence: [ZulipTargetLookupCallPath](../../research/reviews/ZulipTargetLookupCa
Remaining uncertainty: Static source trace only; no application, database or upstream test executed, and no race actually reproduced. show() performs its own state reset on entry, so a stale re-entry's externally observable effect is unverified without execution. Second cycle in comparison group B-independent-navigation-source-v1; still short of three comparable cycles, and the workplan's rule against manufacturing cycles to reach that count still applies.
Supersedes: none recorded.
### LEARN-0018 — Which process-recovery obligations can SQLite plus actual Git receive-pack satisfy, and where does object identity fail to supply an operation receipt?
2026-09-28 · stream D · prospective · corroboration
Before: Atomic local intent/outbox updates and stable receiver identity support recovery within explicit transaction, ancestry and retention boundaries.
Conclusion: Nine checks corroborate atomic SQLite process recovery, Git replay and server ancestry refusal; split-transaction and fresh-identity negative controls retain the limits. One authored integration, not a full frontend use.
Evidence: [StorageReceiverRecovery](../../research/reviews/StorageReceiverRecovery.md), [result](../../research/experiments/storage-receiver/result.json), [2026-09-28-storage-receiver-effort](../../research/evolution/cycles/2026-09-28-storage-receiver-effort.json).
Remaining uncertainty: Power-loss, device-loss backup, remote durability and arbitrary operation-ID retention remain unverified. No full frontend contract, independent human validation or M1/M2/M3 acceptance. New runtime group is not comparable with static studies; no learning trend.
Supersedes: none recorded.

View file

@ -0,0 +1,11 @@
{
"start_utc": "2026-09-28T08:55:43+00:00",
"end_utc": "2026-09-28T08:59:51.273196+00:00",
"research_minutes": 4.123,
"execution_minutes": 0.014,
"reported_tokens": null,
"cost_usd": null,
"status": "complete",
"method": "Continuous single-agent active interval from recorded preregistration clock through implementation and evidence review, minus measured experiment subprocess duration. Includes protocol commit; excludes earlier orientation and subsequent ledger/check/sync administration. No overlap.",
"budget": "30 active research minutes, 5 experiment execution minutes; within both."
}

View file

@ -1,6 +1,6 @@
# LEARN-0018 — Durable pending intent and a concrete receiver
Status: preregistered before execution. Owner/reviewer: Codex, author-model.
Status: complete; preregistered before execution at `aea9796`. Owner/reviewer: Codex, author-model.
Date: 2026-09-28. Existing work: FEP-WP-0003 T04; no new work item.
Stream D; new comparison group D-sqlite-git-recovery-v1. Do not pool this
runtime protocol with source inspection or the Matrix synthetic receiver.
@ -48,4 +48,13 @@ existing T01/T02 and WP-0002 T03. No new follow-up task is needed for exclusions
## Outcome
Pending execution.
Corroboration: nine checks pass, including two negative controls. Evidence:
[review](../../reviews/StorageReceiverRecovery.md),
[result](../../experiments/storage-receiver/result.json), and
[effort receipt](2026-09-28-storage-receiver-effort.json).
One question/outcome; nine assertions are not nine independent uses. No failed
development run, independent review, new inference call or maturity promotion.
T04 completes for this bounded pair; full frontend recurrence remains T01,
validated/human cases T02 and WP-0002 T03. Supersedes no prior ledger entry.
Active research and execution times are recorded separately in the effort receipt;
provider tokens/cost are unmeasured. Review: Codex, author-model, not independent.

View file

@ -596,6 +596,46 @@
"cost_usd": null
},
"supersedes": []
},
{
"id": "LEARN-0018",
"date": "2026-09-28",
"recording": "prospective",
"stream": "D",
"comparison_group": "D-sqlite-git-recovery-v1",
"protocol_ref": "research/evolution/cycles/2026-09-28-storage-receiver.md",
"question": "Which process-recovery obligations can SQLite plus actual Git receive-pack satisfy, and where does object identity fail to supply an operation receipt?",
"previous_belief": "Atomic local intent/outbox updates and stable receiver identity support recovery within explicit transaction, ancestry and retention boundaries.",
"conclusion": "Nine checks corroborate atomic SQLite process recovery, Git replay and server ancestry refusal; split-transaction and fresh-identity negative controls retain the limits. One authored integration, not a full frontend use.",
"disposition": "corroboration",
"affected_patterns": [
"CAND-0002",
"CAND-0003",
"CAND-0006",
"CAND-0007"
],
"evidence": [
"research/reviews/StorageReceiverRecovery.md",
"research/experiments/storage-receiver/result.json",
"research/evolution/cycles/2026-09-28-storage-receiver-effort.json"
],
"remaining_uncertainty": [
"Power-loss, device-loss backup, remote durability and arbitrary operation-ID retention remain unverified.",
"No full frontend contract, independent human validation or M1/M2/M3 acceptance.",
"New runtime group is not comparable with static studies; no learning trend."
],
"reviewer": {
"name": "Codex",
"role": "author-model",
"independent_human": false
},
"effort": {
"research_minutes": 4.123,
"execution_minutes": 0.014,
"reported_tokens": null,
"cost_usd": null
},
"supersedes": []
}
]
}

View file

@ -1,6 +1,6 @@
{
"schema_version": 1,
"assessed_at": "2026-09-27",
"assessed_at": "2026-09-28",
"profiles": [
{
"id": "CAND-0001",
@ -60,20 +60,26 @@
"family": "Joplin",
"ref": "research/reviews/LocalFirstEvidenceExpansion.md",
"scope": "Local storage and synchronization architecture."
},
{
"kind": "runtime",
"family": "authored SQLite / Git recovery fixture",
"ref": "research/reviews/StorageReceiverRecovery.md",
"scope": "SQLite WAL/FULL: pre-commit process kill rolls back; post-commit kill preserves document and outbox. No power-loss/backup proof."
}
],
"independent_full_contract_uses": null,
"review": {
"assessed_at": "2026-09-27",
"assessed_at": "2026-09-28",
"reviewer": "Codex",
"role": "author-model",
"independent_human": false,
"last_substantive_review_date": "2026-09-27",
"substantive_review_ref": "research/reviews/M1-SeedReview.md",
"substantive_review_scope": "Editorial review of the connected core; M1 revise/defer."
"last_substantive_review_date": "2026-09-28",
"substantive_review_ref": "research/reviews/StorageReceiverRecovery.md",
"substantive_review_scope": "SQLite WAL/FULL: pre-commit process kill rolls back; post-commit kill preserves document and outbox. No power-loss/backup proof."
},
"gaps": [
"No executed storage crash or backup test.",
"Process-kill recovery observed in SQLite fixture; power-loss, device-loss backup and full frontend contract remain unverified.",
"Local acceptance is not off-device recovery."
],
"superseded_by": []
@ -92,20 +98,26 @@
"family": "Joplin",
"ref": "research/reviews/LocalFirstEvidenceExpansion.md",
"scope": "Per-item synchronization metadata."
},
{
"kind": "runtime",
"family": "authored SQLite / Git recovery fixture",
"ref": "research/reviews/StorageReceiverRecovery.md",
"scope": "SQLite atomic supersession/restart plus Git identity replay after lost local acknowledgement; one authored integration, no frontend recurrence."
}
],
"independent_full_contract_uses": null,
"review": {
"assessed_at": "2026-09-27",
"assessed_at": "2026-09-28",
"reviewer": "Codex",
"role": "author-model",
"independent_human": false,
"last_substantive_review_date": "2026-09-27",
"substantive_review_ref": "research/reviews/M1-SeedReview.md",
"substantive_review_scope": "Editorial review of the connected core; M1 revise/defer."
"last_substantive_review_date": "2026-09-28",
"substantive_review_ref": "research/reviews/StorageReceiverRecovery.md",
"substantive_review_scope": "SQLite atomic supersession/restart plus Git identity replay after lost local acknowledgement; one authored integration, no frontend recurrence."
},
"gaps": [
"Atomic supersession and retry contract proposed, not storage-tested."
"Atomic supersession observed in SQLite fixture; concurrent in-flight supersession, UI states and general receiver retention remain unverified."
],
"superseded_by": []
},
@ -223,17 +235,23 @@
"family": "authored release fixture",
"ref": "research/reviews/ActivationConflictsAndReload.md",
"scope": "Related conditional activation-map mechanism in one process, not the full frontend pattern."
},
{
"kind": "runtime",
"family": "authored SQLite / Git recovery fixture",
"ref": "research/reviews/StorageReceiverRecovery.md",
"scope": "Actual Git receive-pack rejects a forced divergent branch update; related ancestry mechanism, not exact-version frontend semantics."
}
],
"independent_full_contract_uses": null,
"review": {
"assessed_at": "2026-09-27",
"assessed_at": "2026-09-28",
"reviewer": "Codex",
"role": "author-model",
"independent_human": false,
"last_substantive_review_date": "2026-09-27",
"substantive_review_ref": "research/reviews/M1-SeedReview.md",
"substantive_review_scope": "Editorial review of the connected core; M1 revise/defer."
"last_substantive_review_date": "2026-09-28",
"substantive_review_ref": "research/reviews/StorageReceiverRecovery.md",
"substantive_review_scope": "Actual Git receive-pack rejects a forced divergent branch update; related ancestry mechanism, not exact-version frontend semantics."
},
"gaps": [
"Distributed atomicity and complete frontend recurrence remain unverified.",
@ -279,17 +297,23 @@
"family": "Element / Matrix",
"ref": "research/reviews/MatrixRecoveryExperiment.md",
"scope": "Real SDK with synthetic transport: lost acknowledgement, identity reuse and authoritative echo."
},
{
"kind": "runtime",
"family": "authored SQLite / Git recovery fixture",
"ref": "research/reviews/StorageReceiverRecovery.md",
"scope": "Actual Git same-commit replay is a no-op, reachable identity survives GC, fresh identity repeats intent; no arbitrary operation ledger."
}
],
"independent_full_contract_uses": null,
"review": {
"assessed_at": "2026-09-27",
"assessed_at": "2026-09-28",
"reviewer": "Codex",
"role": "author-model",
"independent_human": false,
"last_substantive_review_date": "2026-09-27",
"substantive_review_ref": "research/reviews/M1-SeedReview.md",
"substantive_review_scope": "Editorial review of the connected core; M1 revise/defer."
"last_substantive_review_date": "2026-09-28",
"substantive_review_ref": "research/reviews/StorageReceiverRecovery.md",
"substantive_review_scope": "Actual Git same-commit replay is a no-op, reachable identity survives GC, fresh identity repeats intent; no arbitrary operation ledger."
},
"gaps": [
"No production server deduplication or complete retention-window guarantee.",

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
# SQLite / Git recovery experiment
Existing owner: FEP-WP-0003 T04. Preregistered as LEARN-0018 in commit `aea9796`.
[Review](../../reviews/StorageReceiverRecovery.md) explains the acceptance scope.
Run from the repository root with ordinary Python (assertions enabled), SQLite
and Git; no dependency installation or service is needed:
```sh
python3 research/experiments/storage-receiver/experiment.py --output /tmp/fep-recovery-new.json
```
The output must be a new path. The runner retains a failure receipt before raising;
never overwrite the committed result. Compare the receipt's engine versions and
binary/source hashes before treating another environment as a replication.
Temporary databases and bare repositories are isolated and removed on exit. Git
configuration is isolated from user/system configuration. Each killed process is a
new fixture worker, stopped only after an explicit checkpoint. Every read of the
receiver runs a new Git process; the receiver itself is real `git receive-pack`
using Git's local transport, not an authored protocol substitute. No port is opened.
Nine checks pass, including two intentionally broken negative controls. The sender
is killed after Git reports success but before the application persists its receipt:
this models lost **local** acknowledgement, not network packet loss. SQLite kills
bracket commit and interrupt a multi-statement transaction; they do not inject a
crash inside the commit syscall or simulate power loss. Receiver fsync/disk-failure
behavior, remote authentication, backup, UI behavior and full-contract recurrence
remain untested. A reachable commit is not an indefinitely retained operation ledger.

View file

@ -0,0 +1,238 @@
#!/usr/bin/env python3
"""Bounded local mechanism experiment; never touches the checkout's Git refs."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import selectors
import shutil
import signal
import sqlite3
import subprocess
import sys
import tempfile
import time
from datetime import datetime, timezone
HERE = Path(__file__).resolve()
def connect(path):
db = sqlite3.connect(path)
assert db.execute('PRAGMA journal_mode=WAL').fetchone()[0] == 'wal'
db.execute('PRAGMA synchronous=FULL')
assert db.execute('PRAGMA synchronous').fetchone()[0] == 2
return db
def run(*args, cwd=None, check=True, input=None):
# Ignore user Git configuration/hooks and inherited repository targeting.
env = {k: v for k, v in os.environ.items() if not k.startswith('GIT_')}
env.update(GIT_CONFIG_NOSYSTEM='1', GIT_CONFIG_GLOBAL='/dev/null',
GIT_AUTHOR_NAME='Recovery Fixture', GIT_AUTHOR_EMAIL='fixture@example.invalid',
GIT_COMMITTER_NAME='Recovery Fixture', GIT_COMMITTER_EMAIL='fixture@example.invalid',
GIT_AUTHOR_DATE='2026-09-28T00:00:00Z', GIT_COMMITTER_DATE='2026-09-28T00:00:00Z')
return subprocess.run(args, cwd=cwd, env=env, text=True, input=input,
capture_output=True, check=check, timeout=20)
def checkpoint():
print('checkpoint', flush=True)
# Parent kills only this dedicated fixture subprocess, after its marker.
signal.pause()
def worker(mode, root):
db = connect(root / 'local.db')
if mode == 'send':
oid = db.execute("SELECT oid FROM pending WHERE id='send'").fetchone()[0]
run('git', 'push', str(root / 'receiver.git'), f'{oid}:refs/heads/main', cwd=root / 'client')
checkpoint() # Accepted remotely, local acknowledgement deliberately absent.
return
db.execute('BEGIN IMMEDIATE')
db.execute("UPDATE pending SET state='superseded' WHERE id='old'")
if mode == 'split':
db.commit() # Deliberately broken negative control.
checkpoint()
return
db.execute("UPDATE document SET body='new'")
if mode == 'middle':
checkpoint()
return
db.execute("INSERT INTO pending VALUES ('new','new-identity','pending')")
if mode == 'before':
checkpoint()
return
db.commit()
checkpoint()
def kill_at(mode, root):
p = subprocess.Popen([sys.executable, str(HERE), '--worker', mode, '--root', str(root)],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
try:
with selectors.DefaultSelector() as selector:
selector.register(p.stdout, selectors.EVENT_READ)
if not selector.select(timeout=20):
raise RuntimeError(f'{mode}: checkpoint timeout')
line = p.stdout.readline().strip()
if line != 'checkpoint':
raise RuntimeError(f'{mode}: no checkpoint: {line} {p.stderr.read()}')
p.kill()
p.communicate(timeout=5)
assert p.returncode == -signal.SIGKILL
return p.returncode
finally:
if p.poll() is None:
p.kill()
p.communicate(timeout=5)
def initialize(root):
root.mkdir()
with connect(root / 'local.db') as db:
db.executescript('CREATE TABLE document(body TEXT); '
'CREATE TABLE pending(id TEXT PRIMARY KEY, oid TEXT, state TEXT); '
"INSERT INTO document VALUES ('old'); "
"INSERT INTO pending VALUES ('old','old-identity','pending');")
def state(root):
with connect(root / 'local.db') as db:
assert db.execute('PRAGMA integrity_check').fetchone()[0] == 'ok'
return {'body': db.execute('SELECT body FROM document').fetchone()[0],
'pending': db.execute('SELECT * FROM pending ORDER BY id').fetchall()}
def sha(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def experiment(report):
with tempfile.TemporaryDirectory(prefix='fep-storage-receiver-') as temp:
root = Path(temp)
old = {'body': 'old', 'pending': [('old', 'old-identity', 'pending')]}
new = {'body': 'new', 'pending': [('new', 'new-identity', 'pending'),
('old', 'old-identity', 'superseded')]}
for mode, expected in [('before', old), ('after', new), ('middle', old),
('split', {'body': 'old', 'pending': [('old', 'old-identity', 'superseded')]})]:
case = root / mode
initialize(case)
code = kill_at(mode, case)
observed = state(case)
report['checks'].append({'id': f'local-{mode}', 'pass': observed == expected,
'negative_control': mode == 'split', 'worker_exit': code,
'observed': observed})
assert observed == expected
case = root / 'receiver'
initialize(case)
client = case / 'client'
receiver = case / 'receiver.git'
run('git', 'init', '--quiet', '--initial-branch=main', str(client))
run('git', 'init', '--quiet', '--bare', '--initial-branch=main', str(receiver))
run('git', 'config', 'receive.denyNonFastForwards', 'true', cwd=receiver)
tree = run('git', 'mktree', cwd=client, input='').stdout.strip()
def commit(parent, text):
args = ['git', 'commit-tree', tree]
if parent:
args += ['-p', parent]
return run(*args, cwd=client, input=text + '\n').stdout.strip()
def push(oid, force=False):
return run('git', 'push', '--porcelain', str(receiver),
('+' if force else '') + f'{oid}:refs/heads/main', cwd=client, check=False)
def tip():
return run('git', 'rev-parse', 'refs/heads/main', cwd=receiver).stdout.strip()
base = commit(None, 'base')
accepted = commit(base, 'operation A: synthetic intent')
assert push(base).returncode == 0
with connect(case / 'local.db') as db:
db.execute("INSERT INTO pending VALUES ('send',?,'pending')", (accepted,))
code = kill_at('send', case)
with connect(case / 'local.db') as db:
pending = db.execute("SELECT state FROM pending WHERE id='send'").fetchone()[0]
report['checks'].append({'id': 'lost-local-ack', 'pass': tip() == accepted and pending == 'pending',
'worker_exit': code, 'receiver_tip': tip(), 'outbox_state': pending})
assert tip() == accepted and pending == 'pending'
replay = push(accepted)
count = int(run('git', 'rev-list', '--count', 'main', cwd=receiver).stdout)
report['checks'].append({'id': 'same-identity-replay', 'pass': replay.returncode == 0 and count == 2,
'exit': replay.returncode, 'history_count': count,
'up_to_date': 'up to date' in replay.stdout})
assert replay.returncode == 0 and count == 2
with connect(case / 'local.db') as db:
db.execute("UPDATE pending SET state='acknowledged' WHERE id='send'")
concurrent = commit(accepted, 'operation B')
assert push(concurrent).returncode == 0
stale = commit(base, 'stale operation C')
with connect(case / 'local.db') as db:
db.execute("INSERT INTO pending VALUES ('stale',?,'pending')", (stale,))
rejected = push(stale, force=True) # Force transmission; real server denies it.
with connect(case / 'local.db') as db:
pending = db.execute("SELECT state FROM pending WHERE id='stale'").fetchone()[0]
server_refused = 'non-fast-forward' in rejected.stderr and 'remote rejected' in rejected.stdout
report['checks'].append({'id': 'stale-ancestry', 'pass': rejected.returncode != 0 and server_refused and tip() == concurrent and pending == 'pending',
'exit': rejected.returncode, 'server_refused': server_refused,
'receiver_tip_unchanged': tip() == concurrent, 'outbox_state': pending})
assert report['checks'][-1]['pass']
run('git', 'gc', '--prune=now', cwd=receiver)
retained = run('git', 'cat-file', '-t', accepted, cwd=receiver).stdout.strip()
ancestor = run('git', 'merge-base', '--is-ancestor', accepted, 'main', cwd=receiver, check=False)
report['checks'].append({'id': 'reachable-identity-after-gc', 'pass': retained == 'commit' and ancestor.returncode == 0})
assert report['checks'][-1]['pass']
# The same business intent under a NEW identity is another accepted commit.
fresh = commit(concurrent, 'operation A: synthetic intent')
assert push(fresh).returncode == 0
count = int(run('git', 'rev-list', '--count', 'main', cwd=receiver).stdout)
report['checks'].append({'id': 'fresh-identity-duplicates-intent', 'negative_control': True,
'pass': fresh != accepted and count == 4, 'history_count': count})
assert report['checks'][-1]['pass']
report['identities'] = {'base': base, 'accepted': accepted, 'concurrent': concurrent,
'stale': stale, 'fresh': fresh}
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--worker', choices=['before', 'after', 'middle', 'split', 'send'])
parser.add_argument('--root', type=Path)
parser.add_argument('--output', type=Path)
args = parser.parse_args()
if args.worker:
worker(args.worker, args.root)
return
if not args.output:
parser.error('--output is required (use a new file to preserve prior receipts)')
# Reserve the receipt before execution and refuse to overwrite retained evidence.
with args.output.open('x') as output:
start = time.monotonic()
import _sqlite3
report = {'started_utc': datetime.now(timezone.utc).isoformat(), 'checks': [],
'provenance': {'python': sys.version.split()[0], 'sqlite': sqlite3.sqlite_version,
'sqlite_source_id': sqlite3.connect(':memory:').execute('SELECT sqlite_source_id()').fetchone()[0],
'git': run('git', '--version').stdout.strip(),
'git_binary_sha256': sha(shutil.which('git')),
'sqlite_python_extension_sha256': sha(_sqlite3.__file__),
'experiment_sha256': sha(HERE)},
'settings': {'sqlite_journal': 'wal', 'sqlite_synchronous': 'FULL',
'receiver': 'local bare git receive-pack', 'denyNonFastForwards': True},
'scope': 'Process kills and discarded local acknowledgement; no power-loss or network-loss simulation.'}
try:
experiment(report)
report['status'] = 'passed'
except Exception as exc:
report['status'] = 'failed'
report['error'] = f'{type(exc).__name__}: {exc}'
raise
finally:
report['execution_seconds'] = round(time.monotonic() - start, 6)
json.dump(report, output, indent=2)
output.write('\n')
print(f"{len(report['checks'])} checks: {report['status']}")
if __name__ == '__main__':
main()

View file

@ -0,0 +1,131 @@
{
"started_utc": "2026-09-28T08:57:26.496125+00:00",
"checks": [
{
"id": "local-before",
"pass": true,
"negative_control": false,
"worker_exit": -9,
"observed": {
"body": "old",
"pending": [
[
"old",
"old-identity",
"pending"
]
]
}
},
{
"id": "local-after",
"pass": true,
"negative_control": false,
"worker_exit": -9,
"observed": {
"body": "new",
"pending": [
[
"new",
"new-identity",
"pending"
],
[
"old",
"old-identity",
"superseded"
]
]
}
},
{
"id": "local-middle",
"pass": true,
"negative_control": false,
"worker_exit": -9,
"observed": {
"body": "old",
"pending": [
[
"old",
"old-identity",
"pending"
]
]
}
},
{
"id": "local-split",
"pass": true,
"negative_control": true,
"worker_exit": -9,
"observed": {
"body": "old",
"pending": [
[
"old",
"old-identity",
"superseded"
]
]
}
},
{
"id": "lost-local-ack",
"pass": true,
"worker_exit": -9,
"receiver_tip": "c55e497766915672901a3fb34181c6322d87a352",
"outbox_state": "pending"
},
{
"id": "same-identity-replay",
"pass": true,
"exit": 0,
"history_count": 2,
"up_to_date": true
},
{
"id": "stale-ancestry",
"pass": true,
"exit": 1,
"server_refused": true,
"receiver_tip_unchanged": true,
"outbox_state": "pending"
},
{
"id": "reachable-identity-after-gc",
"pass": true
},
{
"id": "fresh-identity-duplicates-intent",
"negative_control": true,
"pass": true,
"history_count": 4
}
],
"provenance": {
"python": "3.12.3",
"sqlite": "3.45.1",
"sqlite_source_id": "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257ccalt1",
"git": "git version 2.43.0",
"git_binary_sha256": "2a8c18fbf43da9f692d75474c72bea9dfd796c260b0f3dfe456376abc3bbd668",
"sqlite_python_extension_sha256": "ecf54958b24c533f5c8dc67ca82cdce236aeb322864140bfe93eb8a69b729f4d",
"experiment_sha256": "5fe558214648636878816a35d0d63632de08a3391bd07f3eb4e6c81c7747063f"
},
"settings": {
"sqlite_journal": "wal",
"sqlite_synchronous": "FULL",
"receiver": "local bare git receive-pack",
"denyNonFastForwards": true
},
"scope": "Process kills and discarded local acknowledgement; no power-loss or network-loss simulation.",
"identities": {
"base": "8581afcccc64516084339b8d633daf4b01ecef35",
"accepted": "c55e497766915672901a3fb34181c6322d87a352",
"concurrent": "ce6b4356d194fef30d6246a2ed7f526c9e9a5f2f",
"stale": "e0e578fa81bb53fed8192623f3489529b6ddd710",
"fresh": "98a5ada629763dba0706264e88814e87d90883c1"
},
"status": "passed",
"execution_seconds": 0.868828
}

View file

@ -0,0 +1,43 @@
# Loose-end review — 2026-09-28
Reviewed all eight local workplans, including finished records. Initially WP-0006
was active; WP-0002/0003/0004/0008 blocked; WP-0001/0005/0007 finished. No ready or
proposed workplan remained. Working tree was clean. State Hub inbox and FEP human-
needed task results were empty; source files take precedence over the stale brief.
| Existing task(s) | Closeout disposition | Remaining prerequisite / resume condition |
| --- | --- | --- |
| WP-0003 T04 | **Done:** implemented and executed the preregistered concrete SQLite/Git recovery experiment; nine checks, including two negative controls | [Bounded evidence and exclusions](StorageReceiverRecovery.md); no stronger frontend/durability claim |
| WP-0002 T03 | Wait; plan blocked | Independent usefulness/validated seed-case evidence; submitted model worksheet is not independent human review |
| WP-0003 T01 | Progress; plan remains blocked by T02 | Six-stream full-contract implementation evidence remains incomplete. Static source paths, authored fixtures and documentary leads are not independent full uses; existing navigation fixture remains paused |
| WP-0003 T02 | Wait; plan blocked | M1 acceptance, then completed contrasting cases and quality/human/AT evidence |
| WP-0004 T01 | Wait; plan blocked | M1 acceptance before canonical validator/export |
| WP-0004 T02/T03 | Wait; plan blocked | M2 acceptance before held-out evaluation and complete real-repository applicability studies |
| WP-0006 T03 | Changed to wait; plan changed to blocked | Useful comparable research window; largest group has two cycles and incomplete effort. Today's different runtime protocol cannot make three |
| WP-0008 T01 | Wait; plan blocked | Hosted discovery/common-template integration and first real pinned consumer decision |
| WP-0008 T02 | Wait; plan blocked | Actual consumer closeout hook, real use and linked observed outcome; roster still empty |
| WP-0008 T03 | Wait; plan blocked | Admitted budget-enforced executor, standing release/rollback evidence, natural cadence proof and a real unassisted cycle/monthly packet |
Other tasks remain done. No task or workplan was created or cancelled; no evidence
gate was waived. No finished workplan was reopened. WP-0003 T01 remains actionable
research, not an invented external blocker; completing its broad independent-use
contract cannot be substituted by further author-only synthesis in this closeout.
WP-0006 waits on a qualifying evidence window rather than running repetitions for
a count. Existing tasks retain all obligations; no residual work is parked here alone.
The WP-0008 recheck found only September 27 intake smoke/manual receipts, not the
expected September 28 natural daily receipt. [Query evidence](../../docs/evidence/2026-09-28-loop-recheck.json)
records absence without diagnosing the scheduler. The owner plans ACTIVITY-WP-0041
and GLAS-WP-0012 remain blocked; this session grants no runtime authority. The
October 1 packet remains due even without consumers. No schedule or service changed.
Current profiles and evolution reporting now include the bounded recovery evidence;
all eighteen candidates remain hypotheses. Current orientation counts are corrected;
frozen seventeen-record 0.1 publication and historical reviews remain immutable.
Validation also exposed a pre-existing missing CAND-0018 classification record.
Completed that local T01 repair after reading the candidate: composition/portability
with implementation and compatibility costs. No new pattern or maturity change.
Checks: 69 tests passed across evolution, schema, bench/admission and reference;
synthetic bench plan, evolution/reference/classification freshness and whitespace.
The classification failure before repair is recorded here rather than hidden.

View file

@ -0,0 +1,65 @@
# Concrete storage and receiver recovery
2026-09-28, Codex author-model review; no independent human validation.
Owner: FEP-WP-0003 T04. [Preregistration](../evolution/cycles/2026-09-28-storage-receiver.md)
at `aea9796`; [runner and reproduction](../experiments/storage-receiver/README.md),
[retained receipt](../experiments/storage-receiver/result.json).
## Result and task disposition
Nine checks pass on SQLite 3.45.1 and Git 2.43.0. This completes T04's bounded
concrete-pair investigation with explicit negative controls and remaining unknowns.
The Git receiver is upstream receive-pack, launched against a disposable bare
repository; the sender/outbox integration is authored research code. It is neither
a deployed frontend nor a replacement for the Matrix experiment's missing real
Matrix-server proof. No candidate contract or maturity is promoted.
| Obligation | Observation | Boundary |
| --- | --- | --- |
| Interrupted local transaction | SIGKILL before commit leaves old document and pending operation intact after reopen/integrity check | Process interruption, not power loss or a kill inside commit I/O |
| Commit before acknowledgement | SIGKILL after commit retains new document, new pending identity and old superseded state | SQLite WAL, synchronous FULL on this host |
| Supersession | Kill between statements rolls the transaction back; committed path changes all three facts | No sender concurrency or transmitted-operation recall guarantee |
| Negative local control | Committing old-operation supersession separately leaves no replacement after interruption | Atomic grouping is necessary in this fixture |
| Lost local receipt | Actual receive-pack accepts A; killed sender leaves A pending in SQLite | Git response arrived at sender; local acknowledgement was not persisted |
| Same-identity replay | New push of A reports up-to-date; history still has only base plus A | Identity is the immutable commit, not arbitrary business intent |
| Stale ancestry | A forced divergent update after B is refused by the receiver's denyNonFastForwards setting; branch stays at B and local work remains pending | Ancestry policy differs from exact expected-version comparison |
| Identity retention | Fresh processes and garbage collection preserve reachable A and show it is an ancestor of B | Reachability is the tested retention scope, not indefinite retention or an authenticated receipt |
| Negative receiver control | Rebuilding the same intent as a new commit on B adds a fourth history entry | Git cannot deduplicate business intent with different identities |
## Interpretation and documentary basis
The SQLite [atomic commit description](https://www.sqlite.org/atomiccommit.html)
explains transaction recovery. Its [synchronous pragma](https://sqlite.org/pragma.html#pragma_synchronous)
defines FULL synchronization for WAL. These are engine contracts, not observations
of this machine's power-loss resilience; the fixture tests ordinary process kills.
Git's [receive configuration](https://git-scm.com/docs/git-config#Documentation/git-config.txt-receivedenyNonFastForwards)
and [push documentation](https://git-scm.com/docs/git-push) distinguish ref updates,
ancestry and expected-value leases. The experiment forces a divergent transmission
and observes server refusal, rather than counting a client-side lease refusal as
receiver evidence. Replay while A is the tip is a no-op. Once B is the tip, checking
A's reachable ancestry supplies scoped evidence of inclusion; trying to push A
again is not a general outcome-lookup API. A fresh identity does not prove freshness
of intent, and object existence alone does not prove a branch accepted that object.
This corroborates the existing separation among Local Commit Boundary (CAND-0002),
Pending Synchronization (CAND-0003), Version-Checked Mutation (CAND-0006) and Mutation
Outcome Tracking (CAND-0007). Git's ancestry rule is only a related mechanism for
CAND-0006: it is not exact version equality or a frontend implementation. SQLite
and Git observations are one authored integration, not two independent full uses.
## Evidence limits and existing ownership
No failed development run occurred; the negative-control failures of the proposed
contracts are retained as successful detection checks. There were no inference calls.
No power failure, filesystem fault, device-loss restore, remote durability guarantee,
identity-expiry test, authorization change, user interaction or accessibility test
was performed. SQLite and Git cannot form one distributed transaction here. Full
frontend contracts and production recurrence remain unknown.
T04 is done for this selected pair and tested process-failure envelope. Broader
implementation evidence remains WP-0003 T01; validated cases/human/AT evidence remain
WP-0003 T02 and WP-0002 T03. Those existing obligations retain the stronger claims;
this review opens no task, workplan or stronger recovery promise. Device-loss backup
is an explicit exclusion, not silently inferred from synchronization. Frozen 0.1
publication and historical assessments remain unchanged.