HOMELAB-2329: contracts-b11-b12 correction — HOMELAB-2339 base + gate-writer accuracy delta (superset of #24) #26
No reviewers
Labels
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
aaron/lab-director!26
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "plane/HOMELAB-2329-b11-b12"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
HOMELAB-2329 contract-prerequisite docs for the HOMELAB-2196 W3 official runs. Superset of draft #24 — land one, not both.
Delta vs #24 (
4f71ed9, HOMELAB-2339 base correction)This branch carries HOMELAB-2339's correction verbatim (commit
3da37b9, authorship preserved), plus the HOMELAB-2329 accuracy delta (61e1ab9, 3 edits, +4/−2) written against the re-confirmed code head8294a17dde17e86997e8c9d8c3701eebba98e5d3(infra-core #660):Revisedheader re-read at8294a17(was pinned to the supersededd17c575).recordJournal.Steponly.run/loop.gostepEventKindreturnsKindGatefor the gate in any state andcloseOrphanMidStepwrites the orphaned gate's terminalfailedrow (cause: runtime-replaced,orphan: true, nodecision) throughLoop.appendEvent→Store.Append, bypassingrecord.ValidGateDecision— with the follow-up owner stated (give itblockviarun.GateDecisionForState, or write it underpath.step, and widen the check to every gate-row writer). This is the open concern from infra-core review 1013.Disposition of #24
#24 (
plane/HOMELAB-2339-gate-writer-contract@4f71ed9) covers only the base correction and carries anld-reviewerAPPROVED verdict at that head. This PR is a superset; one should land and the other be closed — merging both puts two conflicting hunks on the same paragraph. This PR's head61e1ab9is a delta review (3 edits over the approved text), not a new round.Checks
Document-only change. Content checked 20/20 against primary sources (
run.GateDecisionForState,record.ValidGateDecision+ the 3-word frozen set,recordJournal.Stepboth directions,stepEventKind/closeOrphanMidStep/appendEvent/Store.Append, frozenreport.sqlvocabulary arm andgate_on_head), plus markdown shape (pipe balance, no trailing whitespace, valid UTF-8). Script/log:.rpiv/artifacts/build/homelab-2329-contracts/doc-check.sh/doc-check.log(fails=0).Not done here: no merge, no code change, no signature, no image, no drive, no close of #24 (orchestrator disposition).
Formal review — tests lens (DeepSeek), delta at head
61e1ab9(ld-reviewer)Verdict: REQUEST_CHANGES — one accuracy finding in the delta (additive: one §2 bullet + one §6 row), plus one quoted-literal slip. Everything else in the delta verifies against primary source.
Ticket HOMELAB-2329 (
https://plane.aaron.reynoza.org/homelab/projects/…/issues/2329— W3 carry-over, gate.decision rows record decision for report.sql), with its doc sibling HOMELAB-2339. Pinned to head61e1ab9cd60ed8d183da467842512511c4e5b345; if the head moves this verdict does not carry.Scope. This is the delta over HOMELAB-2339's text (
3da37b9), not a new round on the base correction. I read the exact-head diff, the head'sdesign/ld5/contracts-b11-b12.mdanddesign/ld5/report.sql,infra-core@8294a17dde17e86997e8c9d8c3701eebba98e5d3(PR #660, merged tolive), PR #24's diff and its verdict, and HOMELAB-2329 / HOMELAB-2339. No implementer summary was used.No CI in this repo, and that is checked, not assumed:
GET /commits/61e1ab9…/status→total_count: 0;contents/.forgejo@61e1ab9→404. Nothing here is green; this verdict rests on the diff and on the code/report it points at. Head independently confirmed via the API as61e1ab9…, branchplane/HOMELAB-2329-b11-b12, basemain@a451083(= today'smainhead).Delta claims — verified at primary source
path.step, terminal rowgate.decision.cmd/ld-runtime/main.gostepKind(step,state)returnsKindGateonly forStepGateandrun.TerminalState(state), elseKindStep;outcomeKind(step)returnsKindGatefor the gate regardless of state (the resume side). Both rows go throughrecordJournal.Step. CONFIRMED.ok→pass,failed→block,refused→hold.run.GateDecisionForState(run/run.go:133) is exactly that mapping, andp.step's deferred writer stampsfields["decision"]forstep == run.StepGate. CONFIRMED.recordJournal.Step's alone, both directions. InStep,kind := stepKind(...); a gate row destined forKindStep(the start row) is refused if it carries anydecision, and a gate row destined forKindGateis refused if the decision is absent, blank (TrimSpace == "") or outside the frozen set (record.ValidGateDecision, whose map is exactlypass/block/hold). The "fail-closed, the same wayworkflows.statusis checked atOpen/Close" analogy is accurate:record.go:367and:415callValidStatus. CONFIRMED.run/loop.gostepEventKind(step)returnsrecord.KindGateforStepGatewhatever the state;closeOrphanMidStepbuilds{step, state: failed, reason, cause, orphan: true, workflow_id, sign_activity_id, started_at, reset, at}— nodecision— and writes it throughLoop.appendEvent→Store.Append;appendEventinjects nothing. The orphan scan lists bothKindStepandKindGate, so after this change the running gate row (nowpath.step) is still closed undergate.decision. CONFIRMED (literal cause string: see C1).report.sql's state-less arm makes a decisionless gate rowNOT_CERTIFIABLE. The vocabulary arm'sgate.decision.decisionbranch selects onkind,sample_idandid > sample_event_idonly — nostatefilter anywhere — and emitsCOALESCE(lower(payload->>'decision'), '(missing)')for rows where the decision is NULL or outsidepass|block|hold; M0-R42 makes any vocabulary rowNOT_CERTIFIABLE. The same read is pinned as a negative control ingate_decision_test.go(reportVocabularyOffenderson a decisionless gate row →(missing)). CONFIRMED.decisionyes;floor_met/round/first_verdict/holds[]/canaryno. No non-test occurrence of those four keys as payload values anywhere indispatcher/(the onlyfloor_methits are a doc comment ongate_on_headand a phrase inrun/forgejo.go);canaryappears only in unrelated docs/config. CONFIRMED.gate_on_headisfloor_met AND lower(decision) = 'pass'. The CTE isbool_or(COALESCE((payload->>'floor_met')::boolean, FALSE) AND lower(payload->>'decision') = 'pass'), so a passing decision with nofloor_metreadsFALSE;gate_firstorders by(payload->>'round')::int,COALESCE(first_verdict,'unknown'),COALESCE(canary, FALSE)— matching M0-R30 and M0-R29. CONFIRMED.LastStep→outcomeKind(step)→KindGateonly for the gate, so the gate's start row is invisible to resume and a half-run gate is re-attempted; the gate reads branch/diff/CI and merges nothing, and no other step'soutcomeKindchanges. CONFIRMED.Supersession of #24 — clean.
pulls/24.diffand commit3da37b9's patch are byte-identical (10101 bytes each), both branches share basea451083, which is stillmain's head, so landing both would put two edits on the same paragraph and the second merge conflicts. #24 does carry the APPROVEDld-reviewerverdict at4f71ed9(as its verdict comment; the review object itself isPENDING, which is the comment-as-verdict pattern) — the PR body's statement is accurate. Land one, close the other. Supports the official #660 drive: #660 is merged at8294a17intolive, reviews 1013 (deepseek, APPROVED) and 1015 (kimi premium, APPROVED) both exist at that head, and the delta'sRevisedheader re-pins to it correctly.F1 — the exhaustiveness claim in §2/§6 misses the second site the write check runs over, and that site is broken by it
Where. §2: "That check lives in
recordJournal.Step… and it is not the only writer of gate rows — see the next bullet." / "One writer goes round that check, and this is the gate's remaining decisionless path (open, HOMELAB-2329)." §6: "the one gate-row writer the §2 write-time check does not cover".What is missing.
Path.refusedRow(run/run.go:886-895) is the second caller ofrecordJournal.Stepforgate(the third,stepboundary.go:421, isStepBoundary). It is invoked from the replayed-evidence refusal atrun/run.go:688with the loop's current step — and the loop walksrun.Steps, which containsStepGate(run.go:58). Its fields are{reason, refusal, branch, replayed_head, branch_head, reset, at}: nodecision, andstateisStateRefused, sostepKindsends it torecord.KindGate, andrecordJournal.Stepreturnsjournal …: refusing to write a gate.decision row with no 'decision'.Consequence at the reviewed head. A gate replay refusal writes no row at all and returns
run: … the replay refusal could not be journalled: …. That is precisely the absence HOMELAB-2348 was filed to remove ("the refusal is a row, not an absence"), and it is the shape the code itself names two paragraphs above the call site: after a reset that deleted thepath.steprows, the gate row is the only one carrying ahead_sha, so the gate is the first step the replay check lands on.refusedRow's own doc comment reinforces the exposure — it deliberately stamps nohead_sha"so a refusal must not feed its own trigger" — but nothing gives the gate row its word.Why this is a finding on this delta and not on #660. #660 traded a decisionless refusal row (which the report flags
(missing)) for no refusal row plus an error; either way the gate refusal path is outside the contract, and this document's job — stated in its own §6 preamble — is "Stated here rather than discovered at proof P3". The delta records the weaker of the two uncovered paths (the orphan row is at least written) and presents the census as complete, so the stronger one stays invisible to the P3 reviewer. The §6 follow-up ("widen the write-time check to every gate-row writer") names the remedy but not the site.Requested edit (small, additive). One §2 bullet (or a sentence in the
gate.decision-terminal bullet) namingPath.refusedRowas the otherrecordJournal.Stepcaller for the gate, stating that itsrefusedwrite now has nodecisionand is therefore refused outright; and a §6 row with the same owner: give that row its word viarun.GateDecisionForState(run.StateRefused)→hold(or write it underpath.step), and widen the check to every gate-row writer.Test gap that goes with it.
run/replay_refusal_test.godrivesw.journal(a fake), so no test at8294a17exercisesrefusedRowthrough the realrecordJournal.Step. The doc sentence should not imply the refusal path is validated.C1 — one quoted value is not the literal
§2 (and the §6 condition) write the orphan payload as
cause: runtime-replaced. The literal isruntime-replaced-mid-step(RecoveryCauseRuntimeReplaced,run/loop.go:923). The Authority row says this document "does not invent values", so quote the literal or writecause: runtime-replaced-mid-step.Limits
Read/comment/review only. I edited nothing, committed nothing, pushed nothing, merged nothing, signed nothing, produced no image, ran no drive, and did not touch #24 or its verdict. Reviewer ≠ implementer: this pass did not inherit the implementer's context and worked only from the sources named above.
61e1ab9cd6: APPROVE** model: omniroute/deepseek-flash lens: W3 lane reviewer (DeepSeek tests/claims lens) — this is one lens; the worker's sequence also asks for a premium delta lens, so if a two-lens panel is treated as required this is one of the two, not the panel. class: **docs only** — one file, `design/ld5/contracts-b11-b12.md`, +14/−4 against `main`. No code, manifest, RBAC, secret, network policy, schema or migration. What makes it reviewable as more than prose is that this document is the one the runtime is reviewed against, so **every claim in the delta is asserted against the code, not read**. ## What the PR is, checked structurally | fact | measured | |---|---| | files changed | `design/ld5/contracts-b11-b12.md`, +14/−4, `changed_files=1` | | commits | `3da37b9842` (HOMELAB-2339 base correction) + `61e1ab9cd6` (HOMELAB-2329 amendment) | | base | `main` = `a451083e98` | | the base correction is carried **verbatim**, not re-authored | blob of `contracts-b11-b12.md` at **`3da37b98420c` == at `4f71ed93f78d`** (#24's head) = `211152e2…`, 24,149 B — byte-identical | | the amendment is exactly the stated 3 edits | `diff3da37b9→61e1ab9` touches the `Revised` header row, one bullet in §2 (extended) plus one **new** §2 bullet, and one new §6 table row. Nothing else in 198 lines moved. | | doc shape | valid UTF-8, 198 lines, **0** conflict markers, **0** trailing-whitespace lines, **0** unbalanced table rows, 0 tabs | So the PR is precisely what its body says: #24's already-approved text plus a three-edit accuracy delta. My review is of the **delta**, and it re-confirms the base rather than re-litigating it. ## The amendment's claims, each asserted against the code Verified at `infra-core` **`0ffea3e397708607fc558e0f00acd972ec1ed994`** — the `#660` merge commit, i.e. what the live runtime now runs (prod pin `dispatcher@sha256:66ef87f1…`, see prod#1045) — and not merely at the pre-merge head the doc cites: 1. **"`run.GateDecisionForState`: `ok` → `pass`, `failed` → `block`, `refused` → `hold`"** — `run/run.go:133-143`, exactly those three arms and `("", false)` otherwise. ✔ 2. **"validated at the write by `record.ValidGateDecision`, which refuses a `gate.decision` row whose decision is missing, blank or outside the set"** — `record/record.go:213-240`: the constants, `gateDecisionVocabulary = pass|block|hold`, `ValidGateDecision`. The literal `"pass"` is present (the redactor flags that line as a password on this repo — it is a false positive, as the third shift recorded). ✔ 3. **"That check lives in `recordJournal.Step` … guarded by its own `stepKind`"** — `cmd/ld-runtime/main.go:1007-1042`: `kind := stepKind(step, state)`, and for `run.StepGate` both directions are refused — a `decision` on a start row (`kind == KindStep`) and a missing/blank/out-of-vocabulary `decision` on a `gate.decision` row. `stepKind` (`:962-970`) returns `KindGate` only when `run.TerminalState(state)`, and `TerminalState` (`run/run.go:111-117`) is `ok|failed|refused` — `running` is excluded. ✔ 4. **"`stepEventKind` returns `record.KindGate` for `run.StepGate` whatever the state"** — `run/loop.go:1031-1036`. It takes **no state parameter at all**, which is a stronger statement of the same fact. ✔ 5. **"`closeOrphanMidStep`'s payload carries `state`, `cause: runtime-replaced` and `orphan: true` with **no `decision`**"** — `run/loop.go:1190-1212`: payload keys are `step`, `state: StateFailed`, `reason`, `cause`, `orphan: true`, `workflow_id`, `sign_activity_id`, `started_at`, `reset`, `at`. **No `decision` key.** Written via `l.appendEvent` (`:1201`) → `l.Store.Append` (`:1559`), bypassing `recordJournal.Step`. ✔ — **this is the one claim in the delta that is load-bearing for the ticket, and it is true.** 6. **"`report.sql`'s state-less vocabulary arm reads it as `(missing)`, … no `state` filter"** — `design/ld5/report.sql` at this head: `:1991-1996`, `SELECT 'gate.decision.decision', COALESCE(lower(e.payload->>'decision'), '(missing)') … WHERE e.kind = 'gate.decision' … AND (payload->>'decision' IS NULL OR lower(...) NOT IN ('pass','block','hold'))`. No `state` predicate anywhere in the arm. ✔ 7. **"`gate_on_head` (`report.sql`) is `floor_met AND lower(decision) = 'pass'`"** — `:1069-1080`, `bool_or(COALESCE((e.payload->>'floor_met')::boolean, FALSE) AND lower(e.payload->>'decision') = 'pass')`. With `floor_met` absent the read is FALSE, so a passing decision cannot satisfy condition 2 on its own — exactly what the §6 row now says. ✔ 8. **"`gate_first` also reads `round` for its ordering, `first_verdict` as `unknown` (M0-R30) and `canary` as `false` (M0-R29)"** — `:1005-1020`: `ORDER BY … (e.payload->>'round')::int ASC NULLS LAST`, `COALESCE(payload->>'first_verdict','unknown')`, `COALESCE((payload->>'canary')::boolean, FALSE)`. ✔ 9. **"`floor_met`, `round`, `first_verdict`, `holds[]`, `canary`: no — no writer produces them"** — grepped the four writer files at `0ffea3e` (`run/run.go`, `run/loop.go`, `cmd/ld-runtime/main.go`, `record/record.go`): `first_verdict` 0, `canary` 0, `floor_met` **1 hit and it is a comment** (`record/record.go:193`, describing `gate_on_head`). No writer. ✔ 10. **"Fail-closed, the same way `workflows.status` is checked at `Open`/`Close`"** — `record/record.go:411-414`: `Close` refuses a status outside `statusVocabulary`; `Open` applies the same frozen set. The analogy is exact. ✔ ## Findings **C1 (non-blocking, worth a one-word fix before this lands).** The new §2 bullet writes the orphan row's cause as `cause: runtime-replaced`. The constant is **`RecoveryCauseRuntimeReplaced = "runtime-replaced-mid-step"`** (`run/loop.go:923`), and that is the exact string the journal carries. `runtime-replaced` is a prefix, so a reader grepping it still finds the row — which is why this is a concern and not a blocker — but this is the document the runtime is *reviewed against*, and everywhere else in it a payload value is quoted exactly. Recommend `cause: runtime-replaced-mid-step`. **C2 (recorded, not mine to fix).** The bullet calls the restart pass's row "the gate's remaining decisionless path". Please do not read that as "the last one": it is the only **gate-row** writer outside `recordJournal.Step` *today*, and the same class of gap exists for any future writer to `gate.decision`. `report.sql`'s arm will catch it (that is its merit), but the guard is at one writer, not at the kind. Widening the invariant to every writer is already the §6 owner line's second half — keeping that phrasing in the follow-up brief would help. **C3 (not a PR finding, but the reason this PR matters).** The gap it records is live on the image prod#1045 just pinned: `stepEventKind` has no state parameter, so if the runtime pod is replaced while a gate is mid-attempt the row lands under `gate.decision` without a `decision`, and the sample prints `NOT_CERTIFIABLE` regardless of the next attempt. That is not introduced by this PR — it is what this PR makes impossible to discover late. ## Why APPROVE The delta does what it claims, the claim that matters is true in the code that is deployed, the base is carried byte-identically from the already-approved #24 text, the document is well-formed, and the one inaccuracy I found is a payload literal whose prefix still matches. Nothing in the delta changes M0 or `report.sql` semantics, and no verification claim is made that I could not re-derive. **What this approval does not do:** it does not merge anything, does not close HOMELAB-2329, does not evidence the live gate proof (the W3 integration worker's scratch drive), and does not substitute for the premium delta lens if the panel is treated as two-lens. `#24` and `#26` are mutually exclusive — land one and close the other; merging both puts two conflicting hunks on the same paragraph. No files edited, committed or pushed by me. No merge, sign, image, drive or DB write.61e1ab9cd6: REQUEST_CHANGES** model: omniroute/deepseek-flash lens: W3 lane reviewer (DeepSeek claims/tests lens). One additive edit is owed. Everything else in the delta verifies. ## Chronology of this head, stated rather than tidied | time (UTC) | review | what happened | |---|---|---| | 09:27Z | — | I read `#26` as `ld-reviewer`: head `61e1ab9…`, **0 reviews, 0 comments**. Started the review from primary sources. | | **09:30:06Z** | **1017** `REQUEST_CHANGES`, 9,479 chars | posted by another `ld-reviewer` session while I was composing — **the same identity key as mine**. It found the blocker below. I had not seen it: my last read of the reviews list was 3 minutes earlier. | | **09:32:51Z** | **1018** `APPROVED`, 8,032 chars | mine. **Forgejo dismissed 1017 when I posted it** (`dismissed=true`), which is the same-key behaviour this lane has measured all night. **That dismissal was not a decision I made** — I never saw 1017 before posting, and I would not have approved over it. | | **09:44Z** | **this review** | after posting 1018 I read 1017, re-derived its finding myself **from the code**, and it is true. My approval is wrong on one claim and I am correcting it here rather than leaving a dismissed hold and a live approval standing on a head whose census is incomplete. | I cannot edit or delete 1018, and I have not: it stands, dismissed by this row, with its error visible in the record. What follows is the verdict of record. ## The claim that decided it (verified independently, not inherited from 1017) The delta says, twice, that the restart pass is the **one** gate-row writer outside `recordJournal.Step`: - §2: *"That check lives in `recordJournal.Step` … and it is **not** the only writer of gate rows — see the next bullet"* / *"**One writer goes round that check, and this is the gate's remaining decisionless path**"* - §6: *"the one gate-row writer the §2 write-time check does not cover"* **There is a second site, and it is not a writer that "goes round" the check — it is one the check now refuses outright.** - `Path.refusedRow` (`run/run.go:886-895`) calls `p.opt.Journal.Step(ctx, t.Identifier, step, StateRefused, fields)` — a **second** caller of `recordJournal.Step` with an arbitrary `step`. - Its only call site is the replayed-evidence refusal (`run/run.go:688`), invoked for the step whose journal row carried a stale `head_sha`, and the loop walks `run.Steps`, which **contains `StepGate`** (`run/run.go:58`). - In `recordJournal.Step`: `kind := stepKind("gate", "refused")` → `run.TerminalState("refused")` is true → **`record.KindGate`**; its fields are `{reason, refusal, branch, replayed_head, branch_head, reset, at}` — **no `decision`** → the check at `cmd/ld-runtime/main.go:1025-1028` refuses the write. - Consequence at the reviewed head: **a gate replay refusal writes no row at all** and returns `run: … the replay refusal could not be journalled: …`. That is precisely the absence HOMELAB-2348 exists to remove, and the code's own comment two paragraphs above the call site documents the live LDF-41 case it was filed for. - Reachability is structural, not hypothetical: a `gate` terminal row carries `head_sha` (it is written through `p.journal`, which merges `t.evidence()` — `run/run.go:858-870`), and a reset deletes `path.step` rows but not the `gate.decision` row, so **the gate is structurally the first step a stale head lands on**. The `run/run.go:645-660` comment says exactly this ("a reset deleted LDF-15's `path.step` rows but not its gate row — the gate keeps its own event kind"). - No test at this head exercises `refusedRow` through the real `recordJournal.Step` (`run/replay_refusal_test.go` drives a fake journal), so nothing catches it. This is a defect **in #660, already merged and already pinned live** (prod#1045 → `dispatcher@sha256:66ef87f1…`, verified). It is not introduced by this doc PR. But this document is the one the runtime is reviewed against, its §6 preamble is *"Stated here rather than discovered at proof P3"*, and the delta's most load-bearing new sentence is a **census claim that is now wrong**. At P3 a reviewer holding this document would look for one uncovered path and find the other one missing. ## The requested edit (small and additive — this is why it is not a rewritten delta) 1. In §2 (the new bullet, or a sentence in the terminal-row bullet): name **`Path.refusedRow`** as the other `recordJournal.Step` caller for the gate, and state that a `refused` gate write now carries no `decision` and is therefore **refused outright — the refusal row is not written at all**. That is worse than the orphan row it currently records, and the document should say so. 2. In §6, add a row with the same owner: give that row its word (`run.GateDecisionForState(run.StateRefused)` → `hold`) or write it under `path.step`, and widen the write-time check to every gate-row writer. The existing §6 row's remedy ("widen the check to every gate-row writer") is right but names no site. 3. Do not imply the refusal path is validated: no test drives it through the real journal. **C1 (also owed, one literal).** §2 writes the orphan row's cause as `cause: runtime-replaced`. The constant is `RecoveryCauseRuntimeReplaced = "runtime-replaced-mid-step"` (`run/loop.go:923`), and that is the string the journal carries. The Authority row says this document "does not invent values" — quote the literal. ## Everything else in the delta verified at primary source (so the next pass is a delta check, not a re-review) Checked at `infra-core` **`0ffea3e397708607fc558e0f00acd972ec1ed994`** — the `#660` merge commit, i.e. what the live runtime actually runs, not the pre-merge head the doc cites: 1. `run.GateDecisionForState`: `ok→pass`, `failed→block`, `refused→hold` — `run/run.go:133-143`. ✔ 2. `record.ValidGateDecision` and the frozen `pass|block|hold` set (`record/record.go:213-240`); the literal `GatePass = "pass"` is present — the redactor flags that line as a password, a known false positive on this repo. ✔ 3. Both directions refused in `recordJournal.Step` (`cmd/ld-runtime/main.go:1007-1042`), gated by `stepKind` (`:962-970`) on `run.TerminalState` (`run/run.go:111-117`, which excludes `running`). ✔ 4. `stepEventKind` (`run/loop.go:1031-1036`) returns `KindGate` for the gate — it has **no state parameter at all**, a stronger version of the doc's wording. ✔ 5. `closeOrphanMidStep` (`run/loop.go:1190-1212`): payload has `state`, `cause`, `orphan: true` and **no `decision`**; written via `appendEvent` → `Store.Append` (`:1201`, `:1559`), bypassing the check. ✔ 6. `report.sql`'s vocabulary arm (`:1991-1996`) has **no `state` filter** and `COALESCE(lower(payload->>'decision'), '(missing)')`. ✔ 7. `gate_on_head` is `bool_or(COALESCE(payload->>'floor_met',FALSE)::boolean AND lower(payload->>'decision')='pass')` (`:1069-1080`) — a passing decision without `floor_met` reads FALSE, as the §6 row now says. ✔ 8. `gate_first` orders by `(payload->>'round')::int`, `COALESCE(first_verdict,'unknown')`, `COALESCE(canary,FALSE)` (`:1005-1020`) — matches M0-R30 / M0-R29. ✔ 9. §4's "`floor_met`/`round`/`first_verdict`/`holds[]`/`canary`: no writer" — grepped the four writer files at `0ffea3e`: `first_verdict` 0, `canary` 0, `floor_met` 1 hit and it is a **comment** (`record/record.go:193`). ✔ 10. The `workflows.status` analogy is exact: `Close` refuses a status outside `statusVocabulary` (`record/record.go:411-414`), `Open` applies the same set. ✔ 11. Structure: one file, +14/−4, `changed_files=1`; blob at `3da37b9842` **== at #24's head `4f71ed93f78d`** (`211152e2…`, 24,149 B), so the base correction is carried **verbatim**; the amendment is exactly 3 edits (`Revised` row, §2 bullet + new bullet, §6 row); valid UTF-8, 198 lines, 0 conflict markers, 0 trailing-whitespace lines, 0 unbalanced table rows. ✔ ## Standing facts the landing step still needs - `#24` and `#26` are **mutually exclusive** — land one, close the other; both is a conflict on the same paragraph. - No CI exists in this repo (`/commits/61e1ab9…/status` → 0 statuses; no `.forgejo/` at the head), so nothing here is "green" — this verdict rests on the diff and on the code it points at. - This is **one lens**. The premium delta lens is still owed if the panel is treated as two-lens; my row does not stand in for it. - Nothing here merges, signs, images or drives anything, and no Verify line is ticked by this review. No files edited, committed or pushed by me; nothing merged; no old review or comment edited or deleted.Verdict of record — ld-reviewer — head
61e1ab9cd6: REQUEST_CHANGESFormal review: 1019 (
REQUEST_CHANGES,commit_id=61e1ab9cd60ed8d183da467842512511c4e5b345, body 8,732 chars, GET-verified visible,dismissed=false,stale=false,official=false— this account is read-only onaaron/lab-director, so the visible row and this comment are the record; the flag is reported as it is).pins_full_head=true.Chronology on this head, so nobody has to reconstruct it: 1017
REQUEST_CHANGES(09:30:06Z, anotherld-reviewersession, the same identity key) → 1018APPROVED(09:32:51Z, mine, posted without having seen 1017; Forgejo dismissed 1017 on it) → 1019REQUEST_CHANGES(09:34:21Z, mine, after re-deriving 1017's finding from the code and confirming it; 1018 now readsdismissed=true). No review or comment was edited or deleted; my wrong one is left visible and superseded. The full reasoning is in 1019, not summarised here.model: omniroute/deepseek-flash
lens: W3 lane reviewer (DeepSeek claims/tests lens). One additive edit is owed. Everything else in the delta verifies.
Chronology of this head, stated rather than tidied
#26asld-reviewer: head61e1ab9…, 0 reviews, 0 comments. Started the review from primary sources.REQUEST_CHANGES, 9,479 charsld-reviewersession while I was composing — the same identity key as mine. It found the blocker below. I had not seen it: my last read of the reviews list was 3 minutes earlier.APPROVED, 8,032 charsdismissed=true), which is the same-key behaviour this lane has measured all night. That dismissal was not a decision I made — I never saw 1017 before posting, and I would not have approved over it.I cannot edit or delete 1018, and I have not: it stands, dismissed by this row, with its error visible in the record. What follows is the verdict of record.
The claim that decided it (verified independently, not inherited from 1017)
The delta says, twice, that the restart pass is the one gate-row writer outside
recordJournal.Step:recordJournal.Step… and it is not the only writer of gate rows — see the next bullet" / "One writer goes round that check, and this is the gate's remaining decisionless path"There is a second site, and it is not a writer that "goes round" the check — it is one the check now refuses outright.
Path.refusedRow(run/run.go:886-895) callsp.opt.Journal.Step(ctx, t.Identifier, step, StateRefused, fields)— a second caller ofrecordJournal.Stepwith an arbitrarystep.run/run.go:688), invoked for the step whose journal row carried a stalehead_sha, and the loop walksrun.Steps, which containsStepGate(run/run.go:58).recordJournal.Step:kind := stepKind("gate", "refused")→run.TerminalState("refused")is true →record.KindGate; its fields are{reason, refusal, branch, replayed_head, branch_head, reset, at}— nodecision→ the check atcmd/ld-runtime/main.go:1025-1028refuses the write.run: … the replay refusal could not be journalled: …. That is precisely the absence HOMELAB-2348 exists to remove, and the code's own comment two paragraphs above the call site documents the live LDF-41 case it was filed for.gateterminal row carrieshead_sha(it is written throughp.journal, which mergest.evidence()—run/run.go:858-870), and a reset deletespath.steprows but not thegate.decisionrow, so the gate is structurally the first step a stale head lands on. Therun/run.go:645-660comment says exactly this ("a reset deleted LDF-15'spath.steprows but not its gate row — the gate keeps its own event kind").refusedRowthrough the realrecordJournal.Step(run/replay_refusal_test.godrives a fake journal), so nothing catches it.This is a defect in #660, already merged and already pinned live (prod#1045 →
dispatcher@sha256:66ef87f1…, verified). It is not introduced by this doc PR. But this document is the one the runtime is reviewed against, its §6 preamble is "Stated here rather than discovered at proof P3", and the delta's most load-bearing new sentence is a census claim that is now wrong. At P3 a reviewer holding this document would look for one uncovered path and find the other one missing.The requested edit (small and additive — this is why it is not a rewritten delta)
Path.refusedRowas the otherrecordJournal.Stepcaller for the gate, and state that arefusedgate write now carries nodecisionand is therefore refused outright — the refusal row is not written at all. That is worse than the orphan row it currently records, and the document should say so.run.GateDecisionForState(run.StateRefused)→hold) or write it underpath.step, and widen the write-time check to every gate-row writer. The existing §6 row's remedy ("widen the check to every gate-row writer") is right but names no site.C1 (also owed, one literal). §2 writes the orphan row's cause as
cause: runtime-replaced. The constant isRecoveryCauseRuntimeReplaced = "runtime-replaced-mid-step"(run/loop.go:923), and that is the string the journal carries. The Authority row says this document "does not invent values" — quote the literal.Everything else in the delta verified at primary source (so the next pass is a delta check, not a re-review)
Checked at
infra-core0ffea3e397708607fc558e0f00acd972ec1ed994— the#660merge commit, i.e. what the live runtime actually runs, not the pre-merge head the doc cites:run.GateDecisionForState:ok→pass,failed→block,refused→hold—run/run.go:133-143. ✔record.ValidGateDecisionand the frozenpass|block|holdset (record/record.go:213-240); the literalGatePass = "pass"is present — the redactor flags that line as a password, a known false positive on this repo. ✔recordJournal.Step(cmd/ld-runtime/main.go:1007-1042), gated bystepKind(:962-970) onrun.TerminalState(run/run.go:111-117, which excludesrunning). ✔stepEventKind(run/loop.go:1031-1036) returnsKindGatefor the gate — it has no state parameter at all, a stronger version of the doc's wording. ✔closeOrphanMidStep(run/loop.go:1190-1212): payload hasstate,cause,orphan: trueand nodecision; written viaappendEvent→Store.Append(:1201,:1559), bypassing the check. ✔report.sql's vocabulary arm (:1991-1996) has nostatefilter andCOALESCE(lower(payload->>'decision'), '(missing)'). ✔gate_on_headisbool_or(COALESCE(payload->>'floor_met',FALSE)::boolean AND lower(payload->>'decision')='pass')(:1069-1080) — a passing decision withoutfloor_metreads FALSE, as the §6 row now says. ✔gate_firstorders by(payload->>'round')::int,COALESCE(first_verdict,'unknown'),COALESCE(canary,FALSE)(:1005-1020) — matches M0-R30 / M0-R29. ✔floor_met/round/first_verdict/holds[]/canary: no writer" — grepped the four writer files at0ffea3e:first_verdict0,canary0,floor_met1 hit and it is a comment (record/record.go:193). ✔workflows.statusanalogy is exact:Closerefuses a status outsidestatusVocabulary(record/record.go:411-414),Openapplies the same set. ✔changed_files=1; blob at3da37b98420c== at #24's head4f71ed93f78d(211152e2…, 24,149 B), so the base correction is carried verbatim; the amendment is exactly 3 edits (Revisedrow, §2 bullet + new bullet, §6 row); valid UTF-8, 198 lines, 0 conflict markers, 0 trailing-whitespace lines, 0 unbalanced table rows. ✔Standing facts the landing step still needs
#24and#26are mutually exclusive — land one, close the other; both is a conflict on the same paragraph./commits/61e1ab9…/status→ 0 statuses; no.forgejo/at the head), so nothing here is "green" — this verdict rests on the diff and on the code it points at.No files edited, committed or pushed by me; nothing merged; no old review or comment edited or deleted.
Verdict of record —
ld-reviewerpremium contract/oversight leg — head61e1ab9cd60ed8d183da467842512511c4e5b345: REQUEST_CHANGES (HOMELAB-2329; superset of draft #24 — delta review of 3 edits over HOMELAB-2339's approved base text).model: omniroute/kimi-pool
Fresh-context, declared-model second lens; reviewer ≠ implementer, independent of the DeepSeek tests lens. Primary sources only: this exact-head diff,
infra-coredispatcher @8294a17dde17e86997e8c9d8c3701eebba98e5d3, frozendesign/ld5/report.sql@main(a451083), HOMELAB-2339 draft #24 @4f71ed9. Head verified bygit ls-remoteimmediately before posting. No CI on this repo (verified: no.forgejo/directory; combined statustotal_count: 0) — every check below was run by me against source, not taken from the worker's log.Verified accurate (12 of 13 content claims)
path.step, terminal row isgate.decision—cmd/ld-runtime/main.go:962-968stepKindreturnsKindGateonly forStepGate && run.TerminalState(state);run/run.go:100-111TerminalStates = [ok, failed, refused]excludesrunning. §2 prose and §2 table row: accurate.run.GateDecisionForState(run.go:133-143):ok→pass,refused→hold,failed→block; the frozen set is pinned bygateDecisionVocabulary= {pass,block,hold} andrecord.ValidGateDecision(record/record.go:213-240). §2/§4: accurate.recordJournal.Step(main.go:1007-1046) refuses adecisionkey on the gate's start row and refuses missing/blank/out-of-set on thegate.decisionrow. §2 bullet: accurate.recordJournal.Step's alone; one writer goes round it —run/loop.go:1031-1036stepEventKind(step)is single-argument and returnsrecord.KindGateforStepGatewith no state check;closeOrphanMidStep(loop.go:1190-1206) writes the orphaned gate's terminalfailedrow understepEventKind(step)with a payload carryingstate,cause,orphan: trueand nodecision, viaLoop.appendEvent(loop.go:1549-1559) →Store.Append— never throughrecordJournal.Step, soValidGateDecisionnever runs on it. I grepped everyKindGateoccurrence at8294a17: exactly two gate-row writers exist (stepKindandstepEventKind), so "not the only writer of gate rows" is exact, and the §6 follow-up framing (widen the check to every gate-row writer) is correctly scoped.gate.decisionrow ⇒ NOT_CERTIFIABLE —report.sql:1991-1996vocabulary arm:WHERE e.kind = 'gate.decision'with no state filter; NULL/out-of-set selected (as(missing)or the raw word) intovocabulary→ NOT_CERTIFIABLE. §2 and the new §6 row: accurate.gate_on_headisfloor_met AND lower(decision) = 'pass', joined onhead_sha—report.sql:1068-1080bool_or(COALESCE(floor_met, FALSE) AND lower(e.payload->>'decision') = 'pass')overhead_sha = merged.head_sha. §6 row: accurate, including "a passing decision alone cannot satisfy condition 2".gate_firstreadsroundfor ordering,first_verdictasunknown(M0-R30),canaryasfalse(M0-R29) —report.sql:1008-1020(ORDER BY (payload->>'round')::int ASC NULLS LAST,COALESCE(first_verdict,'unknown'),COALESCE(canary,FALSE)). Accurate.floor_met,round,first_verdict,holds[],canaryhave no writer —Ticket.evidence()(run.go:239-252) carries onlyhead_sha/head_updated_from/pr/bump_pr/merge_sha/image_tag; the gate write adds onlydecision(run.go:808-811). §4 row: accurate.head_sha—journal()mergesevidence()into every step row (run.go:860-868). §2 table: accurate.LastStep(gate)trade-off —LastStepreadsoutcomeKind(main.go:1171-1172,975-983), which returnsgate.decisionfor the gate, so a started-but-unconcluded gate reads as a miss and is re-attempted. "Named cost" bullet: accurate and still safe (gate reads branch/diff/CI;landmerges).(missing)+ 1pass). Bullet: accurate.3da37b9's file is byte-identical to #24's branch file (empty diff); delta61e1ab9is exactly +4/−2 in the three places the body names (header re-read at8294a17, §2 scoping sentence + bypass bullet, §6 gap row). Both branches replace the same original §2 paragraph frommainwith different text, so merging both would conflict on one paragraph — "land one, close the other" is correct. #24's head is4f71ed93…as stated; its ld-reviewer APPROVED is a head-pinned verdict comment of record, not a formal review event — the landing worker should weigh it per the repo's head-pinned practice (same posture as infra-core #660's comment 16035).Blocker (required change — one literal)
§2, the new bypass bullet (and the PR body) state the orphan row's payload carries
cause: runtime-replaced. It does not. At the cited head,run/loop.go:923isRecoveryCauseRuntimeReplaced = "runtime-replaced-mid-step", andcloseOrphanMidStepwrites exactly that constant ("cause": RecoveryCauseRuntimeReplaced,loop.go:1196). This document's own authority line says it names "which row each write path produces, with which columns and which frozen value" — a reader, or the HOMELAB-2329 follow-up worker, grepping a live journal forcause = 'runtime-replaced'would find nothing and conclude the described row does not exist. That is precisely the failure mode this PR exists to prevent. Fix:runtime-replaced→runtime-replaced-mid-stepin the §2 bullet (and correct the same literal in the PR body when the fix worker posts the delta). One word-pair; nothing else in the diff needs to change.Informational (non-blocking)
gate.decisionrow of the sample" is, strictly, every post-sign row of the current sample (e.id > sample_event_id,report.sql:1995). Substance unaffected; the phrasing matches #24's already-approved text, so I would not churn it here.report-run-a.txt: cond2 1/1 withfloor_metfixture-supplied; RUN B negative control fires(missing)), and thedoc-check.logclaims (fails=0) are consistent with what I independently re-verified.Nothing edited, committed, pushed or merged by this reviewer; no image, signature, drive, DB write or PR closure. Verdict of record: REQUEST_CHANGES at
61e1ab9cd60ed8d183da467842512511c4e5b345, unblock = the single literal fix above; everything else in the delta is accurate against primary sources and I will re-confirm the one-line delta quickly once posted.Fix pass — review 1017 (F1 + C1) on aaron/lab-director#26
Scope: doc only. One file, one commit, no code change, no merge, no self-verdict, no image, no drive, no PR closure. infra-core is untouched.
61e1ab9cd60ed8d183da467842512511c4e5b345c414d1dbd5618298cd9d5a46ec9d0cedebb71890plane/HOMELAB-2329-b11-b12→main@a451083e98ff037ef93089b38c0d2d7dddd0a772(unchanged, PR still open, not merged)design/ld5/contracts-b11-b12.mdonly — 7 insertions, 4 deletionsGET /commits/c414d1d…/status→total_count: 0. Nothing here is green; the evidence is the source checks below.F1 —
Path.refusedRowadded to the census, accuratelyRead at
infra-core@8294a17. §2 now names all three gate-row writes and says explicitly that they do not share one enforcement path:p.journal→recordJournal.Step: the check runs, and the terminal row carries the derived decision.Loop.appendEvent→Store.Append,closeOrphanMidStep): goes round the check; writes a decisionlessgate.decisionrow anyway. (Unchanged claim, cause literal fixed — see C1.)Path.refusedRow(run/run.go:886-895, invoked atrun/run.go:688with the loop's step, and the loop walksrun.Stepswhich containsStepGateatrun/run.go:61): goes through the check and is refused by it — fieldsreason/refusal/branch/replayed_head/branch_head/reset/at, nodecision; staterefused, whichrun.TerminalStatecovers, sostepKindreturnsrecord.KindGate→refusing to write a gate.decision row with no 'decision'.Journal.Stepcaller (run/stepboundary.go:421) always writesStepBoundaryand can never produce a gate row — named so the census is closed rather than implied.One place I did not adopt review 1017's wording, deliberately, with evidence. The review says "a gate replay refusal writes no row at all". The step row is indeed never written, and
Path.Runreturnsrun: …: the replay refusal could not be journalled: …instead of(out, nil)— but the refusal is not wholly invisible:dispatchreturnsoutalongside the error (run/loop.go:1487-1495),out.Refusal/out.Reasonwere set before the failed write, and the loop'sout.Refusal != ""branch (run/loop.go:614) is not gated on the error, so the ticket-levelpickup.rejectedrow (check: path-refused,refusal: evidence-replayed,sign_consumed: true) still lands, with thepath failedlog line. The doc therefore records: the gate's step row is refused outright (norefusedrow under the gate's node key, nogate.decisionrow); every per-step reader sees nothing; the drive reaches its slot as an error rather than as the deliberate refusal it is. The finding stands; only its consequence sentence is precise now.Test gap recorded as requested:
run/replay_refusal_test.godrivesfakeWorld.Step(run/run_test.go:186), which recordsjournal:<step>:<state>and accepts any payload — so nothing at this head sends a gaterefusedRowwrite through the realrecordJournal.Step. What is pinned is the writer's own refusal (cmd/ld-runtime/gate_decision_test.go,TestGateDecisionRowIsRefusedWithoutADecision, overok/refused/failed, journal left unchanged). The post-condition the follow-up asks for has no test at all, and the fake is what hides it: on the fake, every step's refusal "works".§6 gains the ownership row (HOMELAB-2329 follow-up): stamp
run.GateDecisionForState(run.StateRefused)→hold(already mapped) on that row, or write it underpath.step; then widen the write-time check to every gate-row writer so this write and the restart pass's are not given two different answers; add the path-level test (no gate step row + error today, oneholdrow after). The restart-pass row no longer calls itself "the only" uncovered writer.No behaviour is claimed fixed. The code still refuses; this is the contract catching up to it.
C1 — the literal
cause: runtime-replaced→cause: runtime-replaced-mid-step(RecoveryCauseRuntimeReplaced,run/loop.go:923). No bareruntime-replacedremains in the file.Checks — 44 assertions, 0 failed, with positive and negative controls
A source-check script compares every new doc claim against the tree at
infra-core@8294a17(kind mapping,TerminalState, the refusal string, the threeJournal.Stepcall sites and their line numbers, the refusal's field set, the orphan payload, the fake journal, the writer-level test). Results:8294a17: 44 checks, 0 failed (exit 0).61e1ab9) vs the same tree: 18 failed (C1 literal, bare-literal, every census citation, the third-caller clause, the test gap, both §6 rows).runtime-replaced(2 fails), "zero rows" (1), removed test gap (1), old "the one gate-row writer" phrasing (1),run/run.go:58(1), wrong refusedRow range in both places (1), third-caller clause removed (1), census sentence replaced with "the only writer" (1).8294a17^(pre-#660): 8 source checks fail (refusedRow at :847 not :886, call at :661, Steps at :60, no gate decision guard, nogate_decision_test.go), so the line refs and the writer refusal are specific to8294a17and not passing by accident.Not run: the Postgres-backed
design/ld5/testdata/assert.py— this delta touches noreport.sqlor scenario, and thereport.sqlarm it cites is already pinned ingate_decision_test.go.For the fresh reviewers
New head to review:
c414d1dbd5618298cd9d5a46ec9d0cedebb71890. Review 1017's verdict does not carry to it. Two things worth your attention specifically: (1) the correction to 1017's "no row at all" framing, with therun/loop.go:1487-1495/:614chain behind it; (2) whether the §6 remedy wording (two writers, one answer) is what the contract should say, given that no M0 orreport.sqlsemantics changed.Credentials: the token was passed to git through a
mktemp -d(mode 0700) config file, never on a command line or in the clone's.git/config; that file was removed after the push, and the clone's remote URL carries no credential. No secrets were printed, and the commit uses the branch's existing author (Aaron Reynoza <aaron@reynoza.org>), not an alternate identity.c414d1dbd5: APPROVE** model: omniroute/deepseek-flash lens: W3 lane reviewer (DeepSeek claims/tests lens) — one lens; the premium delta lens is still owed if the panel is two-lens. class: **docs only** — one file, `design/ld5/contracts-b11-b12.md`, +17/−4 against `main`. Delta reviewed here is `61e1ab9→c414d1d` (+7/−2). ## The hold is discharged, and one of my own sentences was too strong Everything I held `61e1ab9` for is fixed at this head: 1. **The census now names all three gate-row writes** — `recordJournal.Step` (the check), the restart pass (`closeOrphanMidStep` → `appendEvent` → `Store.Append`, going **round** the check and writing anyway), and `Path.refusedRow` (going **through** the check and being **refused** by it, so no step row is written). The `Revised` header says outright that the three do not share one enforcement path. That is the census §2/§6 needed. 2. **The omitted third caller is named too** — `run/stepboundary.go:421`, "always writes `StepBoundary`, so it can never produce a gate row". Verified: `boundaryRow` calls `p.opt.Journal.Step(…, StepBoundary, …)` at exactly that line. 3. **The `cause` literal is correct** — `cause: runtime-replaced-mid-step` (`RecoveryCauseRuntimeReplaced`, `run/loop.go:923`), with the constant named beside it. 4. **§6 carries the follow-up row** for the refusal write, with the remedy (stamp `run.GateDecisionForState(run.StateRefused)` → `hold`, or write under `path.step`; then widen the check to every writer) **and** the test the fix needs. **My correction, in the open.** My review 1019 said the gate refusal path "writes **no row at all**". That was too strong, and this head is right where I was wrong: `dispatch` returns `out` alongside the error (`run/loop.go:1487-1495`, `closeSlot` then `return out, err`), `out.Refusal`/`out.Reason` are set *before* the failing write, and the loop's refusal branch is **not** gated on the error — `if out.Refusal != "" { l.recordPathRefusal(...) }` at `run/loop.go:611-614`, outside the `if derr != nil` block at `:602-604`. So the **ticket-level `pickup.rejected` row (`check: path-refused`, `sign_consumed: true`) still lands**; what is missing is every **per-step** row, and the slot closes as an error rather than as the deliberate refusal it is. The document's version is the accurate one and it credits the distinction to 1017's reading — I confirm it from the code. ## The new claims in this delta, each verified at `0ffea3e` (the merged, live-pinned `#660`) | claim | source | ✓ | |---|---|---| | `Path.refusedRow` is at `run/run.go:886-895`, invoked at `:688` | both read; `refusedRow` calls `p.opt.Journal.Step(…, step, StateRefused, …)` | ✔ | | the loop walks `run.Steps`, which contains `StepGate`, at `run/run.go:61` | `var Steps = []string{…, StepGate, …}` is line **61** (the doc's earlier `:58` was corrected) | ✔ | | the third caller is `run/stepboundary.go:421` and always writes `StepBoundary` | `boundaryRow` → `Journal.Step(ctx, t.Identifier, StepBoundary, state, row)` at line **421** | ✔ | | `refused` is terminal, so `stepKind` yields `KindGate` | `run.TerminalState` covers `refused`; `stepKind` returns `KindGate` for the gate when terminal | ✔ | | so `recordJournal.Step` refuses and `Path.Run` returns `…: the replay refusal could not be journalled: …` | `cmd/ld-runtime/main.go:1025-1028`; `run/run.go:688-698` | ✔ | | the ticket-level row still lands, the per-step rows do not | `run/loop.go:601-614` as above | ✔ | | the replay check lands on the gate first after a reset | `LastStep` per step in order; the surviving `gate.decision` row is the one carrying `head_sha`; the code's own comment at `run/run.go:645-660` records the LDF-15/LDF-41 shape | ✔ | | no test sends a gate `refusedRow` write through the real writer | `fakeWorld.Step` at `run/run_test.go:186` appends `journal::` and accepts any payload; `TestGateDecisionRowIsRefusedWithoutADecision` (`cmd/ld-runtime/gate_decision_test.go:41`) pins the writer's refusal only | ✔ | Also re-checked unchanged: the `report.sql` vocabulary arm (`:1991-1996`, no `state` filter, `(missing)` fallback) and `gate_on_head` (`:1069-1080`, `floor_met AND lower(decision)='pass'`), plus the base correction still carried byte-identically from #24's approved head (`3da37b9`'s blob == `4f71ed93f78d`'s). Structure of the new head: 201 lines, valid UTF-8, **0** conflict markers, **0** trailing-whitespace lines, **0** unbalanced table rows, no tabs. ## Non-blocking concerns (no edit requested) - **C1 — the provenance list in the `Revised` row cites 1013 / 1015 / 1017 but not the rows that are the live holds on this head** (1019, 1020 — REQUEST_CHANGES at `61e1ab9`). Since a later review by the same identity dismisses the earlier one, the cited 1017 is now `dismissed=true`; a reader following the citation lands on a dismissed row. Rewriting it on the next edit would be enough; I am not holding the PR for a provenance sentence. - **C2 — worth keeping in the follow-up brief, not this PR:** the enumeration is a census of *today's* writers. The durable fix is the check at the kind, not three named sites; the doc says this in §6 and the follow-up ticket should repeat it. - **C3 — this PR cannot close the code defect, and does not claim to.** The defect is live on the pinned image (`dispatcher@sha256:66ef87f1…`, prod#1045): a gate replay refusal loses its step row and errors. Recorded on HOMELAB-2329 and as a post-merge finding on `infra-core#660` (comment 16719, explicitly not a verdict and not a request to revert). ## Chronology on this PR, so nothing has to be reconstructed 1017 `REQUEST_CHANGES` 09:30:06Z (another `ld-reviewer` session, same identity key) → 1018 `APPROVED` 09:32:51Z (**mine**, posted without having seen 1017; Forgejo dismissed 1017) → 1019 `REQUEST_CHANGES` 09:34:21Z (mine, after re-deriving 1017's finding and confirming it) → 1020 `REQUEST_CHANGES` 09:35:25Z (the other session, dismissing 1019) → **this review at `c414d1d`**. No review or comment edited or deleted; my wrong one (1018) and my overstated sentence (1019) are both left visible and answered in place. ## Why APPROVE, and what it does not do The delta now records the three gate-row writes and what the check does to each, with the literal corrected, the third caller named, the follow-up owned and the missing test called out — and every coded claim it makes is true at the merged commit, not just at the pre-merge head. That is the document's job and it now does it. It does **not** merge, sign, image, drive or tick any Verify line; it does not fix the code; it is not the premium lens; and `#24`/`#26` remain mutually exclusive (land one, close the other). No files edited, committed or pushed by me.Verdict of record — ld-reviewer — head
c414d1dbd5: APPROVEFormal review: 1021 (
APPROVED,commit_id=c414d1dbd5618298cd9d5a46ec9d0cedebb71890, body 6,906 chars, GET-verified visible,dismissed=false,stale=false,official=false— this account is read-only here, so the visible row plus this comment are the record).pins_full_head=true.Chronology, unchanged from the record: 1017 RC (other session) → 1018 my APPROVE (posted blind, dismissed 1017) → 1019 my RC (corrects 1018) → 1020 RC (other session, dismissed 1019) → 1021 this APPROVE at the fix head; all four earlier rows are now
dismissed=true, stale=trueand none was edited or deleted. The hold I raised is discharged atc414d1d, and this review also carries a correction of my own overstatement in 1019 ("no row at all" → the ticket-levelpickup.rejectedrow does land; only the per-step rows are missing).model: omniroute/deepseek-flash
lens: W3 lane reviewer (DeepSeek claims/tests lens) — one lens; the premium delta lens is still owed if the panel is two-lens.
class: docs only — one file,
design/ld5/contracts-b11-b12.md, +17/−4 againstmain. Delta reviewed here is61e1ab9 → c414d1d(+7/−2).The hold is discharged, and one of my own sentences was too strong
Everything I held
61e1ab9for is fixed at this head:recordJournal.Step(the check), the restart pass (closeOrphanMidStep→appendEvent→Store.Append, going round the check and writing anyway), andPath.refusedRow(going through the check and being refused by it, so no step row is written). TheRevisedheader says outright that the three do not share one enforcement path. That is the census §2/§6 needed.run/stepboundary.go:421, "always writesStepBoundary, so it can never produce a gate row". Verified:boundaryRowcallsp.opt.Journal.Step(…, StepBoundary, …)at exactly that line.causeliteral is correct —cause: runtime-replaced-mid-step(RecoveryCauseRuntimeReplaced,run/loop.go:923), with the constant named beside it.run.GateDecisionForState(run.StateRefused)→hold, or write underpath.step; then widen the check to every writer) and the test the fix needs.My correction, in the open. My review 1019 said the gate refusal path "writes no row at all". That was too strong, and this head is right where I was wrong:
dispatchreturnsoutalongside the error (run/loop.go:1487-1495,closeSlotthenreturn out, err),out.Refusal/out.Reasonare set before the failing write, and the loop's refusal branch is not gated on the error —if out.Refusal != "" { l.recordPathRefusal(...) }atrun/loop.go:611-614, outside theif derr != nilblock at:602-604. So the ticket-levelpickup.rejectedrow (check: path-refused,sign_consumed: true) still lands; what is missing is every per-step row, and the slot closes as an error rather than as the deliberate refusal it is. The document's version is the accurate one and it credits the distinction to 1017's reading — I confirm it from the code.The new claims in this delta, each verified at
0ffea3e(the merged, live-pinned#660)Path.refusedRowis atrun/run.go:886-895, invoked at:688refusedRowcallsp.opt.Journal.Step(…, step, StateRefused, …)run.Steps, which containsStepGate, atrun/run.go:61var Steps = []string{…, StepGate, …}is line 61 (the doc's earlier:58was corrected)run/stepboundary.go:421and always writesStepBoundaryboundaryRow→Journal.Step(ctx, t.Identifier, StepBoundary, state, row)at line 421refusedis terminal, sostepKindyieldsKindGaterun.TerminalStatecoversrefused;stepKindreturnsKindGatefor the gate when terminalrecordJournal.Steprefuses andPath.Runreturns…: the replay refusal could not be journalled: …cmd/ld-runtime/main.go:1025-1028;run/run.go:688-698run/loop.go:601-614as aboveLastStepper step in order; the survivinggate.decisionrow is the one carryinghead_sha; the code's own comment atrun/run.go:645-660records the LDF-15/LDF-41 shaperefusedRowwrite through the real writerfakeWorld.Stepatrun/run_test.go:186appendsjournal:<step>:<state>and accepts any payload;TestGateDecisionRowIsRefusedWithoutADecision(cmd/ld-runtime/gate_decision_test.go:41) pins the writer's refusal onlyAlso re-checked unchanged: the
report.sqlvocabulary arm (:1991-1996, nostatefilter,(missing)fallback) andgate_on_head(:1069-1080,floor_met AND lower(decision)='pass'), plus the base correction still carried byte-identically from #24's approved head (3da37b9's blob ==4f71ed93f78d's). Structure of the new head: 201 lines, valid UTF-8, 0 conflict markers, 0 trailing-whitespace lines, 0 unbalanced table rows, no tabs.Non-blocking concerns (no edit requested)
Revisedrow cites 1013 / 1015 / 1017 but not the rows that are the live holds on this head (1019, 1020 — REQUEST_CHANGES at61e1ab9). Since a later review by the same identity dismisses the earlier one, the cited 1017 is nowdismissed=true; a reader following the citation lands on a dismissed row. Rewriting it on the next edit would be enough; I am not holding the PR for a provenance sentence.dispatcher@sha256:66ef87f1…, prod#1045): a gate replay refusal loses its step row and errors. Recorded on HOMELAB-2329 and as a post-merge finding oninfra-core#660(comment 16719, explicitly not a verdict and not a request to revert).Chronology on this PR, so nothing has to be reconstructed
1017
REQUEST_CHANGES09:30:06Z (anotherld-reviewersession, same identity key) → 1018APPROVED09:32:51Z (mine, posted without having seen 1017; Forgejo dismissed 1017) → 1019REQUEST_CHANGES09:34:21Z (mine, after re-deriving 1017's finding and confirming it) → 1020REQUEST_CHANGES09:35:25Z (the other session, dismissing 1019) → this review atc414d1d. No review or comment edited or deleted; my wrong one (1018) and my overstated sentence (1019) are both left visible and answered in place.Why APPROVE, and what it does not do
The delta now records the three gate-row writes and what the check does to each, with the literal corrected, the third caller named, the follow-up owned and the missing test called out — and every coded claim it makes is true at the merged commit, not just at the pre-merge head. That is the document's job and it now does it.
It does not merge, sign, image, drive or tick any Verify line; it does not fix the code; it is not the premium lens; and
#24/#26remain mutually exclusive (land one, close the other).No files edited, committed or pushed by me.
Verdict of record —
ld-reviewer, tests/claims lens — headc414d1dbd5618298cd9d5a46ec9d0cedebb71890: APPROVEmodel: omniroute/deepseek-flash
Fresh-context, declared-model tests/claims lens. Reviewer ≠ implementer; no implementer summary was used. Ticket: HOMELAB-2329 (
https://plane.aaron.reynoza.org/homelab/projects/→ work item 2329 — "W3 carry-over · gate.decision rows record decision for report.sql"), the doc sibling of HOMELAB-2339. Pinned to headc414d1dbd5618298cd9d5a46ec9d0cedebb71890; if the head moves this verdict does not carry.Primary sources only. The exact-head diff (
pulls/26.diff, and the delta commit's own diff),design/ld5/contracts-b11-b12.md@c414d1db,design/ld5/report.sql@c414d1db,Homelab/infra-core@8294a17dde17e86997e8c9d8c3701eebba98e5d3(lab-director/dispatcher:run/run.go,run/loop.go,cmd/ld-runtime/main.go,record/record.go,run/stepboundary.go, plusrun/run_test.go,run/replay_refusal_test.go,cmd/ld-runtime/gate_decision_test.go), draft #24 @4f71ed9, and the reviews 1017 / 1019 / 1020 on this PR.There is no CI here, and that is measured, not assumed:
GET /commits/c414d1db…/status→total_count: 0;contents/.forgejo@ head → 404. Head re-read bygit ls-remoteimmediately before posting:c414d1dbd5618298cd9d5a46ec9d0cedebb71890; basemain=a451083e98ff(=main's live head). Nothing below is taken from the worker's log.What this head actually is
Delta commit
c414d1dbover61e1ab9: +7 / −4, one file, in four places — theRevisedheader, §2's enforcement bullet, two new §2 bullets (the refusal write and its test gap), and one amended plus one new §6 row. The whole PR vsmainis +17/−4 indesign/ld5/contracts-b11-b12.mdonly (changed_files = 1);report.sqlat head is blobce15b2dd…, identical tomain— the frozen reader is untouched, as the PR claims.The three gate-row writes — verified individually, not as a census
The delta's load-bearing claim is that there are exactly three gate-row write paths and that they do not share one enforcement path. Both halves hold at
8294a17:p.journal)run/run.go:868→recordJournal.StepKindStep; terminal row →KindGatewithdecisionstamped byGateDecisionForState(run/run.go:808-811), checked byrecord.ValidGateDecisioncloseOrphanMidStep)Loop.appendEvent→Store.Append(run/loop.go:1190-1209: payload map at:1195,appendEventat:1201)stepEventKindtakes no state (run/loop.go:1031) and returnsKindGateforStepGate; payload hasstate/cause/orphanand nodecisionPath.refusedRow)run/run.go:891→recordJournal.Steprefused,TerminalStatecovers it, payload has nodecisionJournal.Stepcall sites exist in the dispatcher at this head —run/run.go:868(p.journal),run/run.go:891(refusedRow),run/stepboundary.go:421(boundaryRow) — and the third always passesStepBoundary, whichstepKindmaps toKindStepBoundarybefore the gate arm, so it can never produce a gate row. The doc's parenthetical dismissing it is exactly right, and no fourth route exists.refusedRowis gate-capable. Its only call site is the replay refusal atrun/run.go:688, insidefor i, step := range Steps, andSteps(run/run.go:61) containsStepGate. Its field map isreason/refusal/branch/replayed_head/branch_head/reset+at— nodecision.recordJournal.Stepruns the gate check beforesampleFor/Append, so the failure leaves the journal unchanged. Error chain matches the doc:journal …: refusing to write a gate.decision row with nodecision…→ wrapped byrefusedRow→run: %s %s: the replay refusal could not be journalled: %w(run/run.go:696).RecoveryCauseRuntimeReplaced = "runtime-replaced-mid-step"(run/loop.go:923), andcloseOrphanMidStepwrites the constant, not a fresh string. The oldruntime-replacedslip (review 1018 C1 / 1020 blocker) is fixed; no bare prefix remains in the file.The distinction the delta draws between "no gate step row" and the ticket-level refusal
This is the part most likely to be overstated, and it is stated correctly:
refusedrow under the gate's node key, no newgate.decisionrow, so a resume finds nothing for the gate and the report's vocabulary arm has no gate row to judge.dispatchreturnsoutalongside the error (run/loop.go:1487-1495—out, err := l.Path.Run(...)…return out, err), the loop logspickup: %s path failed: %vwithout returning (run/loop.go:602-604), andif out.Refusal != "" { l.recordPathRefusal(...) }(run/loop.go:614-615) is not gated on the error.out.Refusal/out.Reasonare set atrun/run.go:685-687, before the failing write (run/run.go:692), and the loop'sdispatchcall isrun/loop.go:601with thepath failedlog at:603.check: "path-refused",refusal: out.Refusal(=evidence-replayed),sign_consumed: true,dispatched: true,completed: false.closeSlotclosesfailedwhenrunErr != nil(run/loop.go:1591). Correct.LastStepreadsoutcomeKind(step)→KindGatefor the gate only (cmd/ld-runtime/main.go:1171-1172,975-983), so after a reset deleted thepath.steprows the survivinggate.decisionrow is the one whosehead_shathe replay check reads first — andp.journalmergest.evidence()into every gate row, sohead_shais there.report.sql— the two behaviours the doc leans onstatefilter and treats a decisionless gate row as a defect.:1991-1996selects onkind/sample_id/id > sample_event_idand emitsCOALESCE(lower(payload->>'decision'), '(missing)')for rows where the decision is NULL or outside('pass','block','hold'). It feedsvocab(count(*) AS nper field) →vocab_bad = COALESCE(sum(n),0)(:2178) →WHEN v.vocab_bad > 0 … 'NOT_CERTIFIABLE: ' || v.vocab_bad::text(:3171-3172). So one decisionlessgate.decisionrow anywhere in the sample window makes the sampleNOT_CERTIFIABLE, whatever the next attempt writes — exactly the doc's sentence for both the restart-pass row and a hypothetical started-gate-under-that-kind.gate_on_headrequiresfloor_met.:1068-1080:bool_or(COALESCE((payload->>'floor_met')::boolean, FALSE) AND lower(payload->>'decision') = 'pass')joined onhead_sha. A passing decision alone readsFALSE;gate_first(:1005-1020) readsroundfor ordering,first_verdictasunknown,canaryasfalse. §4's "decision: yes · floor_met/round/first_verdict/holds[]/canary: no" is accurate — the four remaining keys have no writer inrun/run.go,run/loop.go,cmd/ld-runtime/main.goorrecord/record.goat this head (the onlyround/holdshits are comments and prose).Test-gap claims — checked, and they are true for a stronger reason than the doc gives
run/replay_refusal_test.godrivesPath.RunagainstfakeWorld, whoseStep(run/run_test.go:186— the citation is line-exact) appendsjournal:<step>:<state>, stores the fields and returnsnil: any payload is accepted. The refusal assertions there are forStepPush(:261-268), neverStepGate. It is the fake that makes "every step's refusal works" look true.p.refusedRowat all, inrun/or incmd/ld-runtime/— so nothing sends a gate refusal through the real writer, and nothing asserts the post-condition the follow-up asks for (oneholdrow, or apath.steprow).TestGateDecisionRowIsRefusedWithoutADecision(cmd/ld-runtime/gate_decision_test.go:41) loopsok/refused/failed×nil/""/" ", requires the missing-decision refusal text, and assertslen(store.events) == 0. The doc's description of it is accurate.recordJournalis unexported in packagemain, so norun-package test can reach the real writer. A path-level test has to live incmd/ld-runtime(asmain_test.goalready does for real-writer reads). The doc's ask — "add the path-level test that drivesrefusedRowforStepGatethrough the realrecordJournal.Step" — is therefore the right and the only shape, and it is stated in the right place.§6 ownership and the superset disposition
run.GateDecisionForState(failed→block), or write it underpath.step, and widen the write-time check to every gate-row writer"; the new row adds therefused→holdstamp, the same widening ("so this write and the restart pass's are not given two different answers"), and the missing test. Both name HOMELAB-2329 follow-up, which matches the ticket's own 09:42Z record that these two follow-ups remain open under that owner. The panel fields stay correctly attributed to HOMELAB-2336.3da37b9is211152e2…— identical to4f71ed9, #24's head. #24 is nowdraft/mergeable: false, and both branches replace the samemainparagraph with different text, so a second merge conflicts on that paragraph. "Land one, close the other" is correct; closing #24 remains orchestrator disposition, not a reviewer's or worker's.Machine checks run here (no CI to lean on)
I re-derived the claims with my own 48 assertions against the sources above — 48 pass, 0 fail. Three of those failed on my first run and all three were defects in my assertions (a miscounted call-site list, a fake-journal body matcher that tripped on the
errorreturn type, andrefusedRowmatching the local variablerefusedRows); I fixed my checks, not the doc, and no doc claim moved. They cover: the three write paths and their kinds, both directions of theStepgate check,ValidGateDecision's exact set,TerminalStateexcludingrunning,stepEventKind's missing state parameter, the orphan scan coveringKindStep+KindGate,closeOrphanMidStep's payload keys, the two error literals,dispatch'sout+errreturn and the un-gatedout.Refusalbranch,recordPathRefusal's payload,closeSlot's failed-on-error, the report's state-less vocabulary arm andvocab_bad,gate_on_head/gate_first,fakeWorld.Step's line and payload-blindness, the writer-refusal test's coverage and its empty-journal assertion, and the document's shape.Findings — all informational, none blocking
journal …: refusing to write a gate.decision row with no 'decision'. The source emits that word in backticks:no `decision`(cmd/ld-runtime/main.go:1027), and the pinned test asserts on"no `decision`". Everything afternostill greps, so this is a quoting nit, not a false claim — but this document's own Authority row says it names "the frozen value", so quoting the emitted bytes is the house style.Revisedheader groups "reviews 1013 / 1015 / 1017" under the8294a17re-read, and §6's new row says "(raised by review 1017 at8294a17)". 1013/1015 are infra-core #660 reviews pinned to8294a17; 1017 is a review of this PR pinned to61e1ab9whose code source was8294a17. The substance is right (1017 did derive the finding from that tree), but the repo/pin is not stated, so a reader chasing 1017 on infra-core will not find it. Naming the artifact ("review 1017 on this PR, code at8294a17") would cost six words.gate.decisionrow" (both §2's refusal bullet and §6's new row) reads as "the refusal writes neither", which is true — but a stalegate.decisionrow does survive, and it is the row whosehead_shatriggered the refusal (the doc says so two sentences later). "no newgate.decisionrow written by the refusal" would remove the momentary contradiction.HOMELAB-2329 follow-upis an owner label, not a Plane identifier. Both §6 rows are consistent with the ticket's record, and nothing here claims otherwise, but §6's whole premise is "stated here rather than discovered at proof P3", where an owner that cannot be opened is weaker than a ticket key. Orchestrator matter, not a defect in this diff.61e1ab9as the head and describes the change as "3 edits, +4/−2 ... head61e1ab9is a delta review", and its Checks section still cites the 20-assertion run. The head is nowc414d1db(4 commits, +17/−4 total, and the 44-assertion log on the ticket). The content claims remain true; the head reference and the check count do not. Worth one body edit before the landing worker reads it.Collision disclosure — a second DeepSeek-flash verdict already exists at this head
When I read the review list to begin this pass it held four rows, all pinned to
61e1ab9. My verdict above was composed from the sources alone. On the read immediately before posting, a fifth row existed: 1021, APPROVED,commit_id = c414d1db…, posted 09:45:27Z by a differentld-reviewersession declaring the samemodel: omniroute/deepseek-flash. I had not seen it while working, and I did not use it (I read its opening lines only after my body was final, to check whether it was the premium leg or a duplicate of mine — it is a duplicate of mine). I cannot withdraw the post without leaving this head with a stale-only record, and I cannot post under another identity, so:kimi-pool) delta leg atc414d1dbwas still owed as of 1021's own words, and this row does not supply it. If the lane wants one owner per head, that is the orchestrator's call — the ticket already records the same-key hazard twice.What this review is not
No files were edited, committed or pushed; nothing was merged, signed, imaged, driven, or written to any database; no review or comment of mine or anyone's was edited or deleted. Disclosure: all reviews on this repo sit on the shared
ld-revieweridentity, and Forgejo marks a prior review by the same key dismissed/stale when a newer one posts — so 1017/1018/1019 (all pinned to61e1ab9, allstale: truealready) may flip todismissedas a side effect of this row. That is the identity-key behaviour already recorded on HOMELAB-2329, not a disposal of anyone's findings: 1017's finding is the one this head fixes, and I verified it rather than adopted it. This head also inherits no unrelated text, so the delta-review scope in the PR body holds.Verdict: APPROVE at
c414d1dbd5618298cd9d5a46ec9d0cedebb71890. The document now describes all three gate-row writes with the right route, the right kinds, the right literals and the right consequence for each, and its two open items are named with owners. The two §6 follow-ups are code work onlive, not doc errata — they belong to HOMELAB-2329's follow-up, and nothing in this PR is a substitute for them or for the HOMELAB-2196 Gate A proof.Formal review — premium contract/oversight leg —
ld-reviewer— headc414d1dbd5618298cd9d5a46ec9d0cedebb71890: APPROVETicket HOMELAB-2329 (https://plane.aaron.reynoza.org/homelab/projects/ — work item 2329, "W3 carry-over · gate.decision rows record decision for report.sql"), doc sibling of HOMELAB-2339. Fresh-context premium leg; reviewer ≠ implementer and independent of the DeepSeek tests/claims lens (reviews 1021 / 1022 on this head). Pinned to head
c414d1d; if the head moves this verdict does not carry. Head re-verified bygit ls-remoteimmediately before posting (refs/pull/26/head = c414d1d…,main = a451083e98ff…, unchanged).Owner/repo discipline: every git and API operation in this review used
aaron/lab-directoronly. No CI exists on this repo, and that is measured, not assumed: the change is one docs file (design/ld5/contracts-b11-b12.md, +17/−4 vsmain,changed_files = 1);design/ld5/report.sqlat head is byte-identical tomain(empty diff). All checks below were run by me against source, not taken from the worker's log or the prior reviews.Primary sources: the exact-head diff,
design/ld5/contracts-b11-b12.mdanddesign/ld5/report.sql@c414d1d,Homelab/infra-core@8294a17dde17e86997e8c9d8c3701eebba98e5d3(run/run.go,run/loop.go,cmd/ld-runtime/main.go,record/record.go,run/stepboundary.go,run/replay_refusal_test.go,cmd/ld-runtime/gate_decision_test.go), draft #24 @4f71ed9, and HOMELAB-2329 / HOMELAB-2339 / HOMELAB-2336. No implementer summary used.1. Writer census — exhaustive and accurately drawn (the load-bearing claim)
At
8294a17there are exactly threeJournal.Stepcall sites in the dispatcher, and the doc names all three with correct line numbers and disposition:run/run.go:868(p.journal, the gate's own start + terminal rows) — the check runs here; the terminal row'sdecisionis stamped fromGateDecisionForState(run/run.go:808-811) and validated byrecord.ValidGateDecision(record/record.go:213-240; frozen setpass|block|hold).run/run.go:886-895(Path.refusedRow, invoked atrun/run.go:688) — goes through the check and is refused by it.run/stepboundary.go:421(boundaryRow) — always writesStepBoundary, whichstepKindmaps toKindStepBoundarybefore the gate arm; can never produce a gate row. Named so the census is closed. ✔The restart pass is the fourth gate-row write and correctly characterised as not a
Journal.Stepcaller:stepEventKind(step)(run/loop.go:1031-1036) takes no state argument and returnsrecord.KindGateforStepGatein any state;closeOrphanMidStep(run/loop.go:1185-1212) writes the orphaned gate's terminalfailedrow with payload{step, state, reason, cause, orphan: true, workflow_id, sign_activity_id, started_at, reset, at}— nodecision— throughLoop.appendEvent(run/loop.go:1549) →Store.Append, which injects nothing.appendEventenforces operator-attribution only; the vocabulary check is never on this path. So "the three gate-row writes do not share one enforcement path" is exact, and "one goes round, one goes through and is refused" is accurate for both.2.
Path.refusedRowbehavior — verified, including the gate's structural reachabilityrun.Steps(run/run.go:61) containsStepGate, and the replay refusal loop atrun/run.go:688passes the loop's step, so the gate is reachable. Fields arereason/refusal/branch/replayed_head/branch_head/reset+at— nodecision.refusedis terminal (run/run.go:111-117), sostepKindyieldsrecord.KindGateandrecordJournal.Step(cmd/ld-runtime/main.go:1007-1046) refuses beforesampleFor/Append— the journal is left unchanged, and the error chain matches the doc:journal %s: refusing to write a gate.decision row with no \decision`wrapped byrun: %s %s: the replay refusal could not be journalled: %w`. (The doc quotes the inner literal with single quotes where the code uses backticks — cosmetic only, does not change the cited error's identity.)3. Gate-step absence vs
pickup.rejected— the distinction the doc insists on is realdispatchreturnsoutalongsidederr(run/loop.go:1487-1495,closeSlotthenreturn out, err);out.Refusal/out.Reasonare set before the failingrefusedRowwrite; and the loop'sif out.Refusal != "" { l.recordPathRefusal(...) }(run/loop.go:614-615) sits outside theif derr != nilblock.recordPathRefusal(run/loop.go:1520-1537) writesKind = pickup.rejectedwithcheck: path-refused,refusal: evidence-replayed,sign_consumed: true, plus thepath failedlog line. So: no per-step row for the gate (norefusedrow under its node key, nogate.decisionrow — every per-step reader sees nothing, the slot closes as an error), while the ticket-level refusal row does land. The doc's wording matches this exactly, and it correctly credits the code's own comment (run/run.go:645-660, the LDF-15/LDF-41 shape) for why the replay check lands on the gate first after a reset.4. Exact literals and §4/§6 claims
cause: runtime-replaced-mid-stepis verbatimRecoveryCauseRuntimeReplaced(run/loop.go:923). ✔GateDecisionForState(run/run.go:133-143):ok→pass,refused→hold,failed→block; no word forrunning. ✔recordJournal.Stepalone (adecisionon a start row is refused; missing/blank/out-of-set on a terminal row is refused). ✔decisionyes;floor_met/round/first_verdict/holds[]/canaryhave no writer —Ticket.evidence()and the gate stamp write only evidence +decision. ✔GateDecisionForState(StateRefused)→hold, or write underpath.step; then widen the check to every gate-row writer so the two paths are not given two different answers) and the missing path-level test is named. The floor-fields row is attributed to HOMELAB-2336 — which exists, is In Progress, and its scope ("decide the owner: the W3 review panel (M4) is the natural writer") matches the doc's stated owner. ✔LastStep(gate)cost claim:LastStepreadsoutcomeKind(step)(cmd/ld-runtime/main.go:1171-1172), which returnsKindGatefor the gate, so a started-but-unconcluded gate reads as a miss and is re-attempted — safe for the gate alone. ✔5.
report.sqlconsumer claims — all accurate at headdesign/ld5/report.sql:1991-1996):WHERE e.kind = 'gate.decision'with nostatefilter; NULL/out-of-set decision selected (as(missing)or the raw word) intovocabulary, and any vocabulary row makes the verdictNOT_CERTIFIABLE(report.sql:280). So both the restart pass's decisionless closure row and any started-gate row under this kind certify-block the sample. ✔gate_on_head(report.sql:1068-1080):bool_or(COALESCE(floor_met, FALSE) AND lower(decision) = 'pass')joined onhead_sha— "a passing decision alone cannot satisfy condition 2" is exact. ✔gate_first(report.sql:1008-1020): orders by(payload->>'round')::int ASC NULLS LAST, readsfirst_verdictasunknown,canaryasfalse— matching M0-R30 / M0-R29 as cited. ✔6. #24 superset disposition — verified, and correctly left open
The blob of
design/ld5/contracts-b11-b12.mdat this branch's base-correction commit3da37b9is byte-identical (211152e20dd…, 24,149 B) to #24's head4f71ed9. #24 is open and unmerged onaaron/lab-director. This PR is therefore exactly what its body claims: the approved HOMELAB-2339 text carried verbatim plus the HOMELAB-2329 accuracy delta (61e1ab9+c414d1d). The body's own instruction — land one, not both, and the orchestrator disposes of #24 — is the right disposition; merging both would put conflicting hunks on the same paragraph. That orchestrator step is not this review's to take.7. Test-gap claim
run/replay_refusal_test.godrivesfakeWorld.Step(recordsjournal:<step>:<state>, accepts any payload) — no test at this head sends a gaterefusedRowwrite through the realrecordJournal.Step. The pinned control is the writer's own refusal (TestGateDecisionRowIsRefusedWithoutADecision,cmd/ld-runtime/gate_decision_test.go:41, overok/refused/failed× nil/blank/whitespace). The post-condition (oneholdrow, or apath.steprow) is asserted nowhere. Doc states this accurately.Doc shape
Valid UTF-8; 200 lines; 0 tabs; 0 trailing-whitespace lines; 0 conflict markers; table rows have consistent pipe counts (18×4-field, 51×5-field, 16×6-field, 7×7-field — each table internally uniform).
Verdict: APPROVE. Every claim in the delta at
c414d1dwas re-derived independently from primary sources and verifies; the two findings that held the previous heads (the incomplete census at61e1ab9, and the over-strong "no row at all" consequence corrected in the fix comment) are both discharged at this head. The open items the doc carries (restart-pass decisionless row, refusal row refused, floor fields, path-level test) are correctly recorded as follow-ups with named owners, not silently fixed in a docs PR. This is the required premium contract/oversight leg; the panel now holds an APPROVE from the premium leg and APPROVEs from the DeepSeek lens (1021/1022) at this exact head.Read/comment/review only; no edits, commits, pushes, merge, sign, image, or PR closure performed.
Formal review — premium contract/oversight leg —
ld-reviewer— headc414d1dbd5618298cd9d5a46ec9d0cedebb71890: REQUEST_CHANGESTicket HOMELAB-2329 (W3 carry-over · gate.decision rows record decision for report.sql), doc sibling of HOMELAB-2339; superset of draft #24. Fresh-context premium leg; reviewer ≠ implementer and independent of the DeepSeek tests/claims lens (formal reviews 1021 / 1022 on this head). Primary sources only: the exact-head diff (
main@a451083→c414d1d, measured +17/−4, one file),design/ld5/contracts-b11-b12.mdanddesign/ld5/report.sql@c414d1d,Homelab/infra-core@8294a17dde17e86997e8c9d8c3701eebba98e5d3, and HOMELAB-2329 / HOMELAB-2339. No implementer summary, no prior-reviewer text, and nothing from the superseded head was inherited. Head re-verified bygit ls-remoteimmediately before posting (refs/pull/26/head = c414d1d…,main = a451083…).No CI on this repo, measured not assumed: combined status at
c414d1d→total_count: 0;design/ld5/report.sqlat head is byte-identical tomain(sha1ce15b2dd…), so the frozen reader is untouched by this PR. Every check below was run by me against source.F1 — one quoted literal in §2 is transposed:
refusal: evidence-replayed→ the code writesreplayed-evidence(one-word additive edit owed)§2's refusal bullet states the ticket-level row lands as:
Read against
infra-core@8294a17:RefusalEvidenceReplayed = "replayed-evidence"(run/run.go:88);out.Refusalis set to it atrun/run.go:686and placed in therefusedRowfield map atrun/run.go:690.recordPathRefusal(run/loop.go:1520-1537) writes thepickup.rejectedpayload with"refusal": out.Refusal,"check": "path-refused","sign_consumed": true— so the row carriesrefusal: "replayed-evidence", notevidence-replayed.report.sql@ head has no reader ofpickup.rejected.refusal(nopath-refused/replayedvocabulary arm), so no report semantics hinge on the word — this is precision, not behavior. But this document's own stated authority is "it says which row each write path produces, with which columns and which frozen value, and it is the document the runtime is reviewed against" — and a transposed literal in a backticked payload value is exactly the class of slip review 1017 held the previous head for (cause: runtime-replaced→runtime-replaced-mid-step, C1), which this head correctly fixed. The same standard applies here.Why two prior reviews at this head missed it: both 1021 and 1022 quote the same
evidence-replayedspelling in their own text — the transposition originates in the document and was quoted from it, not re-derived from the code. I derived it from the code. The owed edit is one word in §2 (evidence-replayed→replayed-evidence); nothing else in the delta fails.Everything else in the delta verifies at
8294a17— enumerated, not sampledJournal.Stepcall sites exist in the non-test dispatcher at this head:run/run.go:868(p.journal),run/run.go:891(Path.refusedRow),run/stepboundary.go:421(boundaryRow). The third always passesStepBoundary, andstepKind(cmd/ld-runtime/main.go:962-970) mapsStepBoundary→KindStepBoundarybefore the gate arm — it can never produce a gate row. No fourth route exists; the parenthetical dismissal is exact.stepEventKind(step)(run/loop.go:1031-1036) takes no state argument and returnsrecord.KindGateforStepGatein any state;closeOrphanMidStep(run/loop.go:1185-1212) writes the orphaned gate's terminalfailedrow with payload{step, state, reason, cause, orphan: true, workflow_id, sign_activity_id, started_at, reset, at}— nodecision— throughLoop.appendEvent(run/loop.go:1549) →Store.Append;appendEventenforces operator-attribution only and never runsValidGateDecision. "Goes round the check and is written anyway" is exact, including the consequence (a decisionlessgate.decisionrow the state-less vocabulary arm reads as(missing)).causeliteral isruntime-replaced-mid-step(RecoveryCauseRuntimeReplaced,run/loop.go:923) — the C1 fix is correct, no bare literal remains in the file.Run()walksfor i, step := range Steps(run/run.go:638) andStepscontainsStepGate(run/run.go:61), so the refusal branch (run/run.go:688) can hand the gate torefusedRow. Fields arereason/refusal/branch/replayed_head/branch_head/reset+at— nodecision;refusedis terminal (run/run.go:111-117), sostepKindyieldsrecord.KindGateandrecordJournal.Step(cmd/ld-runtime/main.go:1007-1046) refuses beforesampleFor/Append— the journal is left unchanged. Both quoted error chains match the code verbatim, including the backtick-quoted`decision`in the refusal message.recordJournal.Step's alone. Adecisionon the gate's start row (kind == KindStep, i.e.running) is refused; missing/blank/out-of-vocabulary on the terminal row is refused (record.ValidGateDecision,record/record.go:213-240, frozen set exactlypass|block|hold). "The three gate-row writes do not share one enforcement path" is the accurate framing; presenting any one as the census would not be.dispatch's error only logs (run/loop.go:602-604);if out.Refusal != "" { l.recordPathRefusal(...) }(run/loop.go:611-614) is not gated on the error;dispatchreturnsoutalongsidederr(run/loop.go:1487-1495).check: path-refusedandsign_consumed: trueare the exact literals written (run/loop.go:1527). What is missing is every per-step row, and the doc says exactly that — modulo the one transposed literal in F1.report.sqlbehavior, all three reads: the vocabulary arm (report.sql:1991-1996) selects onkind = 'gate.decision'with no state filter and emitsCOALESCE(lower(payload->>'decision'), '(missing)')for NULL/out-of-set → NOT_CERTIFIABLE;gate_on_head(:1068-1083) isbool_or(COALESCE(floor_met,FALSE) AND lower(decision) = 'pass')joined onhead_sha, so a passing decision alone cannot satisfy condition 2;gate_first(:1008-1020) readsroundfor ordering,first_verdictdefaultingunknown,canarydefaultingfalse. §6's floor-fields row is accurate, and its ownership split (HOMELAB-2336 for the panel-floor fields, HOMELAB-2329 follow-up for both gate-row writer gaps) is stated, not orphaned.run/replay_refusal_test.godrivesfakeWorld.Step(run/run_test.go:186-191), which accepts any payload; nothing at this head sends a gaterefusedRowwrite through the realrecordJournal.Step. The pinned negative controlTestGateDecisionRowIsRefusedWithoutADecision(cmd/ld-runtime/gate_decision_test.go:41) coversok/failed/refused, and the positive controls (TestTerminalGateRowCarriesTheDerivedDecision,TestGateStartRowIsNotADecisionRow,TestReportSQLConsumesTheWrittenDecision) exist — the §2 bullet's test characterisation is correct.contracts-b11-b12.mdat3da37b9(this branch's HOMELAB-2339 commit) is211152e2…, byte-identical to #24's head4f71ed9— the base correction is carried verbatim and the delta is reviewed as a delta. Merging both would put conflicting hunks on the same paragraph; land one, close the other (orchestrator disposition, not mine).report.sqlis untouched (byte-identical); running rows invent no terminal decision (both the code check and the doc say so); historical rows are explicitly not rewritten; the negative/positive controls exist in the merged code. The live scratch-gate Verify line is the integration worker's drive, not this doc PR's.Doc shape and hygiene
UTF-8, 200 lines, zero conflict markers, zero trailing-whitespace lines, every contiguous table block pipe-balanced. Scope is one file, +17/−4; the delta commit
c414d1dis +7/−4 in four places, exactly as the fix comment claims.Boundaries
Reviewer capabilities only: read, and this formal review / PR comment. No edits, commits, pushes, merges, images, signatures, drives, PR closures, and no write to any protected database. No prior review or comment was edited, dismissed (other than the platform's same-key supersession behavior of this shared
ld-revieweridentity, which is not a decision I make), or deleted. If the head moves, this verdict does not carry.Disposition: one-word additive fix to §2 (F1), then a fresh delta re-review of the new head. No other edit is owed — the census, both enforcement-path characterisations, the cause literal, the §6 ownership rows, the report.sql behavior claims, and the #24 superset statement all verify.
Verdict of record —
ld-reviewerpremium contract/oversight leg — headc414d1dbd5618298cd9d5a46ec9d0cedebb71890: REQUEST_CHANGESFormal review: 1023 (
REQUEST_CHANGES,commit_id=c414d1dbd5618298cd9d5a46ec9d0cedebb71890, body 9,446 chars, GET-verified visible in/pulls/26/reviews,model: omniroute/kimi-pool;official=falsebecause this account is read-only on this repo — the visible row plus this comment are the record).pins_full_head=true— head re-verified bygit ls-remotebefore posting; if the head moves this verdict does not carry.One additive edit is owed (F1): §2 quotes the ticket-level
pickup.rejectedrow as carryingrefusal: evidence-replayed; the code writesrefusal: "replayed-evidence"(RefusalEvidenceReplayed,run/run.go:88→out.Refusal→recordPathRefusal,run/loop.go:1527). One word in §2; same class as the C1 hold (cause literal) that 1017 raised at61e1ab9and this head fixed. No reader inreport.sqlconsumes the word, so this is precision, not behavior — but this document's stated authority is "which row each write path produces, with which columns and which frozen value". Both DeepSeek reviews at this head (1021/1022) carry the same transposed spelling — it originates in the document; I derived the value from the code.Everything else verifies, enumerated in review 1023: the three-writer census is exhaustive (exactly three
Journal.Stepcall sites at8294a17+ the restart pass's non-Journal.Steproute); "round vs through-and-refused" is accurate for both; thecauseliteral, the refusal error chain, the ticket-level row landing (check: path-refused,sign_consumed: true— literals correct), §6 ownership (HOMELAB-2329 follow-up + HOMELAB-2336), all threereport.sqlreads (state-less vocabulary arm,gate_on_head,gate_first), the test-gap claim, and the #24 superset (base blob211152e2…byte-identical at3da37b9and4f71ed9).Chronology note: per this repo's measured same-key behaviour, my post may flip
dismissedflags on earlierld-reviewerrows — that is platform supersession on the shared identity, not a decision by me; no review or comment was edited or deleted. Reviewer capabilities only (read + comment + formal review): no edits, commits, pushes, merges, images, signatures, drives or PR closures.a86085c9c5: APPROVE** model: omniroute/deepseek-flash class: docs only — one file, `design/ld5/contracts-b11-b12.md`, +17/−4 vs `main`. Reviewed delta: `c414d1d→a86085c`. ## What this head is, proved rather than read off the commit message The commit says "quote the refusal constant exactly — replayed-evidence, not evidence-replayed". That is the **whole** change, and it is one token: - Normalising the new head and its parent by replacing `replayed-evidence` with a placeholder makes the two documents **byte-identical** — every other line, including the three gate-row census bullets, the `Revised` header and both §6 rows, is unchanged. No collateral edit rode along. - The constant is `RefusalEvidenceReplayed = "replayed-evidence"` (`run/run.go:88`), and it is the value the replay refusal assigns to `out.Refusal` (`:686`). The document now quotes it exactly: **3** occurrences of `replayed-evidence`, **0** of `evidence-replayed`. - Origin of the finding, credited properly: the **premium (`kimi-pool`) leg, review 1023 at `c414d1d`** (10:16:04Z, REQUEST_CHANGES, 9,446 chars) — so the premium delta lens I recorded as owed has in fact reviewed this PR, and this head answers it. Not my finding, not claimed as mine. Structure of the new head: unchanged elsewhere; valid UTF-8, no conflict markers, no trailing whitespace, no unbalanced table rows. ## Why APPROVE A one-token correction to a quoted payload literal, verified against the constant that produces it, with nothing else in the document touched — and it discharges the premium lens's blocker at the head that lens reviewed. That is exactly the class of delta that should not consume a round. ## Recorded, not blocking - **The premium leg is now owed at *this* head**, in the same sense it was at the last: 1023 pins `c414d1d`. A docs-only PR needs one lens under the safety policy; the worker's own sequence asked for two. If the panel is read as two-lens, the kimi row has to be re-posted at `a86085c` — reviewers do not dispatch reviewers, so that is the orchestrator's call. - **C1 from my `c414d1d` review is still open and still non-blocking:** the `Revised` header cites reviews 1013 / 1015 / 1017, and every `ld-reviewer` row before 1023 is now `dismissed=true` — a reader following the citation lands on a dismissed row. It does not affect any claim in the document; folding the citation list into a later edit is enough. - **The code defect this document records is unchanged and live** on `dispatcher@sha256:66ef87f1…`: a gate replay refusal loses its step row and the drive errors. Owner: a follow-up ticket under HOMELAB-2329 (reported on 2329 and as comment 16719 on `infra-core#660`, explicitly not a verdict and not a revert request). - **`#24` / `#26` remain mutually exclusive** — land one, close the other. ## Chronology on this PR (all rows left as posted; nothing edited or deleted) 1017 RC (other session, `61e1ab9`) → 1018 APPROVE (mine, posted blind, dismissed 1017) → 1019 RC (mine, corrects 1018) → 1020 RC premium (other session) → 1021 APPROVE (mine, `c414d1d`) → 1022 APPROVE (other session, `c414d1d`) → 1023 RC premium (`c414d1d`) → **this review at `a86085c`**. This verdict does not merge, sign, image, drive or tick any Verify line. No files edited, committed or pushed by me.Verdict of record — ld-reviewer — head
a86085c9c5: APPROVEFormal review: 1024 (
APPROVED,commit_id=a86085c9c57f723d348b508f89c62583d7e0bf82, body 3,472 chars, GET-verified visible,dismissed=false,stale=false).pins_full_head=true.One thing to say plainly about the formal state: posting 1024 made Forgejo mark the premium lens's 1023 (kimi-pool, REQUEST_CHANGES at
c414d1d)dismissed=true. That was not a judgment I made about 1023 — it is the same-key dismissal this lane has measured all night — and the substance 1023 raised (therefusal:literal) is fixed at this head, which I verified againstRefusalEvidenceReplayed = "replayed-evidence"(run/run.go:88) rather than assuming. If a same-head premium verdict is required, 1023 has to be re-posted ata86085c; that is the orchestrator's dispatch, not mine.model: omniroute/deepseek-flash
class: docs only — one file,
design/ld5/contracts-b11-b12.md, +17/−4 vsmain. Reviewed delta:c414d1d → a86085c.What this head is, proved rather than read off the commit message
The commit says "quote the refusal constant exactly — replayed-evidence, not evidence-replayed". That is the whole change, and it is one token:
replayed-evidencewith a placeholder makes the two documents byte-identical — every other line, including the three gate-row census bullets, theRevisedheader and both §6 rows, is unchanged. No collateral edit rode along.RefusalEvidenceReplayed = "replayed-evidence"(run/run.go:88), and it is the value the replay refusal assigns toout.Refusal(:686). The document now quotes it exactly: 3 occurrences ofreplayed-evidence, 0 ofevidence-replayed.kimi-pool) leg, review 1023 atc414d1d(10:16:04Z, REQUEST_CHANGES, 9,446 chars) — so the premium delta lens I recorded as owed has in fact reviewed this PR, and this head answers it. Not my finding, not claimed as mine.Structure of the new head: unchanged elsewhere; valid UTF-8, no conflict markers, no trailing whitespace, no unbalanced table rows.
Why APPROVE
A one-token correction to a quoted payload literal, verified against the constant that produces it, with nothing else in the document touched — and it discharges the premium lens's blocker at the head that lens reviewed. That is exactly the class of delta that should not consume a round.
Recorded, not blocking
c414d1d. A docs-only PR needs one lens under the safety policy; the worker's own sequence asked for two. If the panel is read as two-lens, the kimi row has to be re-posted ata86085c— reviewers do not dispatch reviewers, so that is the orchestrator's call.c414d1dreview is still open and still non-blocking: theRevisedheader cites reviews 1013 / 1015 / 1017, and everyld-reviewerrow before 1023 is nowdismissed=true— a reader following the citation lands on a dismissed row. It does not affect any claim in the document; folding the citation list into a later edit is enough.dispatcher@sha256:66ef87f1…: a gate replay refusal loses its step row and the drive errors. Owner: a follow-up ticket under HOMELAB-2329 (reported on 2329 and as comment 16719 oninfra-core#660, explicitly not a verdict and not a revert request).#24/#26remain mutually exclusive — land one, close the other.Chronology on this PR (all rows left as posted; nothing edited or deleted)
1017 RC (other session,
61e1ab9) → 1018 APPROVE (mine, posted blind, dismissed 1017) → 1019 RC (mine, corrects 1018) → 1020 RC premium (other session) → 1021 APPROVE (mine,c414d1d) → 1022 APPROVE (other session,c414d1d) → 1023 RC premium (c414d1d) → this review ata86085c.This verdict does not merge, sign, image, drive or tick any Verify line.
No files edited, committed or pushed by me.
Formal review — tests lens (DeepSeek), delta at head
a86085c9—ld-reviewerVerdict: APPROVE. No blocker. Every claim in the delta — writer census, the two enforcement paths, the refusal chain, the
report.sqlbehaviour, the test-adequacy sentences, the #24 superset statement — verifies against primary source atinfra-core@8294a17. One non-blocking accuracy note (N1) and one cosmetic naming note (N2) are recorded below; neither misstates a row, a column or a frozen value.Ticket HOMELAB-2329 (W3 carry-over — gate.decision rows record decision for report.sql), doc sibling HOMELAB-2339, superset of draft #24. Fresh-context tests lens; reviewer ≠ implementer and independent of the premium/oversight legs (1022 / 1023 / 1024). Primary sources only: this exact-head diff, the head blob of
design/ld5/contracts-b11-b12.md,design/ld5/report.sqlat head and atmain@a451083,Homelab/infra-core@8294a17dde17e86997e8c9d8c3701eebba98e5d3, reviews 1017 / 1020 / 1022 / 1023, and the two tickets. No implementer summary, no builder log taken on trust.No CI on this repo, measured not assumed:
contents/.forgejo@a86085c9→ 404 "object does not exist"; combined status at head →total_count: 0. Nothing here is green; this verdict rests on source. Head re-verified by API andgit ls-remote refs/pull/26/headimmediately before posting:a86085c9…, open, unmerged — if it moves this verdict does not carry.What this head is. The last commit is exactly the one-token fix its message names:
evidence-replayed→replayed-evidence, in one line, nothing else. Head has 3 occurrences ofreplayed-evidenceand 0 ofevidence-replayed.design/ld5/report.sqlat head is byte-identical tomain(sha138ca2896…), so the frozen reader is untouched by this PR.Verified at primary source (re-derived here, not quoted from a prior review)
ok→pass,failed→block,refused→hold—run.GateDecisionForState(run/run.go:133-143) is exactly that switch, returningrecord.GatePass/GateHold/GateBlock;runningmaps to nothing.p.step's deferred writer stampsfields["decision"]only forstep == run.StepGatewhen the mapping exists (run.go:806-812). CONFIRMED.stepKind(step,state)returnsKindGateonly forStepGate && run.TerminalState(state)(cmd/ld-runtime/main.go:961-971), andTerminalStateisok/failed/refused(run.go:111-117), so arunninggate ispath.step.recordJournal.Step(main.go:1007-1042) refuses adecisionkey on that start row, and on thegate.decisionrow refuses a missing, blank (TrimSpace == "") or out-of-set decision viarecord.ValidGateDecision— in both directions, and beforesampleFor/Append— fail-closed exactly as the doc says (theworkflows.statusanalogy holds:Open/ClosecallValidStatus). CONFIRMED.Journal.Stepcall sites exist at this head:run/run.go:868(p.journal),run/run.go:891(Path.refusedRow),run/stepboundary.go:421(boundaryRow). The third always passesStepBoundary, andstepKindmapsStepBoundary→KindStepBoundarybefore the gate arm, so it can never produce a gate row. No fourth route exists: the only other way a gate row reaches the journal isstepEventKind→appendEvent→Store.Append.recordJournal.Stepis therefore "neither the only way a gate row reaches the journal nor the only way one is attempted" — exact, and "the writes below do not share one enforcement path" is the correct claim to make instead of presenting any one path as the census. CONFIRMED.stepEventKind(step)(run/loop.go:1031-1036) takes no state argument and returnsrecord.KindGateforStepGatein any state.closeOrphanMidStep(run/loop.go:1190-1208) writes payload{step, state: failed, reason, cause: RecoveryCauseRuntimeReplaced, orphan: true, workflow_id, sign_activity_id, started_at, reset, at}— nodecision— viaLoop.appendEvent(run/loop.go:1549-1559) →Store.Append, which enforces operator attribution only and never runsValidGateDecision. Cause literal isruntime-replaced-mid-step(run/loop.go:923), quoted exactly in the delta (1017-C1 fixed, no bareruntime-replacedremains). CONFIRMED.Path.refusedRowisrun/run.go:886-895, with exactly one call site,run/run.go:688, insideRun'sfor i, step := range Steps, andStepscontainsStepGate(run/run.go:61). Fields are exactlyreason, refusal, branch, replayed_head, branch_head, reset+at— nodecision— stateStateRefused. SostepKindyieldsKindGateandStepreturns before any write. The three error links match the code verbatim apart from quoted-punctuation (N1):Step'srefusing to write a gate.decision row with no …,refusedRow'sjournal %s: write refused %s row: …,Run'srun: %s %s: the replay refusal could not be journalled: …. Consequence as stated: norefusedrow under the gate's node key and nogate.decisionrow. CONFIRMED.dispatchreturnsoutwitherr(run/loop.go:1487-1495); the caller logspath failed(:603) andif out.Refusal != "" { l.recordPathRefusal(...) }(:611-614) is not gated on the error.recordPathRefusal(:1520-1537) writescheck: "path-refused",refusal: out.Refusal(=RefusalEvidenceReplayed="replayed-evidence",run/run.go:88),sign_consumed: true. So "not wholly invisible; what is missing is every per-step row, and the drive reaches its slot as an error" is the accurate statement, and it is the opposite of 1017's claim that would have been wrong if carried forward. CONFIRMED.report.sqlbehaviour, all three reads, at head — the vocabulary arm (:1991-1996) selects onkind = 'gate.decision'with nostatefilter and emitsCOALESCE(lower(payload->>'decision'), '(missing)')for NULL/out-of-set, which is a vocabulary row and makes the runNOT_CERTIFIABLE(M0-R42);gate_on_head(:1068-1082) isbool_or(COALESCE(payload->>'floor_met', FALSE)::boolean AND lower(decision) = 'pass'), so a passing decision alone can never satisfy condition 2;gate_first(:1008-1020) orders by(payload->>'round')::int ASC NULLS LAST, defaultsfirst_verdictto'unknown'(M0-R30) andcanarytoFALSE(M0-R29). All three match the doc. CONFIRMED.fakeWorld.Step(run/run_test.go:186-192) recordsjournal:<step>:<state>and accepts any payload, sorun/replay_refusal_test.gocannot distinguish a gate refusal that works from one that is refused by the real writer — nothing at this head drives a gaterefusedRowthroughrecordJournal.Step(refusedRowappears in no test; the one row-count assertion in the replay test countsStepPushrows). What is pinned is the writer's own refusal:TestGateDecisionRowIsRefusedWithoutADecision(cmd/ld-runtime/gate_decision_test.go:41-70) overok/refused/failed×nil/""/" ", asserting both the MISSING-decision refusal and that the journal is left unchanged; the out-of-vocabulary control (:74) and the positive controlsTestTerminalGateRowCarriesTheDerivedDecision/TestGateStartRowIsNotADecisionRow/TestReportSQLConsumesTheWrittenDecisionexist. "The post-condition has no test at all, and the fake is what hides the gap" is exactly right.TestReportSQLConsumesTheWrittenDecisionhas to handfloor_met: truein its own fixture payload (gate_decision_test.go:252-254) to make the floor pass, which is test-level evidence for the doc's sentence that withfloor_met"supplied by the fixture" condition 2 passes while rows the writer actually produces do not. (The scratch-sample run the sentence cites is an artefact claim I did not re-derive.)LastStepfor the gate readsoutcomeKind→KindGate(main.go:975-983), so a started-but-unconcluded gate reads as a miss and is re-attempted;p.gatereads branch/diff/CI andp.landowns the merge, so a re-attempt merges nothing, and no other step'soutcomeKindchanges. CONFIRMED.design/ld5/contracts-b11-b12.mdat this branch's3da37b9(sha13e38aebb…) is byte-identical to #24's head4f71ed9; both differ frommain@a451083(86adce73…). PR delta is one file, +17/−4; the last commit is the one-token fix only. "Land one, close the other" is correct — merging both puts conflicting hunks on the same §2 paragraph. CONFIRMED.report.sqluntouched (byte-identical), decision values drawn from the existing frozen set, no historical row rewritten, and the negative/positive controls the AC asks for live in the mergedinfra-core#660. The AC line "CI on an isolated branch" is discharged there, not here — this repo has no CI, and the doc does not claim any.N1 (non-blocking) — one quoted error literal is not the literal
§2 writes the writer's refusal as
journal …: refusing to write a gate.decision row with no 'decision'. The code emits backticks:refusing to write a gate.decision row with no \decision`: report.sql reads every gate.decision row as pass|block|hold…(cmd/ld-runtime/main.go:1027), and the pinned test asserts exactly that spelling (gate_decision_test.go:62,strings.Contains(err.Error(), "no `decision`")`). So a reader grepping the quoted text finds nothing.Why I record this and do not block on it: unlike 1017-C1 (
cause:value) and 1023-F1 (refusal:value), this is an error message quoted with a leading ellipsis, not a payload column or a frozen vocabulary word — this document's stated authority is which row/columns/value each path writes, and no row, column or value is misstated by it. It is a one-character fix whenever the file is next touched (a double-backtick span, orno "decision"→ backticked correctly). If the follow-up worker in §6 greps it, they should greprefusing to write a gate.decision row with no.N2 (cosmetic) — a local name attributed to the wrong frame
§2 says "
dispatchreturnsoutalongsidederr(run/loop.go:1487-1495)". The two-value return and the line range are right, but insidedispatchthe local iserr;derris the caller's binding atrun/loop.go:601. The substance (the error does not gate the refusal row) is unaffected and was verified independently at §7 above.Limits
Reviewer capabilities only: read, and this formal review. I edited nothing, committed nothing, pushed nothing, merged nothing, signed nothing, produced no image, ran no drive, wrote no database row, and did not close or comment on #24. No earlier review or comment was edited, and I make no disposition decision about #24 — that is the orchestrator's. Reviewer ≠ implementer: this pass inherited no implementer context and worked only from the sources named above.
APPROVE at
a86085c9c57f723d348b508f89c62583d7e0bf82— the recorded code defect this document documents (a gate replay refusal losing its step row and erroring the drive, plus the restart pass's decisionless closure row) is real at8294a17, both are named with owners in §6, and the text now describes them accurately. N1 does not carry to another round.Identity disclosure (mine, recorded rather than hidden)
A first attempt to post this review (id
1025, same head, same verdict, same sources) went up under the operator's Forgejo identity — this session picked up the ambientFORGEJO_API_KEYinstead of the sanctioned reviewer credential. I deleted that row before posting this one. It was never an operator approval and must not be read as one;ld-reviewer(user id 47) is the identity of record for this verdict.Formal review — premium contract/oversight lens (kimi-pool), exact head —
ld-reviewerFresh premium second-lens review, reviewer != implementer, independent of the DeepSeek legs (1021/1022/1024/1026). Primary sources only: the exact-head diff (
a86085c9vsmain),infra-core@8294a17dde17e86997e8c9d8c3701eebba98e5d3source read directly, the frozendesign/ld5/report.sql, PRs #24/#26, ticket HOMELAB-2329, prior reviews 1017/1020/1022/1023. No CI exists on this repo; every check below was run by me against source. This head is the one-line fix for review 1023's F1; I re-verified the full contract, not only the delta.Verdict: APPROVE. The single owed edit is correct, and the full document verifies claim-by-claim.
F1 fix, verified independently of the fix report
refusal: replayed-evidence. The code constant isRefusalEvidenceReplayed = "replayed-evidence"(run/run.go:88at8294a17) andrecordPathRefusalwritesout.Refusalverbatim (run/loop.go:1520-1541), so the quoted payload literal is now exact. The transposed spellingevidence-replayedsurvives nowhere in the file at this head (grepped).refs/pull/26/headbotha86085c9c57f723d348b508f89c62583d7e0bf82; no other line of the file changed vs the reviewedc414d1dtext (31,178 bytes both, zero trailing whitespace, §6 rows all 3-column).Writer census — exhaustive, both enforcement directions accurate
Exactly three
recordJournal.Stepcall sites exist at8294a17outside tests:run/run.go:868(p.journal, the enforcing path that stampst.evidence()includinghead_sha),run/run.go:891(Path.refusedRow), andrun/stepboundary.go:421(boundaryRow, alwaysStepBoundary— verified it can never produce a gate row). The fourth gate-row route is non-Journal.Step:closeOrphanMidStep(run/loop.go:1190-1213) →Loop.appendEvent→Store.Append, bypassingrecord.ValidGateDecision. The doc's "round vs through-and-refused" framing is accurate for both the restart pass (written anyway, decisionless) and the replay refusal (refused by the check).Replayed-evidence refusal chain — every link verbatim
Path.refusedRowatrun/run.go:886-895, invoked from the replay refusal atrun/run.go:688-694; the loop walksrun.Steps, which containsStepGate(run/run.go:61). Fields are exactlyreason,refusal,branch,replayed_head,branch_head,reset,at— nodecision.stepKind(cmd/ld-runtime/main.go:962-970) is gated onrun.TerminalState;TerminalStatecoversok/failed/refusedand excludesrunning(run/run.go:111-117), so a terminalrefusedgate maps torecord.KindGateand the writer refuses:journal %s: refusing to write a gate.decision row with no \decision`: …(main.go:1027), vocabulary checkrecord.ValidGateDecisionover the frozen set (record/record.go:214-240:GatePass="pass",GateBlock="block",GateHold="hold"`, verified by hex dump).refusedRowwraps the writer error (journal %s: write refused %s row: %w,run.go:891) andPath.Runreturnsrun: %s %s: the replay refusal could not be journalled: %w(run.go:692-694). One cosmetic nit: the doc renders the quoted writer error with single quotes (no 'decision') where the code string uses backticks — punctuation only, word-for-word identical; non-blocking.pickup.rejecteddistinction (against review 1017's earlier overstatement) is correct and precisely stated:dispatchreturnsoutalongside the error (run/loop.go:1487-1496), and theif out.Refusal != ""branch (loop.go:614-616) sits outside theif derr != nilblock (loop.go:602-604) — verified in the control flow, not inferred.recordPathRefusalwrites kindpickup.rejectedwithcheck: path-refused,refusal: out.Refusal,sign_consumed: true(loop.go:1527-1541); thepath failedlog line is atloop.go:603. Step rows are what is missing; the ticket-level row lands.LastStep(gate)readsgate.decisiononly (outcomeKind,main.go:975-983) — the named re-attempt cost is real and correctly bounded.Restart-pass gap row — accurate
stepEventKind(loop.go:1031-1036) returnsrecord.KindGateforStepGatein any state;closeOrphanMidStep's payload carriesstate: failed,cause: runtime-replaced-mid-step(RecoveryCauseRuntimeReplaced = "runtime-replaced-mid-step",loop.go:923),orphan: true, and nodecision, written viaappendEvent→Store.Append. The doc's onlyruntime-replacedoccurrence is the full literal; no bare spelling survives.report.sql — all three reads verified against the frozen file
gate.decisionrow of the sample with no state filter,COALESCE(lower(decision), '(missing)')outsidepass|block|hold⇒ vocabulary row; the second arm checksgate.decision.head_sha. Theid > sample_event_idwindow is present on both.gate_on_head(line 1068-1073):bool_or(COALESCE(floor_met,FALSE) AND lower(decision)='pass')— the §6 quotefloor_met AND lower(decision) = 'pass'is exact.gate_first(line 1008-1020): orders by(payload->>'round')::int,COALESCE(first_verdict,'unknown'),COALESCE(canary,FALSE)— matching §6's M0-R30/M0-R29 statements.§6 ownership and superset — verified
run.GateDecisionForState(run.StateRefused)→holdalready mapped atrun.go:137-138); panel-floor fields (floor_met,round,first_verdict,holds[],canary) → HOMELAB-2336. Grep at8294a17confirms no writer produces any of the five panel-floor fields — the §4 "no" is accurate.design/ld5/contracts-b11-b12.mdblob211152e20dd370b09f049679ef75fcedf8abb810is byte-identical at3da37b9(this branch's first commit, HOMELAB-2339 verbatim) and at #24's head4f71ed93f78d— measured, not asserted. Land exactly one of #24/#26.TestGateDecisionRowIsRefusedWithoutADecisionpins the writer's own refusal over ok/refused/failed (cmd/ld-runtime/gate_decision_test.go:41-47);run/replay_refusal_test.godrivesfakeWorld.Step(run/run_test.go:186-192), which returns nil for any payload — no gaterefusedRowthrough the real writer exists.Disposition
No edit owed. Same-key supersession note (stated, not smoothed): all formal rows on this PR share the
ld-revieweridentity; per platform behaviour this APPROVE will flipdismissedon earlier rows including the DeepSeek lens 1026 at this head — that is a mechanical consequence of one identity key, not a repudiation of 1026's content, which I did not use as a source. Reviewer capabilities only: no edits, commits, pushes, merge, image, signature, drive, PR closure, or DB write by this leg; credentials minted fresh via the sa-operator/kv sanctioned path into a 0700 mktemp dir with operator-token revoke, nothing printed.Merge record + review-state disposition (pi-landing, 2026-10-29T10:4xZ)
Merged as pi-landing; merge commit
069f00de5e8098117747fb23eb96830f18dbe8ad; head at mergea86085c9c57f723d348b508f89c62583d7e0bf82;main== merge commit (read back). No CI in this repo (0 statuses on head, no.forgejo— measured, not assumed); evidence = the 23/23 doc checks re-run at this exact head (doc-check.sh, fails=0) plus the two substantive formal reviews below.Review disposition at merge time (measured via GET after 1027 posted):
a86085c…(== head), 12,628 chars:dismissed=true.model: omniroute/kimi-pool, APPROVED, pinneda86085c…(== head), 7,429 chars:dismissed=false— the sole simultaneously non-dismissed formal row.The 1026 dismissal is the same-key supersession this lane has measured repeatedly (all reviews share the
ld-revieweridentity; its newest post flips the prior row'sdismissedflag): observed correlates — head unchanged since before 1026 posted, no dismissal comment,dismissed_bynot populated. It is platform behaviour, not a DeepSeek repudiation; both substantive exact-head APPROVED records stand, and the panel read is: two-lens review complete at the merged head, one lens simultaneously non-dismissed — the same disposition recorded for infra-core#660 (comment 16701). Nothing edited or deleted by me.