verdict integrity: rejected outputs fail the instance, no hollow cache rows #9

Manually merged
krisbuild merged 2 commits from lu/verdict-integrity into main 2026-09-27 18:38:36 +02:00
Owner

What

Items 1, 2, 3 and 5 of docs/live-upgrades.md §7 ("Bugs found"), without protocol changes.

  • Rejected outputs fail the instance, durably (kb-control-plane/src/ws.rs). An Outputs event that cannot be applied — an expansion that does not parse/validate/materialize, a declared output that was not reported, or any storage error — marks the instance failed with exit class expansion-invalid / outputs-invalid and writes the reason to the instance log, instead of rolling back and only logging. The State{succeeded} that follows lands on a terminal instance and is ignored. Before this, an Expand in that position "succeeded" with no children: a false green the merge queue would land.
  • No hollow cache rows. record_result_if_done writes a results row only when the recorded outputs cover the def (every declared output; for an Expand, its expansion). A def that promises nothing is still cached with [].
  • The agent's outbox never drops a state, usage or outputs event (kb-agent/src/outbound.rs); log chunks remain the only droppable kind.
  • Docs: SPEC §5/§7.1/§9, ExitInfo class list (comment only, no wire or def_hash change), TODO (lease self-abort shipped in 0dd2e89; remaining lost-Outputs gap recorded — closed later by the atomic Finished event).

Tests

  • tests/outputs_integrity.rs (new, end to end with the fake agent): unparseable expansion → expander failed/expansion-invalid, graph failed, nothing materialized or cached; missing declared output → failed/outputs-invalid; success without promised outputs not cached, while a def promising nothing is still substituted.
  • ws.rs unit test for the coverage rule; outbound.rs overflow tests.
  • tests/artifacts.rs now declares the report output it reports; tests/replay.rs seeds its legacy [] row by hand.

🤖 Generated with Claude Code

## What Items 1, 2, 3 and 5 of docs/live-upgrades.md §7 ("Bugs found"), without protocol changes. - **Rejected outputs fail the instance, durably** (`kb-control-plane/src/ws.rs`). An `Outputs` event that cannot be applied — an expansion that does not parse/validate/materialize, a declared output that was not reported, or any storage error — marks the instance `failed` with exit class `expansion-invalid` / `outputs-invalid` and writes the reason to the instance log, instead of rolling back and only logging. The `State{succeeded}` that follows lands on a terminal instance and is ignored. Before this, an Expand in that position "succeeded" with no children: a false green the merge queue would land. - **No hollow cache rows.** `record_result_if_done` writes a `results` row only when the recorded outputs cover the def (every declared output; for an `Expand`, its expansion). A def that promises nothing is still cached with `[]`. - **The agent's outbox never drops a state, usage or outputs event** (`kb-agent/src/outbound.rs`); log chunks remain the only droppable kind. - Docs: SPEC §5/§7.1/§9, `ExitInfo` class list (comment only, no wire or `def_hash` change), TODO (lease self-abort shipped in 0dd2e89; remaining lost-`Outputs` gap recorded — closed later by the atomic `Finished` event). ## Tests - `tests/outputs_integrity.rs` (new, end to end with the fake agent): unparseable expansion → expander `failed/expansion-invalid`, graph `failed`, nothing materialized or cached; missing declared output → `failed/outputs-invalid`; success without promised outputs not cached, while a def promising nothing is still substituted. - `ws.rs` unit test for the coverage rule; `outbound.rs` overflow tests. - `tests/artifacts.rs` now declares the `report` output it reports; `tests/replay.rs` seeds its legacy `[]` row by hand. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
verdict integrity: rejected outputs fail the instance, no hollow cache rows
Some checks reported errors
krisbuild/kris/krisbuild/nix/hello cached
krisbuild/kris/krisbuild/nix/workspace-deps cached
krisbuild/kris/krisbuild/nix/world cached
krisbuild/kris/krisbuild/nix/clippy succeeded
krisbuild/kris/krisbuild/nix/test superseded
krisbuild/kris/krisbuild/nix/kb-check superseded
krisbuild/kris/krisbuild/nix/build superseded
krisbuild/kris/krisbuild krisbuild kris/krisbuild: superseded by 1aaa4d03053e296f9c8f11f710c4e9332541473e
2d874d8217
An agent's report can no longer turn into a green verdict or a cache
entry it does not back:

- kb-control-plane: an `Outputs` event the control plane cannot apply
  (expansion that does not parse/validate/materialize, a declared output
  missing, any error storing it) fails the instance durably with class
  `expansion-invalid` / `outputs-invalid` and writes the reason to the
  instance log, so the `State{succeeded}` behind it is ignored as
  already terminal. Previously the transaction rolled back, an error was
  logged, and an Expand then "succeeded" with no children.
- `record_result_if_done` writes a `results` row only when the recorded
  outputs cover the def: every declared output, or for an `Expand` its
  expansion. `[]` stays for a def that promises nothing.
- kb-agent: the outbound queue never drops a non-log message when full
  with no log left to evict; it grows past the bound instead, and an
  incoming log is the one refused.
- SPEC §5/§7.1/§9 state the new behaviour; TODO drops the shipped lease
  self-abort item and records the remaining lost-`Outputs` gap.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
verdict integrity: heal hollow cache rows, blame only the report
All checks were successful
krisbuild/kris/krisbuild/nix/workspace-deps cached
krisbuild/kris/krisbuild/nix/hello cached
krisbuild/kris/krisbuild/nix/world cached
krisbuild/kris/krisbuild/nix/clippy succeeded
krisbuild/kris/krisbuild/nix/build succeeded
krisbuild/kris/krisbuild/nix/kb-check succeeded
krisbuild/kris/krisbuild/nix/test succeeded
krisbuild/kris/krisbuild krisbuild kris/krisbuild: all tasks succeeded
krisbuild/queue merged
1aaa4d0305
Review follow-ups:

- Substitution treats a `results` row short of its def's declared
  outputs as a miss (logged), so hollow rows written before coverage was
  required stop being served; the run replaces them.
- `store_outputs` rejects only genuine validation failures (a declared
  output missing; `DbError::Invalid` from parsing/validating/materializing
  the expansion). SQLite errors propagate as before, and an unknown
  instance is ignored rather than rejected.
- An `Expand` owes its declared outputs too (none/container isolation
  collect them like an Exec), plus a non-blank expansion, agreeing with
  replay's `cached_expansion`.
- Drop the no-op `record_result_if_done` in `store_outputs`; rewrap the
  SPEC §5 paragraph.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Author
Owner
@krisbuild r+
krisbuild manually merged commit cca3f8755c into main 2026-09-27 18:38:36 +02:00
Collaborator

Merged as cca3f8755c.

Merged as cca3f8755c0b.
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
kris/krisbuild!9
No description provided.