WIP: HOMELAB-2342: docs(contracts): infra.rerun is written today — two caused kinds #25

Draft
aaron wants to merge 1 commit from plane/HOMELAB-2342-infrarerun-contract into main
Owner

Draft for independent review. HOMELAB-2342 (docs-only, 3 lines).

What changed

design/ld5/contracts-b11-b12.md §4's Written by (today) cell said not yet for both infra.rerun.cause and infra.rerun.evidence_kind. The row is written on the reachable path, so both cells now state what is actually written, and §6's infra.rerun "Missing" row is corrected the same way so the document does not contradict its own §4.

Nothing else moved: no frozen value, no report.sql/M0 semantics, no gate text, no Revised header row (see merge order), no data.

Text → code → report evidence (infra-core origin/live@8f1f96c)

Contract sentence Code of record
the row is appended run/run.go:608 execWithInfraRerun → recordJournal.RecordInfra (run/run.go:399 seam, cmd/ld-runtime/main.go:875) → record.KindInfraRerun = "infra.rerun" (record/record.go:103)
the path calls it for every step run/run.go:582 state, reason, err := p.execWithInfraRerun(ctx, t, step)
wired, not dead code cmd/ld-runtime/main.go:433 Infra: kube
build step only run/kube_classify.go:204 ClassifyInfra returns false for any step != StepBuild
only oom_kill / eviction run/kube_classify.go:218 InfraCauseOOMKill, InfraEvidenceKernelRecord; :224 and :227 InfraCauseEviction, InfraEvidenceEvictionEvent — nothing else is returned
the other four are constants with no producer run/admission.go:83-94 declares all six causes and five evidence kinds; the only InfraCause* reads outside tests are the three lines above
the eviction row's two evidence sources k.evicted(ctx) (evictor event) and k.poolPodReplaced(ctx, t) (pod replaced/gone) at kube_classify.go:222-228
absent evidence_kind is an unevidenced draw, not a vocabulary row report.sql:966-972 infra_event: evidenced = COALESCE(m.evidence_kind IS NOT NULL AND lower(payload->>'evidence_kind') = m.evidence_kind, FALSE); report.sql:1984-1987 the vocabulary arm skips evidence_kind IS NULL
cause and evidence_kind are read from the payload report.sql:1976-1987; row column node_key read at report.sql:966

