Home / Projects / Orchestrata Ultimate / Decisions / ADR index / ADR-0088
ADR-0088: One shared green ledger per repo; a test reruns only when its own code or the code it tests changes
| field | value |
|---|---|
| Status | Accepted — Accepted (ruling, Mohamed, 2026-10-11) · R1 ruled (a) 2026-10-11 |
| Date | 2026-10-11 |
| Register | factory (docs/adr, 4-digit ids) |
| Source | docs/adr/0088-one-shared-green-ledger-per-repo-a-test-reruns-only-when-its-code-or-the-code-it-tests-changes.md — recorded on branch feat/shared-ledger at 099eb1773, not yet on master |
| Wiki register entry | ADR-0088 (wiki status: Accepted) |
The record
Status: Accepted (ruling, Mohamed, 2026-10-11) · R1 ruled (a) 2026-10-11 · Date: 2026-10-11 · Amends: RUL-032 mechanism 1
Context
RUL-032 (2026-09-22) says a test that passed is not run again until its code
or the code it tests changes. Its mechanism, scripts/test_green_ledger.py,
falls short of that in four measured ways (2026-10-11, worktree
wt-GREENLET-FT at f0d9f23d8):
| Gap | Measurement |
|---|---|
| G1 one ledger per working tree | reports/test-green-ledger.json lives in each checkout. A fresh worktree reuses nothing: test_green_ledger.py --status over the four greenlet-ship modules printed 0 reused, 4 to run, and the file did not exist. Every ship runs from a fresh worktree (platform convention 3). |
G2 only run_tests.py writes it |
Greens from ad-hoc pytest, run_tests_streaming.py and the per-change red/green runs are never recorded, although the live observer plugin (-p orchestrata_pytest_observer, loaded for every pytest on this box via PYTEST_ADDOPTS) sees each one. |
| G3 the key misses non-import subjects | The key is the test module + its Python import closure + conftest chain + pyproject.toml. tests/deploy/test_ship_merge_master.py (6 key inputs) does not include deploy/ship-merge-master.sh, the script it runs; tests/core/test_uv_lock_contract.py (5 key inputs, empty closure) does not include uv.lock, the file it reads. Today's two changes were exactly those two files: under the current key both tests' greens would have been reused after their subject changed. |
| G4 module granularity | One key and one entry per module: a module with one failing test re-runs its passing siblings. |
Decision (Mohamed, 2026-10-11, verbatim)
"yes. one shared per repo with the test file and hash and test id and only rerun when the code of the test or the code the code tests changes" — in answer to: record greens from every observed run (the live observer), and keep one shared ledger per repo instead of one per worktree.
- One ledger per repository. Stored under the git common directory
(
$(git rev-parse --git-common-dir)/orchestrata/test-green-ledger.sqlite3), so the primary checkout and every worktree read and write the same store; never committed; it outlives any worktree. SQLite (stdlibsqlite3, WAL, busy timeout) because writers are concurrent: eightrun_tests.pyworkers and several worktrees at once. The per-treereports/test-green-ledger.jsonis retired; its entries are imported once, and only where the recorded key still equals the key recomputed now. - One row per test id and key. Row: test id (pytest node id), test file, content key (sha256), outcome, run id, commit, worktree path, timestamp, duration. A test is reused when a passing row exists for (its id, its current key); otherwise it runs. Rows for several keys of one test coexist, so two worktrees at different code states each keep their own greens.
- The key is the code of the test and the code it tests. sha256, in a
fixed order, over the bytes of:
- the test module and its
conftest.pychain (the code of the test); - every first-party module in its transitive import closure (unchanged from RUL-032);
- every non-Python file the test reaches by reference — the paths the
reference-edge extractor already resolves (
.sh .pl .feature .json) — and every fileplans/affected-tests-manifest.jsonmaps to the test (closes G3:deploy/ship-merge-master.sh,uv.lock); - the inputs every test's code runs on:
pyproject.toml,uv.lock, and the interpreter build (sys.version, GIL state).env/*.envstays out of the key, as RUL-032 ruled. The key is computed at collection time, from the bytes the run imported.
- the test module and its
- Every observed run records. The live observer plugin writes a row when
a test's setup, call and teardown all pass;
run_tests.pykeeps recording. A ledger write failure is logged and never fails a test. - Who reuses.
run_tests.py— and through it ship's verify step — partitions per test id before batching, prints the reused count, and folds reused tests into the report with their source run ids;--forceruns everything. A plainpytestinvocation runs exactly what it was asked to.
Open ruling R1 — does the prod gate reuse?
ADR-0087 (Accepted 2026-10-11) requires "one full correctness run of the promoted commit". Under this ADR, a test whose recorded key equals its key at the promoted commit has been proven on byte-identical code.
- (a) Recommended — the prod gate composes. The
promote_env.py testrecord still binds the promoted commit's sha, and covers every test of that commit: executed in this run, or reused from a row whose key equals the key computed from the promoted tree. The record lists each reused id with its source run. A different tree still cannot be admitted (the keys differ), and RUL-032's "escalation never re-runs a proven test" holds at prod too. - (b) The prod gate executes everything. Reuse applies to verify and
development runs only;
promote_env.py testruns with--force. Cost: one full suite per promote (≈8 min nominal; ADR-0087 measured 55 min under load).
Until R1 is ruled, the prod gate keeps today's behaviour (no reuse).
RULED 2026-10-11 — option (a) (Mohamed: "yes R1 (a), go ahead, that is
LAW"): the prod gate composes. promote_env.py test binds the promoted
commit's sha and covers every test of that commit, executed in this run or
reused from a passing row whose key equals the key computed from the promoted
tree; the record lists each reused test id with its source run. This
satisfies ADR-0087 item 1's "one correctness run of the promoted commit": a
reused row proves byte-identical code, and a different tree cannot be
admitted because its keys differ. --force stays available to execute
everything, and is never required by the gate.
Alternatives rejected
- Keep the per-tree JSON and copy it into new worktrees — copies diverge
the moment two worktrees run; concurrent
os.replacewriters lose updates. - Key on the commit sha — a dirty tree has no sha, and two commits with identical code under test would rerun everything; content is the identity.
- Per-test keys from the runtime call graph — narrower, but the runtime
capture is optional and incomplete (it misses subprocess subjects such as
ship-merge-master.sh); a missed edge means a stale green reused. The static closure plus reference edges plus manifest over-approximates instead. - Record from
run_tests.pyonly — leaves G2: every proven ad-hoc run is thrown away.
Consequences
- A fresh worktree reuses every green proven on identical code anywhere in the repo; a ship reruns only tests whose key changed.
- A change to a shell script, Prolog file, JSON manifest or
uv.lockreruns the tests that exercise it (more reruns than today's key, never fewer correct ones). - A flaky test that passed once is reused until its key changes; ADR-0087's rerun policy and the perf lane are unaffected.
tests/scripts/test_test_green_ledger.pygains the shared-store, per-test-id, concurrent-writer and key-coverage (G3) cases, each red first.
Provenance
- Mohamed, 2026-10-11: "does ship exclude the tests that were already run?", then the ruling quoted above.
- R1 ruled 2026-10-11 by Mohamed: "yes R1 (a), go ahead, that is LAW" (the prod gate composes executed and key-identical reused greens, bound to the promoted sha).
- Evidence: this session's
test_green_ledger.py --statusoutput (G1);key_inputs()for the four sample modules (G3); RUL-032 text (rulings/RUL-032-tests-run-once-per-code-state.md). - Related: RUL-032, ADR-0087.
Addendum 1, 2026-10-14 (append-only; ruling, Mohamed, verbatim: "code state only")
Context: the independent review of the P1 key-soundness change (reviewer report,
plans/briefs/qwen/B2b-review-2026-10-14/, 2026-10-14) measured every one of the 1,962
test modules keying 4,572–4,573 of the repository's 5,504 files (HEAD median: 55), because
the harness closure (D5/D6) walks scripts/run_tests.py and the conftest chain whose read
and path edges name the harness's OWN outputs, directory-prefix expansion (D3/D4) admits
tests/, plans/ and reports/ wholesale, and _is_file admits gitignored files. The
inputs that change on every run: reports/test-durations.ndjson,
reports/run-tests-latest.json, .cache/test_durations.json, .flake_recoveries.ndjson,
.cache/gates.json, docs/CODEBASE_MAP.md, plans/ast-ultimate.json, context.json,
.env, five .venv/bin/* paths. One unchanged rerun of a module changed its key; six
identical runs recorded six keys; --status over 38 modules printed 0 reused, 38 to run.
Reuse under item 3 never occurred. The P1 brief forbade removing any key input, so the
exclusion needed a ruling.
Ruling: a key input is code state only. Item 3 is read as follows:
- A file is a key input only if it is tracked by git at the repository under test
(
git ls-files) or is one of the declared lock/interpreter inputs (pyproject.toml,uv.lock,sys.version, GIL state). A gitignored or untracked file is never a key input, whatever edge names it. - A file the harness WRITES is an output, not an input: the targets of write-mode
open, redirections andPath.write_*edges inscripts/run_tests.py, the observer plugin, the ledger CLI and the conftest chain are excluded from every key, and so is any path underreports/,.cache/andplans/ast-*.json/docs/CODEBASE_MAP*.md(generated artifacts, RUL-030). - A directory-prefix edge (D3/D4) expands only over tracked files, and never over
reports/,.cache/,.venv/or any gitignored subtree. - Environment files (
env/*.env,.env) stay out of the key, as item 3 already ruled. Consequence: the per-test row and the content key again describe the code the test runs on, nothing the run itself produces; a key changes only when a tracked input changes. Acceptance for the next build: the reviewer's measurement reversed — an unchanged rerun keeps every module's key; the median input count returns to the order of HEAD's 55;--statusover the 38 V3 modules reports reuse after one green run. The brief's principle "a missed edge means a stale green reused" is unchanged for tracked inputs.
Addendum 2, 2026-10-14 (append-only; ruling, Mohamed, verbatim: "reach by caller")
Context: the P1 reach guard (D7) marks a module repo-state when any git invocation
happens inside the module's run window, and the observer then withholds the module's
greens. The harness plugin tests/plugins/git_sentinel.py runs git ls-files --others
at every module's last teardown, inside that window, to catch tests that leave untracked
files. Measured by the review: every real module is marked (424 repo-state rows across 42
modules since 2026-10-12, 8 guard-unavailable), so item 4 ("every observed run records")
never held with the guard on; the D7 tests passed only because their fixture repositories
carry no sentinel.
Ruling: reach is attributed by caller. A file access or process spawn observed by the
guard belongs to the test only when the calling frame is in the test module, its
fixtures or conftest chain, or the first-party code the test exercises. A call whose
nearest first-party frame is harness code (the plugins under tests/plugins/, the live
observer, scripts/run_tests.py, the ledger itself) is the harness's reach: it marks
nothing and withholds nothing. The harness is already a key input through its own
section (D6), so its reads are accounted for there. The sentinel stays where it is and
keeps its per-module attribution. Rejected: moving the sentinel outside the window
(loses the attribution it exists for); exempting read-only git commands (a test that
reads repository state is exactly what the guard must catch).
Acceptance: with the guard on, a run of the V3 set records greens for every passing test
and writes no repo-state gap row caused by the sentinel; a test that itself calls git
inside its body is still marked.
Addendum 3, 2026-10-14 (append-only; ruling, Mohamed, 22:54, verbatim: "approved addendum 3")
Context: the P2a executor measured (plans/briefs/qwen/B2b-P2a-report.md §1.6–1.8) that
addendum 1 applied to the letter still leaves 4,486 key inputs for
tests/core/test_reference_edges_anchors.py, 4,353 of them in the D6 harness section,
because the harness _walk follows the reference edges of the harness roots
(scripts/run_tests.py, the live observer, the ledger CLI and the conftest chain) into
3,388 Python files: the roots carry directory-prefix edges over tests/, scripts/,
plans/, src/orchestrata_ultimate/, docs/adr/, specs/ and claude/, all tracked,
so addendum 1's "tracked only" does not shrink them, and only its generated-artifact rule
excludes the four tracked volatile files (reports/run-tests-latest.json,
docs/CODEBASE_MAP.md, plans/ast-ultimate.json, context.json). The pure import
closure of the same roots is 17 files; with the conftest closure (about 129) and the
module's own inputs the key is of the order of 150 inputs.
Ruling: harness roots contribute their code bytes only; their path and prefix edges are not key inputs.
- The D6 harness section is keyed by import closure only: the bytes of the harness roots and of the first-party modules they import, transitively. No reference edge (path, prefix, read, load) from a harness root or from a module of that closure contributes a key input; the data a run reads is not the code the test runs on.
KEY_SCHEMA_VERSIONbumps to 3, so every green recorded under the earlier key rules is missed once and re-proven at the new key.- An edge-named file (a file a test reaches by reference edge or by manifest, decision
item 3) is admitted as a key input only when it is tracked by git and is not a
generated artifact (addendum 1 items 1–2:
reports/,.cache/,.venv/,plans/ast-*.json,docs/CODEBASE_MAP*.md, the write-mode targets of the harness). The test's own reach (its module, fixtures, conftest chain and the first-party code it exercises) keeps every edge kind as before; only the harness's edges are withdrawn. Acceptance (increment P2a-2): an unchanged rerun keeps every module's key; the input count of each V3 module is below 200;--statusover the V3 set reports reuse after one green run;--gapsshows 0 sentinel-causedrepo-staterows; the six red-first tests intests/core/threading/test_green_ledger.pyare green. Provenance: Mohamed, 2026-10-14 22:54, "approved addendum 3", on the proposal recorded inplans/reports/executor-experiment-2026-10-14.md§9.2 and the P2a report §3. Measured 2026-10-14 23:45 (increment P2a-2,.orchestrata/REPORT-P2a-2.md): inputs 4,490 → 147 (reach guard), 150 (live observer), 213 (ledger reuse), 147 (anchors); an unchanged rerun kept all four keys; 0 sentinelrepo-staterows. The 213 is the genuine import closure oftests/scripts/conftest.py(scripts.run_tests,semantic_graph.embedder_factory), kept by this addendum's "the test's own reach keeps every edge kind"; the "below 200" figure above was the supervisor's sizing estimate, not part of the ruling.--statusstill reported no reuse for a reason outside this addendum (census selection string ≠RUN_TESTS_MARKEXPR, fixed in P2b).
Related ADRs
- Also mentioned: ADR-0087
Rulings cited
- RUL-032: RUL-032 — A test runs once per code state; escalation never re-runs a proven test
- RUL-030: RUL-030 — Generated artifacts belong in git
Specs cited
None (or no spec page exists for the ones named).