Skip to content

Record every UK build attempt in Logbook (#666) - #670

Open
juaristi22 wants to merge 2 commits into
mainfrom
uk-logbook-adoption-666
Open

Record every UK build attempt in Logbook (#666)#670
juaristi22 wants to merge 2 commits into
mainfrom
uk-logbook-adoption-666

Conversation

@juaristi22

@juaristi22 juaristi22 commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Closes #666. Workstream A of the UK-migration master epic #665 — this lands before P1 calibration starts generating intermediate builds, since Logbook is the migration's build-attempt memory.

What changed

#628 delivered the append-only Logbook, but adoption is per-tool and only tools/build_us_multispine_pool.py called the record_build_attempt seam. The three UK drivers now append exactly one validated, hash-chained row per non-dry invocation, so discarded intermediate builds stay visible. Rows spool beside the build outputs; receipts live under build-scoped logbook-receipts/<build_id>/.

Dispositions

Exit path Disposition
Success (all three drivers) iterating
Gate-battery block; calibrated-candidate refusal; unexpected exception failed
Named dev-rung SPI singleton abort discarded
Dry runs (both routes, success and failure) no row
Argparse / config refusals, including a malformed or conflicting Logbook chain head no row — recording starts post-parse and after chain-config validation, matching the US driver

The rung abort keeps its existing uk_rung_abort_receipt payload, including its own disposition: "aborted_with_receipt" field; only the Logbook row says discarded. Exit codes, exception types, and stdout payloads are unchanged — the spool notice goes to stderr.

Verdicts and pins. gate_verdicts anchor into durable local receipts, never host-absolute paths (rows are exported publicly). The national rows carry the schema-4 battery statuses uncollapsed (all five of passed/failed/not_applicable/evidence_absent/unreached), read from error.report_path on a block; rung aborts point at the named-edge receipt; rowwise and candidate rows point into their manifests. The national pin surface hoists the source digests out of the checkpoint-only path so failure rows carry real input pins, and names the future ledger_facts slot for #622/#623 without inventing a placeholder.

New module. microcosm.build.logbook_adoption is a driver-agnostic extraction of the generic US helpers: attempt state, predecessor resolution, git code pins, exportable artifact references, normalized role-pin digests, atomic receipts, and a record-once terminal write. The US driver deliberately keeps its local copy in this PR — its contract suite pins those seams by monkeypatch — so migrating it is a named follow-up. Also repoints the Logbook docstring from the merged #616 at #665/#666.

Design notes for review

  • Record-once is structural. A second record_build_attempt call in one run produces a different row (wall_seconds differs), and two rows chaining the same predecessor make a fork the archive refuses. The national driver's handlers are therefore sibling except arms — each records once and then returns or re-raises. GateBatteryBlockedError subclasses RuntimeError, so the except ValueError arm cannot shadow it; the unrecognized-ValueError case records inline before re-raising, since a re-raise never falls through to a later arm on the same try. Every driver test asserts len(rows) == 1.
  • Non-nullable digests. input_pins_digest and identity_digest are required 64-hex, so a placeholder preflight digest is seeded before anything can fail; code_pin keeps the unresolved-local-git-code-pin sentinel until the real pin resolves. That is what lets a preflight failure still record a valid row.
  • Two calls were judgment, not derivation, and are easy to flip: dry-runs recording nothing, and the rung abort as discarded rather than failed.

Verification

  • test_logbook_adoption.py + the four UK driver suites: 169 passed
  • Logbook contract, backfill, archive, CLI, and the untouched US driver suite: 204 passed
  • ruff check . clean; all 10 changed files ruff format clean. Full-repo ruff format --check shows 29 pre-existing unrelated diffs, confirmed identical at the base commit and left alone.
  • Acceptance: a UK row chained onto the committed archive's tail (in a temp copy) exports, validates as one 29-row chain, and renders in the same table beside the US rows — uk-national-staging | f001 | iterating.

🤖 Generated with Claude Code

juaristi22 and others added 2 commits August 13, 2026 17:00
The three UK drivers now append exactly one validated, hash-chained Logbook
row per non-dry invocation, so discarded intermediate builds stay visible
before the P1 calibration work starts generating them. Rows spool beside the
build outputs; receipts live under build-scoped logbook-receipts/<build_id>/.

Successes record iterating, gate-battery blocks and calibrated-candidate
refusals record failed, unexpected exceptions record failed after a
pipeline-error receipt, and the named dev-rung SPI singleton abort records
discarded while keeping its existing uk_rung_abort_receipt payload. Dry runs
are excluded on both success and failure paths. Exit codes, exception types,
and stdout payloads are unchanged — the spool notice goes to stderr.

Terminal verdicts anchor into durable local receipts rather than
host-absolute paths: the national rows carry the schema-4 battery statuses
uncollapsed, rung aborts point at the named-edge receipt, and the rowwise and
candidate rows point into their manifests. The national pin surface hoists the
source digests out of the checkpoint-only path so failure rows carry real
input pins, and names the future ledger_facts slot for #622/#623 without
inventing a placeholder.

Adds microcosm.build.logbook_adoption, a driver-agnostic extraction of the
generic US stacked-driver helpers (attempt state, predecessor resolution, git
code pins, exportable artifact references, normalized role-pin digests, atomic
receipts, record-once terminal write). The US driver keeps its local copy here
because its contract suite pins those seams; migrating it is the follow-up.
Repoints the Logbook docstring from the merged #616 at #665/#666.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…te setup failures

Two findings from the #666 adversarial review, both about failure-path
ordering and observability:

Chain configuration now resolves before any side effect in all three UK
drivers. The national driver used to resolve --logbook-prev-row-digest /
POPULACE_LOGBOOK_PREV_ROW_DIGEST after unlinking the prior attempt's
evidence, replay, build-record, and rung-abort sidecars, so a malformed or
conflicting head destroyed local evidence and then crashed; the rowwise
dataset driver had the same ordering against its stale coverage/crosswalk
unlinks. A config refusal now aborts with no row, no receipt, and untouched
sidecars — the same posture as an argparse refusal — and every recorded row
carries the resolved head rather than the raw CLI value, so a failure row
can no longer chain to the wrong predecessor.

The candidate driver's recording envelope now opens before input
verification. Input hashing, frame and ladder loading, cloning, and target
binding used to run before the try that records failed attempts, so their
failures escaped with no Logbook row despite being real terminal outcomes.
main() now seeds the attempt state right after CLI validation and routes
dry-runs past recording explicitly; pins promote as soon as both inputs are
hashed, and the identity digest waits only on the frame-derived source year.

Both regression tests were verified to fail against the unfixed drivers:
a malformed env head must leave pre-existing national sidecars intact and
spool nothing, and a ladder-load failure must spool exactly one failed
candidate row with real input pins.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juaristi22

Copy link
Copy Markdown
Collaborator Author

An adversarial review (Codex, run locally) surfaced two failure-path findings; both are fixed in 206ff6e.

[high] Invalid Logbook predecessor could erase national sidecars before aborting. The national driver resolved --logbook-prev-row-digest / POPULACE_LOGBOOK_PREV_ROW_DIGEST after unlinking the prior attempt's evidence, replay, build-record, and rung-abort sidecars — so an operator typo in an optional logging feature destroyed local evidence and then crashed. Chain config now resolves before any side effect in all three drivers (the rowwise dataset driver had the same ordering against its stale coverage/crosswalk unlinks). A malformed or conflicting head refuses the run with no row and untouched sidecars, matching the argparse-refusal posture, and every recorded row now carries the resolved head rather than the raw CLI value.

[medium] Rowwise candidate setup failures escaped unrecorded. Input hashing, frame/ladder loading, cloning, and target binding ran before the candidate's recording envelope opened, so those failures spooled no row — contradicting the one-row-per-non-dry-invocation contract. The envelope now opens right after CLI validation; pins promote as soon as both inputs hash, the identity digest waits only on the frame-derived source year, and dry-runs are routed past recording explicitly.

Both regression tests were verified to fail against the unfixed drivers before the fix landed:

  • test_invalid_logbook_predecessor_refuses_before_sidecar_cleanup (malformed-env + cli-env-conflict): pre-existing sidecars keep their bytes, no spool row.
  • test_candidate_setup_failure_records_failed_row: a ladder-load failure spools exactly one failed row with real input pins and no cloned phase.

Full driver + adoption suites (172), logbook contract/backfill/archive/CLI + untouched US driver suites (204), and ruff all pass locally after the fix.

🤖 Generated with Claude Code

@juaristi22
juaristi22 marked this pull request as ready for review August 13, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Record every UK build attempt in Logbook

1 participant