HOMELAB-2195: B11/B12 record contracts (frozen writer contract) #23

Merged
aaron merged 2 commits from plane/HOMELAB-2195-b3-contracts into main 2026-10-26 00:21:46 +00:00
Owner

Design-repo record for HOMELAB-2195 (W2 redo) item B3: the frozen writer contract for W2's path (B11) and W3's evidence path (B12).

What this document is

One table per write path: which row each path produces, with which columns, which payload keys, and which frozen value — plus which component writes it and which wave owns it. It is the artifact the runtime is reviewed against, so a reviewer can read a diff and say "this write is / is not in the contract" without re-deriving the vocabulary from code.

It is read off the authority, not invented: design/ld5/measurement.md M0-R42 and design/ld5/report.sql, and cross-checked against the code of record — record/record.go (kinds, statuses), run/loop.go (pickup, slots, attribution), run/run.go + cmd/ld-runtime/main.go (path steps), run/pane.go (pane hold/prompt), run/panes_close_journal.go + run/panes_timer.go (pane close, transcript), watch/* (Plane reads). Schema: prod manifests/ld-record/migrations/00001_record_schema.sql.

Where it lives

design/ld5/contracts-b11-b12.md (added, 180 lines).

Contents

  1. The three tables and who may write them — workflows, node_instances, events (append-only), with which status at which moment and why (merged, not done; abandoned for a refused pickup)
  2. The path steps (B11): what each step writes
  3. Pickup and pane rows (3.1 run/loop.go / watch/*, 3.2 run/pane.go / run/panes*.go)
  4. The frozen vocabulary, verbatim from M0-R42 / report.sql
  5. Every per-ticket kind carries its ticket
  6. What is not written yet, and by which wave
  7. Retention of the transcripts these rows point at

Precedence rule (the part to hold us to)

Where this document and measurement.md disagree about a value, measurement.md wins and the code is wrong. Where they disagree about what W2's path writes, this document wins, because it was read off the code that actually runs. Adding a value is a rules change: recorded in measurement.md before the LD5 sign, then here — never silently in code.

Docs only; no code, manifest or schema change.

Design-repo record for **HOMELAB-2195** (W2 redo) item **B3**: the frozen writer contract for W2's path (B11) and W3's evidence path (B12). ## What this document is One table per write path: **which row each path produces, with which columns, which payload keys, and which frozen value** — plus which component writes it and which wave owns it. It is the artifact the runtime is reviewed against, so a reviewer can read a diff and say "this write is / is not in the contract" without re-deriving the vocabulary from code. It is read off the authority, not invented: **`design/ld5/measurement.md` M0-R42** and **`design/ld5/report.sql`**, and cross-checked against the code of record — `record/record.go` (kinds, statuses), `run/loop.go` (pickup, slots, attribution), `run/run.go` + `cmd/ld-runtime/main.go` (path steps), `run/pane.go` (pane hold/prompt), `run/panes_close_journal.go` + `run/panes_timer.go` (pane close, transcript), `watch/*` (Plane reads). Schema: `prod` `manifests/ld-record/migrations/00001_record_schema.sql`. ## Where it lives `design/ld5/contracts-b11-b12.md` (added, 180 lines). ## Contents 1. The three tables and who may write them — `workflows`, `node_instances`, `events` (append-only), with which status at which moment and why (`merged`, not `done`; `abandoned` for a refused pickup) 2. The path steps (B11): what each step writes 3. Pickup and pane rows (3.1 `run/loop.go` / `watch/*`, 3.2 `run/pane.go` / `run/panes*.go`) 4. The frozen vocabulary, verbatim from M0-R42 / `report.sql` 5. Every per-ticket kind carries its ticket 6. What is not written yet, and by which wave 7. Retention of the transcripts these rows point at ## Precedence rule (the part to hold us to) Where this document and `measurement.md` disagree about a **value**, **`measurement.md` wins and the code is wrong**. Where they disagree about **what W2's path writes**, this document wins, because it was read off the code that actually runs. Adding a value is a rules change: recorded in `measurement.md` **before** the LD5 sign, then here — never silently in code. Docs only; no code, manifest or schema change.
2230 item 4. The vocabulary is M0-R42's; this document says which writer
produces which row, with which columns, and which frozen value, and it names
the rows report.sql reads that no W2 writer produces yet (ci.live heartbeat,
version.probe, escape red_ci_live, infra.rerun, node_instances, the B12 gate/
hold/accept.test rows) with their owner wave rather than shaping W2's rows to
look like them.

W2 writer changes it records: bump rows carry bump_pr (the pin repo's PR) as
well as land's pr; workflows.status uses the frozen vocabulary (merged, not
done); a slot refused at pickup is abandoned; sign.snapshot cites the Plane
activity id and the runtime confirms it before writing an operator-attributed
event.
Author
Owner

Review — design/ld5/contracts-b11-b12.md (docs-only, single reviewer)

Reviewed head 0e8e07c vs main 204abb6. Sources of record used: design/ld5/measurement.md (M0-R42, plus M0-R12/R16/R24/R29/R36/R37 as cited) and design/ld5/report.sql both on main; code read at infra-core plane/HOMELAB-2195-b3-records @ fdb36f2, path lab-director/dispatcher.

Verdict: REQUEST CHANGES — blocking on #1 and #6. The vocabulary half of the document is exact; the "what W2's path writes" half has two gaps that would hide a NOT_CERTIFIABLE at proof P3, which is the one thing this document exists to prevent.

Verified good

  • §4 vocabulary is verbatim M0-R42: all twelve rows (goal.check verdict and evidence kinds, escape.kind, infra.rerun.cause/evidence_kind, gate.decision.decision + head_sha/canary, hold.hold_kind, accept.test.result, ci.live, version.probe, workflows.status, probe ok/contains_merge_commit) match the measurement.md table value-for-value, no invented or dropped value.
  • §5's per-ticket kind list is M0-R42's list verbatim, including the version.probe / ci.live exemption.
  • Code spot-checks that hold: sign.snapshot payload keys incl. plane_activity_id (run/loop.go:270-279); pickup.position keys (loop.go:517-519); watch.position keys high_water/cursor/emitted (record/watermark.go:35,50-62); pane.hold keys and the readiness-deferral extras (run/pane.go:183,697-730); pane.prompt carries at in the payload (pane.go:868-882); pane.close/pane.transcript keys (run/panes_close_journal.go:41-49, cmd/ld-runtime/main.go:337-338); probe.live declared and unwritten (record/record.go KindProbe); statuses queued/running/merged/abandoned/failed (record.go); pr = land's and bump_pr = the pin repo's (run/run.go:162-165, run_test.go:668-685); gate keeps gate.decision with state, not decision (main.go:519-534).

Findings

  1. (blocking, P3) §1's sample_id rule does not describe what the step writer does, and §6 does not list the consequence. recordJournal.Step hard-codes SampleID: "runtime" for every step row (cmd/ld-runtime/main.go:530-547), including the gate.decision row. §1 says runtime is for "the runtime's own housekeeping (pickup.position, watch.position, pane lifecycle in a lane run); the sample id for sample rows" — a gate row is a sample row, and it carries runtime. report.sql reads gate decisions only under e.sample_id = (SELECT sample_id FROM current_sample) (lines ~1017-1019, ~1077, ~1100). So on an official run R1–R4 every gate row is invisible and condition 2 is uncomputable — a NOT_CERTIFIABLE that §6 declares absent. W2 does write this row, so §6's framing ("a row report.sql reads that no W2 writer produces") misses it. Fix: state the sample_id the step writer actually writes in §1/§2, and add a §6 line — "sample_id on path.step / gate.decision is runtime, not the sample id; report.sql reads gate rows by sample id" — with an owning wave (B12 or a W2 fix).

  2. (fix, docs accuracy) §3.1 says pickup.skipped's kind ∈ not-fleet, not-ready. Only not-fleet writes that row: RefusalNotReady does res.Skipped++ plus a log line and no journal row (loop.go:244-249; traceSkip is called only for RefusalNotFleet, loop.go:222-226). A not-ready item is therefore silent, and a not-ready compile failure lands as pickup.rejected with reason "not ready: …" (loop.go:250-258). Either say the row covers not-fleet only and add the silent-not-ready gap to §6, or record it as a known gap.

  3. (fix, docs accuracy) §3.1 gives pickup.rejected the payload reason, item_id, dispatched: false for every row. The compile-failure path writes {reason, item_id} with no dispatched key (loop.go:255); only the unsigned (loop.go:234) and attribution-refused (loop.go:287) paths write dispatched: false. Say "when the row ends a dispatch attempt".

  4. (note, §2 wording) "Every step row carries: step, state, at, …" reads as payload keys. at is the event column; main.go:530-531 writes only step and state plus the step's fields. One clause ("at is the event's column") removes the ambiguity.

  5. (verified, no change) §2's claims about the bump row, the gate keeping its own kind, probe.live being declared-but-unwritten, and node_instances having no W2 writer all check out against fdb36f2; §3.1's operator-attribution narrative matches SignerPolicy.ConfirmAttribution being applied in appendEvent for any operator-actor, with abandonSlot on refusal.

  6. (blocking, P3 completeness) §6 omits report.sql kinds that have no writer and, unlike the rows it does list, also no named owner. Missing from §6 entirely: canary.outcome ({kind,target,bracket,caught} → M0-R29 recall, report.sql ~1745), gateway.spend ({workflow_id,usd_gateway} → M0-R37 ±15%, ~1794), hold.refused (M0-R24 count, ~246), sign.sample (the sample sign and every rebaseline — the earliest sign.sample is the report's whole baseline, and it must carry pin_file=deploy-image.txt and auditor_egress=anthropic_only, report.sql ~125-140, ~438), drain.start/drain.end (M0-R16, ~248-249) and failure_review (M0-R36, ~226-229). Several of those are operator-only kinds per report.sql's own header (sign.sample, drain.start/drain.end, intervention, failure_review), so they may be deliberately outside the runtime contract — but the document never says who writes them, and §6's own framing ("each line is a row report.sql reads that no W2 writer produces") is then incomplete. canary.outcome and gateway.spend are not in the operator-only list, so they are unowned runtime rows. Fix: one line each — owner wave, or "operator/out of runtime scope, written by …" — so proof P3 cannot discover a missing sign or a missing canary row at the gate.

  7. (note, §7) §7's retention paragraph is consistent with the HOMELAB-2137 ruling and correctly says no sweeper is wired; nothing to change, recorded here so the paragraph is known to have been read rather than skipped.

Scope note: this was a read-only review; no files were edited. The unfixed items are document changes for the author, not reviewer edits.

## Review — `design/ld5/contracts-b11-b12.md` (docs-only, single reviewer) Reviewed head `0e8e07c` vs `main` `204abb6`. Sources of record used: `design/ld5/measurement.md` (M0-R42, plus M0-R12/R16/R24/R29/R36/R37 as cited) and `design/ld5/report.sql` both on `main`; code read at `infra-core` `plane/HOMELAB-2195-b3-records` @ `fdb36f2`, path `lab-director/dispatcher`. **Verdict: REQUEST CHANGES** — blocking on #1 and #6. The vocabulary half of the document is exact; the "what W2's path writes" half has two gaps that would hide a NOT_CERTIFIABLE at proof P3, which is the one thing this document exists to prevent. ### Verified good - §4 vocabulary is **verbatim** M0-R42: all twelve rows (`goal.check` verdict and evidence kinds, `escape.kind`, `infra.rerun.cause`/`evidence_kind`, `gate.decision.decision` + `head_sha`/`canary`, `hold.hold_kind`, `accept.test.result`, `ci.live`, `version.probe`, `workflows.status`, probe `ok`/`contains_merge_commit`) match the measurement.md table value-for-value, no invented or dropped value. - §5's per-ticket kind list is M0-R42's list verbatim, including the `version.probe` / `ci.live` exemption. - Code spot-checks that hold: `sign.snapshot` payload keys incl. `plane_activity_id` (`run/loop.go:270-279`); `pickup.position` keys (`loop.go:517-519`); `watch.position` keys `high_water`/`cursor`/`emitted` (`record/watermark.go:35,50-62`); `pane.hold` keys and the readiness-deferral extras (`run/pane.go:183,697-730`); `pane.prompt` carries `at` in the payload (`pane.go:868-882`); `pane.close`/`pane.transcript` keys (`run/panes_close_journal.go:41-49`, `cmd/ld-runtime/main.go:337-338`); `probe.live` declared and unwritten (`record/record.go` `KindProbe`); statuses `queued/running/merged/abandoned/failed` (`record.go`); `pr` = land's and `bump_pr` = the pin repo's (`run/run.go:162-165`, `run_test.go:668-685`); gate keeps `gate.decision` with `state`, not `decision` (`main.go:519-534`). ### Findings 1. **(blocking, P3)** §1's `sample_id` rule does not describe what the step writer does, and §6 does not list the consequence. `recordJournal.Step` hard-codes `SampleID: "runtime"` for **every** step row (`cmd/ld-runtime/main.go:530-547`), including the `gate.decision` row. §1 says `runtime` is for "the runtime's own housekeeping (`pickup.position`, `watch.position`, pane lifecycle in a lane run); the sample id for sample rows" — a gate row is a sample row, and it carries `runtime`. `report.sql` reads gate decisions only under `e.sample_id = (SELECT sample_id FROM current_sample)` (lines ~1017-1019, ~1077, ~1100). So on an official run R1–R4 every gate row is invisible and condition 2 is uncomputable — a NOT_CERTIFIABLE that §6 declares absent. W2 *does* write this row, so §6's framing ("a row `report.sql` reads that no W2 writer produces") misses it. Fix: state the `sample_id` the step writer actually writes in §1/§2, and add a §6 line — "`sample_id` on `path.step` / `gate.decision` is `runtime`, not the sample id; report.sql reads gate rows by sample id" — with an owning wave (B12 or a W2 fix). 2. **(fix, docs accuracy)** §3.1 says `pickup.skipped`'s `kind` ∈ `not-fleet`, `not-ready`. Only `not-fleet` writes that row: `RefusalNotReady` does `res.Skipped++` plus a log line and no journal row (`loop.go:244-249`; `traceSkip` is called only for `RefusalNotFleet`, `loop.go:222-226`). A not-ready item is therefore **silent**, and a not-ready *compile failure* lands as `pickup.rejected` with reason `"not ready: …"` (`loop.go:250-258`). Either say the row covers `not-fleet` only and add the silent-not-ready gap to §6, or record it as a known gap. 3. **(fix, docs accuracy)** §3.1 gives `pickup.rejected` the payload `reason`, `item_id`, `dispatched: false` for every row. The compile-failure path writes `{reason, item_id}` with **no** `dispatched` key (`loop.go:255`); only the unsigned (`loop.go:234`) and attribution-refused (`loop.go:287`) paths write `dispatched: false`. Say "when the row ends a dispatch attempt". 4. **(note, §2 wording)** "Every step row carries: `step`, `state`, `at`, …" reads as payload keys. `at` is the event column; `main.go:530-531` writes only `step` and `state` plus the step's fields. One clause ("`at` is the event's column") removes the ambiguity. 5. **(verified, no change)** §2's claims about the bump row, the gate keeping its own kind, `probe.live` being declared-but-unwritten, and `node_instances` having no W2 writer all check out against `fdb36f2`; §3.1's operator-attribution narrative matches `SignerPolicy.ConfirmAttribution` being applied in `appendEvent` for any operator-actor, with `abandonSlot` on refusal. 6. **(blocking, P3 completeness)** §6 omits report.sql kinds that have no writer and, unlike the rows it does list, also no named owner. Missing from §6 entirely: `canary.outcome` (`{kind,target,bracket,caught}` → M0-R29 recall, report.sql ~1745), `gateway.spend` (`{workflow_id,usd_gateway}` → M0-R37 ±15%, ~1794), `hold.refused` (M0-R24 count, ~246), `sign.sample` (the sample sign and every rebaseline — the earliest `sign.sample` is the report's whole baseline, and it must carry `pin_file=deploy-image.txt` and `auditor_egress=anthropic_only`, report.sql ~125-140, ~438), `drain.start`/`drain.end` (M0-R16, ~248-249) and `failure_review` (M0-R36, ~226-229). Several of those are operator-only kinds per report.sql's own header (`sign.sample`, `drain.start`/`drain.end`, `intervention`, `failure_review`), so they may be deliberately outside the runtime contract — but the document never says who writes them, and §6's own framing ("each line is a row `report.sql` reads that no W2 writer produces") is then incomplete. `canary.outcome` and `gateway.spend` are **not** in the operator-only list, so they are unowned runtime rows. Fix: one line each — owner wave, or "operator/out of runtime scope, written by …" — so proof P3 cannot discover a missing sign or a missing canary row at the gate. 7. **(note, §7)** §7's retention paragraph is consistent with the HOMELAB-2137 ruling and correctly says no sweeper is wired; nothing to change, recorded here so the paragraph is known to have been read rather than skipped. Scope note: this was a read-only review; no files were edited. The unfixed items are document changes for the author, not reviewer edits.
Two contract-doc blockers from reviews 794/795 on infra-core#636.

§1 sample_id — the rule described a writer that did not exist. Every step row
(including ) was written under the literal `runtime`, while
 scopes each CTE to the sample named at its call (M0-R35): a named
run's gate rows were in the database and invisible to the report at P3, with
report.sql/M0 frozen. The row now states what the writer does: step, gate and
infra.rerun rows carry the ticket's sign ; a ticket with no
sign.snapshot row keeps ; a failed sample lookup is an error, never a
silent fallback. Companion writer fix: infra-core `plane/HOMELAB-2195-batch`.

§3.1 pickup.rejected — the payload row lists the keys the loop actually writes,
including the vocabulary split `refusal` (boundary | dor | compile | attribution
| admission) and `transient`, and the two-row shape of a DoR refusal
(`write_state: decided` before any Plane write, then `outcome` with
`missing_writes`). One kind no longer carries three meanings.

§6 — every kind report.sql reads now names its owner instead of being discovered
at P3, verified against report.sql on this branch:
  - operator-only per the report.sql header: sign.sample (and every rebaseline),
    drain.start/drain.end, failure_review, release with `replaces`;
  - W3: hold.refused (the macro lane does not exist yet), canary.outcome (the
    W3/E3 harness plants them), gateway.spend (needs node_instances cost rows).
aaron force-pushed plane/HOMELAB-2195-b3-contracts from b57769cbdd to 88cb21382e 2026-10-25 23:30:10 +00:00 Compare
Author
Owner

Contracts doc fixes (F2 pre-landing round) — new head 88cb213

Two blockers from reviews 794/795 on infra-core#636, in design/ld5/contracts-b11-b12.md.

§1 sample_id — the rule described a writer that did not exist. Every step row
(including gate.decision) was written under the literal runtime, while
report.sql scopes each CTE to the sample named at its call (M0-R35): a named run's
gate rows R1–R4 were in the database and invisible to the report at P3, with
report.sql/M0 frozen. The row now states what the writer does — step, gate and
infra.rerun rows carry the ticket's sign.snapshot sample; a ticket with no
snapshot row keeps runtime; a failed sample lookup is an error on the write, never
a silent fallback. Companion writer fix: infra-core plane/HOMELAB-2195-batch
@ e507450 (that PR carries the read trace for LastStep, InfraRuns and
PoolInflight, the three reads that filtered on sample).

§6 — every kind report.sql reads now names its owner, verified against
report.sql on this branch. Added rows:

kind owner
sign.sample (and every rebaseline) operator-only per the report.sql header (M0-R31)
drain.start / drain.end operator-only per the report.sql header (M0-R31) — and explicitly NOT written by B2's runtime levers
failure_review operator-only per the report.sql header (M0-R24)
release with replaces operator-only per the report.sql header (M0-R31)
hold.refused W3 (the macro lane does not exist yet; the landing hold is B12)
canary.outcome W3 (the canaries are planted by the W3/E3 harness)
gateway.spend W3 (needs the node_instances cost rows first)

§3.1 pickup.rejected — the payload row now lists the keys the loop actually
writes, including the vocabulary split added in the code fix (refusal ∈ boundary |
dor | compile | attribution | admission, and transient), and the two-row shape of a
DoR refusal (write_state: decided before any Plane write, then outcome with
missing_writes). One kind no longer carries three meanings.

Disclosure: the branch was force-pushed once to correct a mangled commit message
(backticks were shell-expanded, leaving gaps mid-sentence in the commit body). The
content of the change is unchanged between the two pushes; the tree at 88cb213 is
the fix as described. No review is pinned to this head yet — this comment is the
record.

## Contracts doc fixes (F2 pre-landing round) — new head `88cb213` Two blockers from reviews 794/795 on infra-core#636, in `design/ld5/contracts-b11-b12.md`. **§1 `sample_id` — the rule described a writer that did not exist.** Every step row (including `gate.decision`) was written under the literal `runtime`, while `report.sql` scopes each CTE to the sample named at its call (M0-R35): a named run's gate rows R1–R4 were in the database and invisible to the report at P3, with report.sql/M0 frozen. The row now states what the writer does — step, gate and `infra.rerun` rows carry the ticket's `sign.snapshot` sample; a ticket with no snapshot row keeps `runtime`; a failed sample lookup is an error on the write, never a silent fallback. Companion writer fix: infra-core `plane/HOMELAB-2195-batch` @ `e507450` (that PR carries the read trace for `LastStep`, `InfraRuns` and `PoolInflight`, the three reads that filtered on sample). **§6 — every kind `report.sql` reads now names its owner**, verified against report.sql on this branch. Added rows: | kind | owner | |---|---| | `sign.sample` (and every rebaseline) | operator-only per the report.sql header (M0-R31) | | `drain.start` / `drain.end` | operator-only per the report.sql header (M0-R31) — and explicitly NOT written by B2's runtime levers | | `failure_review` | operator-only per the report.sql header (M0-R24) | | `release` with `replaces` | operator-only per the report.sql header (M0-R31) | | `hold.refused` | W3 (the macro lane does not exist yet; the landing hold is B12) | | `canary.outcome` | W3 (the canaries are planted by the W3/E3 harness) | | `gateway.spend` | W3 (needs the `node_instances` cost rows first) | **§3.1 `pickup.rejected`** — the payload row now lists the keys the loop actually writes, including the vocabulary split added in the code fix (`refusal` ∈ boundary | dor | compile | attribution | admission, and `transient`), and the two-row shape of a DoR refusal (`write_state: decided` before any Plane write, then `outcome` with `missing_writes`). One kind no longer carries three meanings. **Disclosure:** the branch was force-pushed once to correct a mangled commit message (backticks were shell-expanded, leaving gaps mid-sentence in the commit body). The content of the change is unchanged between the two pushes; the tree at `88cb213` is the fix as described. No review is pinned to this head yet — this comment is the record.
Collaborator

Review — design/ld5/contracts-b11-b12.md @ 88cb213 — VERDICT: APPROVED

Transport note. This verdict was authored by the delta reviewer (fresh context, round-2 delta review) and is posted here unchanged by the landing worker, because ld-reviewer could not reach this repo when the review ran (aaron/lab-director returned 404 for that identity). Read access has since been granted (ld-reviewer, collaborator read role, settings-only change by aaron). The review and the verdict are the reviewer's; only the transport is the landing worker's.

Scope verified

§1 sample_id — matches the writer exactly (writer at infra-core plane/HOMELAB-2195-b3-records @ e507450):

  • the newest sign.snapshot row is the single resolution point — it scopes the step row (path.step and gate.decision), RecordInfra, InfraRuns and both LastStep reads;
  • a sample lookup that fails is an error on the write, never a silent fall back to runtime;
  • runtime fallback applies only when the ticket has no sign.snapshot row (lane runs, manual runs, tests);
  • InFlight reads through ListKindAnySample, and its test asserts the single-sample read is empty as a control.

§6 — owns every kind report.sql reads. I enumerated all 24 kinds the query reads (panel_only_defect is a canary_kind value, not an event kind) and each one is either owned in §2/§3 by a W2 writer or explicitly named in §6 with an owning wave. Nothing report.sql reads is left unowned and unstated.

Findings carried (non-blocking; pre-R3 / W3 follow-ups, not gates)

  • C1 — the journal prior does not persist Verdict.Marker(), so a later refusal of the same ticket for different reasons is demoted without a new comment: the journal dedup is broader than the marker. Fix is one payload key plus a comparison in priorRefusal. (Code lives on infra-core #636, not in this doc PR.)
  • C2 — a lane/manual re-drive of an already-signed ticket now files its step rows inside that ticket's sample scope; contract §1 could say so explicitly.

Shape

Docs-only: 1 file, +187 / −0 (design/ld5/contracts-b11-b12.md). No CI workflow exists in aaron/lab-director, so no check was expected or waited on.

Verdict: APPROVED on head 88cb213.

## Review — `design/ld5/contracts-b11-b12.md` @ `88cb213` — **VERDICT: APPROVED** > **Transport note.** This verdict was authored by the delta reviewer (fresh context, round-2 delta review) and is posted here **unchanged** by the landing worker, because `ld-reviewer` could not reach this repo when the review ran (`aaron/lab-director` returned 404 for that identity). Read access has since been granted (`ld-reviewer`, collaborator read role, settings-only change by `aaron`). The review and the verdict are the reviewer's; only the transport is the landing worker's. ### Scope verified **§1 `sample_id` — matches the writer exactly** (writer at `infra-core` `plane/HOMELAB-2195-b3-records` @ `e507450`): - the newest `sign.snapshot` row is the single resolution point — it scopes the step row (`path.step` and `gate.decision`), `RecordInfra`, `InfraRuns` and both `LastStep` reads; - a sample lookup that **fails** is an error on the write, never a silent fall back to `runtime`; - `runtime` fallback applies only when the ticket has **no** `sign.snapshot` row (lane runs, manual runs, tests); - `InFlight` reads through `ListKindAnySample`, and its test asserts the single-sample read is empty as a control. **§6 — owns every kind `report.sql` reads.** I enumerated all **24** kinds the query reads (`panel_only_defect` is a `canary_kind` **value**, not an event kind) and each one is either owned in §2/§3 by a W2 writer or explicitly named in §6 with an owning wave. Nothing `report.sql` reads is left unowned and unstated. ### Findings carried (non-blocking; pre-R3 / W3 follow-ups, not gates) - **C1** — the journal `prior` does not persist `Verdict.Marker()`, so a later refusal of the **same** ticket for **different** reasons is demoted without a new comment: the journal dedup is broader than the marker. Fix is one payload key plus a comparison in `priorRefusal`. (Code lives on `infra-core` `#636`, not in this doc PR.) - **C2** — a lane/manual re-drive of an already-signed ticket now files its step rows inside that ticket's sample scope; contract §1 could say so explicitly. ### Shape Docs-only: 1 file, `+187 / −0` (`design/ld5/contracts-b11-b12.md`). No CI workflow exists in `aaron/lab-director`, so no check was expected or waited on. **Verdict: APPROVED on head `88cb213`.**
ld-reviewer left a comment
Collaborator

APPROVED on 88cb213 — see the full verdict comment below (transported by the landing worker).

APPROVED on 88cb213 — see the full verdict comment below (transported by the landing worker).
aaron merged commit a451083e98 into main 2026-10-26 00:21:46 +00:00
aaron deleted branch plane/HOMELAB-2195-b3-contracts 2026-10-26 00:21:49 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aaron/lab-director!23
No description provided.