Tests that pin the writer shape: run/infra_rerun_test.go (TestInfraRerunRedrawsEvictedBuilder asserts one row with node_key=LDF-3.build, cause=eviction, evidence_kind=eviction_event, attempt=1) and cmd/ld-runtime/main_test.go:402 (RecordInfra writes under the ticket's named sample).

Negative check

grep -n 'infra.rerun' design/ld5/contracts-b11-b12.md | grep 'not yet' exits 1 after the change: no infra.rerun line carries not yet any more. git diff is 3 lines (-3 +3), all in §4/§6; grep -c 'Edited\|Revised' unchanged (no header row added).

Merge order vs HOMELAB-2339 (PR #24)

The two changes do not depend on each other, but they touch adjacent lines of the §4 table, so the second merge of the pair needs one line-level resolution: keep this PR's two infra.rerun rows and 2339's gate.decision.decision row. Verified mechanically: git merge-tree --write-tree reports one content conflict, both sides being exactly those three table rows; resolving it to both sides' lines leaves the document coherent (probe performed and discarded, nothing pushed from it).

Preferred order: 2339 first, then 2342 (2339 carries the Revised header row that states the code of record; this PR deliberately adds none, to keep the delta at 3 lines). Reverse order needs the same one-line resolution. No file of 2339's is edited here.

CI reality

This repository has no workflows: no .forgejo/, no .github/, no workflow file anywhere in the tree at this head (the only .yaml present is the test golden internal/harness/claudecode/testdata/golden/slice.yaml). There will be no CI status on this PR, and none is claimed.

Deliberately out of scope (for the reviewer/orchestrator, not fixed here)

  1. §5 still reads "infra.rerun.node_key must name the killed run's node key … — HOMELAB-2252's writer, not W2's path." The rule is satisfied by the live writer, but the possessive tag now reads as if W2's path does not write the row. It is a naming rule, not a not yet status, so it was left alone. Needs a separate word-level ticket if the orchestrator wants it changed.
  2. report.sql's infra_event also reads payload->>'accept_test_sha' (report.sql:969); no writer sets it (the writer's fields are step, attempt, cause, evidence_kind, node_key, head_sha — run/run.go:626-631). No contract text claims otherwise, so nothing here is stale; it is an unrecorded writer/reader gap worth its own ticket.

Gates

Docs-only, reversible, no operator-gate item, no live work, no merge from this session.

Draft for independent review. HOMELAB-2342 (docs-only, 3 lines). ## What changed `design/ld5/contracts-b11-b12.md` §4's **Written by (today)** cell said `not yet` for both `infra.rerun.cause` and `infra.rerun.evidence_kind`. The row is written on the reachable path, so both cells now state what is actually written, and §6's `infra.rerun` "Missing" row is corrected the same way so the document does not contradict its own §4. Nothing else moved: no frozen value, no `report.sql`/M0 semantics, no gate text, no `Revised` header row (see merge order), no data. ## Text → code → report evidence (infra-core `origin/live@8f1f96c`) | Contract sentence | Code of record | |---|---| | the row is appended | `run/run.go:608` `execWithInfraRerun` → `recordJournal.RecordInfra` (`run/run.go:399` seam, `cmd/ld-runtime/main.go:875`) → `record.KindInfraRerun = "infra.rerun"` (`record/record.go:103`) | | the path calls it for every step | `run/run.go:582` `state, reason, err := p.execWithInfraRerun(ctx, t, step)` | | wired, not dead code | `cmd/ld-runtime/main.go:433` `Infra: kube` | | build step only | `run/kube_classify.go:204` `ClassifyInfra` returns `false` for any `step != StepBuild` | | only `oom_kill` / `eviction` | `run/kube_classify.go:218` `InfraCauseOOMKill, InfraEvidenceKernelRecord`; `:224` and `:227` `InfraCauseEviction, InfraEvidenceEvictionEvent` — nothing else is returned | | the other four are constants with no producer | `run/admission.go:83-94` declares all six causes and five evidence kinds; the only `InfraCause*` reads outside tests are the three lines above | | the `eviction` row's two evidence sources | `k.evicted(ctx)` (evictor event) and `k.poolPodReplaced(ctx, t)` (pod replaced/gone) at `kube_classify.go:222-228` | | absent `evidence_kind` is an unevidenced draw, not a vocabulary row | `report.sql:966-972` `infra_event`: `evidenced = COALESCE(m.evidence_kind IS NOT NULL AND lower(payload->>'evidence_kind') = m.evidence_kind, FALSE)`; `report.sql:1984-1987` the vocabulary arm skips `evidence_kind IS NULL` | | `cause` and `evidence_kind` are read from the payload | `report.sql:1976-1987`; row column `node_key` read at `report.sql:966` | Tests that pin the writer shape: `run/infra_rerun_test.go` (`TestInfraRerunRedrawsEvictedBuilder` asserts one row with `node_key=LDF-3.build`, `cause=eviction`, `evidence_kind=eviction_event`, `attempt=1`) and `cmd/ld-runtime/main_test.go:402` (`RecordInfra` writes under the ticket's named sample). ## Negative check `grep -n 'infra.rerun' design/ld5/contracts-b11-b12.md | grep 'not yet'` exits 1 after the change: no `infra.rerun` line carries `not yet` any more. `git diff` is 3 lines (`-3 +3`), all in §4/§6; `grep -c 'Edited\|Revised'` unchanged (no header row added). ## Merge order vs HOMELAB-2339 (PR #24) The two changes do **not** depend on each other, but they touch adjacent lines of the §4 table, so the second merge of the pair needs one line-level resolution: keep this PR's two `infra.rerun` rows **and** 2339's `gate.decision.decision` row. Verified mechanically: `git merge-tree --write-tree` reports one content conflict, both sides being exactly those three table rows; resolving it to both sides' lines leaves the document coherent (probe performed and discarded, nothing pushed from it). Preferred order: **2339 first, then 2342** (2339 carries the `Revised` header row that states the code of record; this PR deliberately adds none, to keep the delta at 3 lines). Reverse order needs the same one-line resolution. No file of 2339's is edited here. ## CI reality This repository has **no workflows**: no `.forgejo/`, no `.github/`, no workflow file anywhere in the tree at this head (the only `.yaml` present is the test golden `internal/harness/claudecode/testdata/golden/slice.yaml`). There will be no CI status on this PR, and none is claimed. ## Deliberately out of scope (for the reviewer/orchestrator, not fixed here) 1. §5 still reads "`infra.rerun.node_key` must name the killed run's node key … — HOMELAB-2252's writer, not W2's path." The rule is satisfied by the live writer, but the possessive tag now reads as if W2's path does not write the row. It is a naming rule, not a `not yet` status, so it was left alone. Needs a separate word-level ticket if the orchestrator wants it changed. 2. `report.sql`'s `infra_event` also reads `payload->>'accept_test_sha'` (`report.sql:969`); no writer sets it (the writer's fields are `step`, `attempt`, `cause`, `evidence_kind`, `node_key`, `head_sha` — `run/run.go:626-631`). No contract text claims otherwise, so nothing here is stale; it is an unrecorded writer/reader gap worth its own ticket. ## Gates Docs-only, reversible, no operator-gate item, no live work, no merge from this session.
§4's "Written by (today)" cell still said **not yet** for both
`infra.rerun.cause` and `infra.rerun.evidence_kind`. The row is written on the
reachable path: `run.Path.execWithInfraRerun` (`run/run.go`) appends it through
`recordJournal.RecordInfra` (`cmd/ld-runtime/main.go`), and the classifier wired
as `Options.Infra` (`main.go`) is `run.Kube.ClassifyInfra`
(`run/kube_classify.go`), which classifies the build step only and can prove
only `oom_kill`/`kernel_record` and `eviction`/`eviction_event`.

So the cell now says exactly that: two of the six cause spellings and their two
mandated evidence kinds are written, the other four are constants in
`run/admission.go` with no producer. §6's "Missing" row is corrected the same
way so the document does not contradict itself: what is still missing is the
four causes the classifier cannot prove, not the row itself.

No frozen value, no M0/report.sql semantics and no gate text changes. Historical
rows are untouched (nothing in this diff writes data).
aaron changed title from HOMELAB-2342: docs(contracts): infra.rerun is written today — two caused kinds, not "not yet" to WIP: HOMELAB-2342: docs(contracts): infra.rerun is written today — two caused kinds 2026-10-28 04:46:45 +00:00
ld-reviewer left a comment
Collaborator

Review — ld-reviewer — aaron/lab-director#25 @ 5f47419 (HOMELAB-2342) — APPROVE

Three lines, one file (design/ld5/contracts-b11-b12.md): the two infra.rerun.* rows in §4 and the
infra.rerun "missing" row in §6. Verified against the writer and the reader, not the PR's own table.

Exactly two evidenced causes are emittable — confirmed. run/kube_classify.go:201-231
ClassifyInfra returns ("", "", false) for any step != StepBuild, and its only three producing
returns are :218 oom_kill/kernel_record, :224 and :227 eviction/eviction_event. Grep for
InfraCause* outside tests and outside the admission.go declarations: those three lines and nothing
else. The other four causes (gateway_5xx_429, lost_exec_stream, job_scheduling_failure, other)
are constants in run/admission.go:83-94 with no producer — the §4 wording says exactly that.

Absent evidence_kind is handled the way the text now claims. report.sql:963-975 infra_event:
evidenced = COALESCE(m.evidence_kind IS NOT NULL AND lower(payload->>'evidence_kind') = m.evidence_kind, FALSE)
— absent evidence is an unevidenced draw, split out at :1000 as unevidenced, not a vocabulary
offence. And report.sql:1983-1989 (the infra.rerun.evidence_kind arm) filters
payload->>'evidence_kind' IS NOT NULL, so an absent value never becomes a NOT_CERTIFIABLE row.
The two readings agree with the sentence added.

§4/§6 consistency, no M0/report/gate change. The diff touches three table rows and nothing else: no
Revised header (that is #24's), no frozen value, no gate text, no report.sql/M0 claim. After the
change grep 'infra.rerun' … | grep 'not yet' returns nothing (verified, exit 1), and §6 now names the
four causes that are missing rather than implying the whole row is, which is the correct statement —
§4 and §6 no longer contradict each other.

No CI, recorded: aaron/lab-director has no .forgejo or .github path anywhere in the tree I
enumerated (637 paths, both pages), and the combined status on this head is total_count: 0. Nothing
is green because nothing ran; my verdict rests on the diff and on reading the code.

Merge order, as briefed: #24 then #25. I composed the keep-both resolution myself rather than
trusting the PR's claim that it is one conflict: taking #24's head and substituting #25's three changed
rows yields a coherent 195-line document with both the new gate.decision.decision row and both
new infra.rerun rows present, and no infra.rerun "not yet" left. The rows are distinct table rows,
so the resolution is genuinely line-level; nothing of #24's is overwritten.

Non-blocking, already declared by the author and worth keeping visible: §5 still tags the
infra.rerun.node_key rule as HOMELAB-2252's writer, which now reads as if this path does not write the
row; and report.sql reads a accept_test_sha payload field that no writer sets. Both are outside a
3-line delta and neither is contradicted by the text here — they want their own tickets rather than a
widened diff.

APPROVE at 5f474196ee21f2e29f907957779da3ad046ff80d, no blocker. Approval may land pending rather
than official (this identity has produced both in this lane); this comment is the verdict of record.
Docs only, not dangerous-class, no second reviewer needed. Nothing edited, nothing merged.

## Review — `ld-reviewer` — aaron/lab-director#25 @ `5f47419` (HOMELAB-2342) — APPROVE Three lines, one file (`design/ld5/contracts-b11-b12.md`): the two `infra.rerun.*` rows in §4 and the `infra.rerun` "missing" row in §6. Verified against the writer and the reader, not the PR's own table. **Exactly two evidenced causes are emittable — confirmed.** `run/kube_classify.go:201-231` `ClassifyInfra` returns `("", "", false)` for any `step != StepBuild`, and its only three producing returns are `:218` `oom_kill`/`kernel_record`, `:224` and `:227` `eviction`/`eviction_event`. Grep for `InfraCause*` outside tests and outside the `admission.go` declarations: those three lines and nothing else. The other four causes (`gateway_5xx_429`, `lost_exec_stream`, `job_scheduling_failure`, `other`) are constants in `run/admission.go:83-94` with no producer — the §4 wording says exactly that. **Absent `evidence_kind` is handled the way the text now claims.** `report.sql:963-975` `infra_event`: `evidenced = COALESCE(m.evidence_kind IS NOT NULL AND lower(payload->>'evidence_kind') = m.evidence_kind, FALSE)` — absent evidence is an *unevidenced draw*, split out at `:1000` as `unevidenced`, not a vocabulary offence. And `report.sql:1983-1989` (the `infra.rerun.evidence_kind` arm) filters `payload->>'evidence_kind' IS NOT NULL`, so an absent value never becomes a `NOT_CERTIFIABLE` row. The two readings agree with the sentence added. **§4/§6 consistency, no M0/report/gate change.** The diff touches three table rows and nothing else: no `Revised` header (that is #24's), no frozen value, no gate text, no `report.sql`/M0 claim. After the change `grep 'infra.rerun' … | grep 'not yet'` returns nothing (verified, exit 1), and §6 now names the four causes that *are* missing rather than implying the whole row is, which is the correct statement — §4 and §6 no longer contradict each other. **No CI, recorded:** `aaron/lab-director` has no `.forgejo` or `.github` path anywhere in the tree I enumerated (637 paths, both pages), and the combined status on this head is `total_count: 0`. Nothing is green because nothing ran; my verdict rests on the diff and on reading the code. **Merge order, as briefed:** #24 then #25. I composed the keep-both resolution myself rather than trusting the PR's claim that it is one conflict: taking #24's head and substituting #25's three changed rows yields a coherent 195-line document with **both** the new `gate.decision.decision` row and both new `infra.rerun` rows present, and no `infra.rerun` "not yet" left. The rows are distinct table rows, so the resolution is genuinely line-level; nothing of #24's is overwritten. **Non-blocking, already declared by the author and worth keeping visible:** §5 still tags the `infra.rerun.node_key` rule as HOMELAB-2252's writer, which now reads as if this path does not write the row; and `report.sql` reads a `accept_test_sha` payload field that no writer sets. Both are outside a 3-line delta and neither is contradicted by the text here — they want their own tickets rather than a widened diff. **APPROVE at `5f474196ee21f2e29f907957779da3ad046ff80d`**, no blocker. Approval may land pending rather than official (this identity has produced both in this lane); this comment is the verdict of record. Docs only, not dangerous-class, no second reviewer needed. Nothing edited, nothing merged.
Collaborator

Verdict of record — ld-reviewer — head 5f474196ee21f2e29f907957779da3ad046ff80d: APPROVED (HOMELAB-2342, 3 lines, one file).

Exactly two evidenced causes emittable, confirmed at source: run/kube_classify.go:201-231 — ClassifyInfra returns not-ok for any step != StepBuild, and the only producing returns are :218 oom_kill/kernel_record and :224/:227 eviction/eviction_event. Grep for InfraCause* outside tests and outside the admission.go declarations: those three lines, nothing else. The other four causes are constants with no producer — which is what the §4 cell now says.

Absent evidence_kind: report.sql:963-975 computes evidenced as evidence_kind IS NOT NULL AND = m.evidence_kind, and the vocabulary arm at :1983-1989 skips evidence_kind IS NULL, so absent evidence reads as an unevidenced draw and never as a vocabulary row — as the text now claims.

§4/§6 consistency: after the change no infra.rerun line says "not yet" (grep exit 1), and §6 names the four unprovable causes instead of the whole row. Diff is three table rows: no frozen value, no report.sql/M0/gate text, no Revised header (that is #24's).

No CI, recorded: no .forgejo/.github path in the enumerated tree; combined status on this head total_count: 0.

Merge order checked, not taken on trust: I composed the keep-both myself — #24's head with this PR's three rows substituted is a coherent 195-line document with both new gate.decision.decision and both new infra.rerun rows, no infra.rerun "not yet" remaining. Distinct table rows, so line-level resolution is correct and nothing of #24's is lost.

Recorded, out of scope and not widened here: §5's HOMELAB-2252 possessive tag now reads as though this path does not write the row, and report.sql reads accept_test_sha which no writer sets. Both want their own tickets.

Approval may land pending rather than official (this identity has produced both); this comment is the verdict of record. Nothing edited, nothing merged.

**Verdict of record — `ld-reviewer` — head `5f474196ee21f2e29f907957779da3ad046ff80d`: APPROVED** (HOMELAB-2342, 3 lines, one file). **Exactly two evidenced causes emittable, confirmed at source:** `run/kube_classify.go:201-231` — `ClassifyInfra` returns not-ok for any `step != StepBuild`, and the only producing returns are `:218` `oom_kill`/`kernel_record` and `:224`/`:227` `eviction`/`eviction_event`. Grep for `InfraCause*` outside tests and outside the `admission.go` declarations: those three lines, nothing else. The other four causes are constants with no producer — which is what the §4 cell now says. **Absent `evidence_kind`:** `report.sql:963-975` computes `evidenced` as `evidence_kind IS NOT NULL AND = m.evidence_kind`, and the vocabulary arm at `:1983-1989` skips `evidence_kind IS NULL`, so absent evidence reads as an unevidenced draw and never as a vocabulary row — as the text now claims. **§4/§6 consistency:** after the change no `infra.rerun` line says "not yet" (grep exit 1), and §6 names the four unprovable causes instead of the whole row. Diff is three table rows: no frozen value, no `report.sql`/M0/gate text, no `Revised` header (that is #24's). **No CI, recorded:** no `.forgejo`/`.github` path in the enumerated tree; combined status on this head `total_count: 0`. **Merge order checked, not taken on trust:** I composed the keep-both myself — #24's head with this PR's three rows substituted is a coherent 195-line document with both new `gate.decision.decision` and both new `infra.rerun` rows, no `infra.rerun` "not yet" remaining. Distinct table rows, so line-level resolution is correct and nothing of #24's is lost. Recorded, out of scope and not widened here: §5's HOMELAB-2252 possessive tag now reads as though this path does not write the row, and `report.sql` reads `accept_test_sha` which no writer sets. Both want their own tickets. Approval may land pending rather than official (this identity has produced both); this comment is the verdict of record. Nothing edited, nothing merged.
This pull request has changes conflicting with the target branch.
  • design/ld5/contracts-b11-b12.md
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin plane/HOMELAB-2342-infrarerun-contract:plane/HOMELAB-2342-infrarerun-contract
git switch plane/HOMELAB-2342-infrarerun-contract

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff plane/HOMELAB-2342-infrarerun-contract
git switch plane/HOMELAB-2342-infrarerun-contract
git rebase main
git switch main
git merge --ff-only plane/HOMELAB-2342-infrarerun-contract
git switch plane/HOMELAB-2342-infrarerun-contract
git rebase main
git switch main
git merge --no-ff plane/HOMELAB-2342-infrarerun-contract
git switch main
git merge --squash plane/HOMELAB-2342-infrarerun-contract
git switch main
git merge --ff-only plane/HOMELAB-2342-infrarerun-contract
git switch main
git merge plane/HOMELAB-2342-infrarerun-contract
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

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