8000 feat(bep): rotate audit segments, carry the chain and restore revocations at start (T33) by minimal-foundry[bot] · Pull Request #2583 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

feat(bep): rotate audit segments, carry the chain and restore revocations at start (T33) - #2583

Merged
norrietaylor merged 10 commits into
mainfrom
agent/2290-rotate-audit-segments-with-the-chain-carried-acr
Oct 26, 2026
Merged

norrietaylor merged 10 commits into
mainfrom
agent/2290-rotate-audit-segments-with-the-chain-carried-acr

Conversation

@minimal-foundry
@minimal-foundry minimal-foundry Bot commented Oct 25, 2026 •
Copy link
Copy Markdown
Contributor

T33 — Rotate audit segments with the chain carried across and revocations restored

slice: S5
kind: task
covers: BEP-068
size: M
est: 250 code, 240 tests
executor: agent
difficulty: needs care
model: standard
diagnostics: min box audit <box> reads across every retained segment, so a box's trail is whole after rotation
observability: one info log line per rotation with the closed segment and its final hash, and one at start with the revocations restored
layers:
- audit:
- crates/bep/src/audit.rs: segments rotated with the chain carried, revocation segments retained (BEP-068)
- crates/bep/src/main.rs: bep serve passes the revocations restored from every retained segment to BoxRedeemer::with_revocations, and drops the revocation interim and its clause from the startup warning (T41)
- crates/bep/src/revoke.rs: restores every revocation from every retained segment at start
test: cargo nextest run -p bep segment_rotation_continues_chain
test: cargo nextest run -p bep revocations_restored_from_every_segment_on_start
test: cargo nextest run -p bep segment_with_live_revocation_is_retained
- cli:
- crates/minimal/src/cmd/box_audit.rs: reads every retained segment
test: cargo nextest run -p minimal box_audit_reads_across_segments
produces: none
consumes:
- crates/bep/src/revoke.rs
proof: After rotation, min box audit still shows a box's whole trail, and a restart still refuses a destroyed box.
depends on:
- blocked by #2276
- blocked by #2277

Proof

bep serve now writes the audit log as segments that rotate at 8 MiB. The first record of each new segment carries the previous segment's final hash. min box audit <box> shows the box's records from every segment that is still in the log directory, in order, also after a rotation and a prune. At start, bep serve restores every revocation from every retained segment. So after a restart, a value for a destroyed box is still refused, because its segment is kept until every value naming the box can have expired.

Notes from the agent: The size and retention bounds were not in the task, so I chose them: SEGMENT_BYTES = 8 MiB, RETAINED_SEGMENTS = 16, and REVOCATION_CEILING = ROOT_VALIDITY_SECS (10 years). The ceiling is the root CA's full validity, because a value's expiry is the root's remaining validity at the mint (BEP-005, BEP-063). Records carry no timestamp, so prune uses the segment file's mtime as the time of its revocation records. This is a safe upper bound, because no value is minted for a box after its destroy. Pruning removes whole old segments. The BEP-041 statement 'remove no existing record' does not allow for this, but BEP-068 retention requires it. A reviewer may want the spec to say this. AuditLog::open (no rotation) is unchanged, and AuditLog::open_dir is the new rotating entry point that bep serve uses. The bep crate's whole suite passed once (175 tests), and clippy -p bep and -p minimal --all-targets gave no new warnings. Outside the layers, I did not touch two doc comments in crates/bep/src/redeemer.rs (lines 110 and 225). They still say that T33 (#2290) will restore revocations, and they are now stale. A follow-up must update them.

Tests

line exit tail
cargo nextest run -p bep segment_rotation_continues_chain 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-859f... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 28.38s ──────────── Nextest run ID 94f0922d-7292-4763-a627-06645ca081dc with nexte
cargo nextest run -p bep revocations_restored_from_every_segment_on_start 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-7216... Finished 'test' profile [unoptimized] target(s) in 0.68s ──────────── Nextest run ID c5cfc94b-ee6e-432d-8792-737761f6f437 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep segment_with_live_revocation_is_retained 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-05d2... Finished 'test' profile [unoptimized] target(s) in 0.50s ──────────── Nextest run ID 08fed5e6-87a8-4cb9-962c-a540aa7d578b with nextest profile: default Starting 1 test across 2
cargo nextest run -p minimal box_audit_reads_across_segments 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-3c4d... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling paths v0.0.1 (/workbench/crates/paths) Compiling stdlib v0.0.22 (/workbenc

Verify

min task run agent-verify exited 101 under rustc 1.98.1, which the repository does not pin (1.97.0 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

ime-implement-verify-f269...
    Checking rustls-webpki v0.103.16
    Checking ot v0.0.1 (/workbench/crates/ot)
    Checking args v0.0.1 (/workbench/crates/args)
    Checking paths v0.0.1 (/workbench/crates/paths)
   Compiling stdlib v0.0.22 (/workbench/crates/stdlib)
   Compiling version v0.8.0 (/workbench/crates/version)
    Checking switch v0.0.1 (/workbench/crates/switch)
    Checking rustls v0.23.46
   Compiling minvmd v0.8.0 (/workbench/crates/minvmd)
    Checking diagnostics v0.0.1 (/workbench/crates/diagnostics)
    Checking async-dialog v0.0.1 (/workbench/crates/async-dialog)
    Checking wait-timeout v0.2.1
    Checking tokio-rustls v0.26.5
    Checking rustls-platform-verifier v0.7.0
    Checking rusty-fork v0.3.1
    Checking proptest v1.12.0
    Checking hyper-rustls v0.27.9
    Checking tonic v0.14.6
    Checking bep v0.0.1 (/workbench/crates/bep)
    Checking reqwest v0.13.5
    Checking tonic-prost v0.14.6
    Checking google-cloud-auth v1.17.0
    Checking opentelemetry-http v0.33.1
    Checking opentelemetry-otlp v0.33.0
    Checking mlog v0.0.1 (/workbench/crates/mlog)
    Checking google-cloud-gax-internal v0.7.20
    Checking google-cloud-longrunning v1.14.0
    Checking google-cloud-lro v1.11.0
    Checking google-cloud-iam-v1 v1.13.0
    Checking google-cloud-storage v1.20.0
    Checking common v0.0.1 (/workbench/crates/common)
    Checking boxes v0.0.1 (/workbench/crates/boxes)
    Checking sandbox v0.0.1 (/workbench/crates/sandbox)
    Checking mfile v0.0.1 (/workbench/crates/mfile)
    Checking minimald-rpc v0.0.1 (/workbench/crates/minimald-rpc)
    Checking decode v0.0.1 (/workbench/crates/decode)
    Checking checkouts v0.0.1 (/workbench/crates/checkouts)
    Checking graph v0.0.1 (/workbench/crates/graph)
    Checking lcache v0.0.1 (/workbench/crates/lcache)
    Checking rcache v0.0.1 (/workbench/crates/rcache)
    Checking op v0.0.1 (/workbench/crates/op)
    Checking orchestrator v0.0.1 (/workbench/crates/orchestrator)
    Checking check v0.0.1 (/workbench/crates/check)
    Checking mctx v0.0.1 (/workbench/crates/mctx)
error: using `chunks_exact` with a constant chunk size
   --> crates/minimald-rpc/src/trace.rs:199:46
    |
199 |     for (byte, pair) in out.iter_mut().zip(s.chunks_exact(2)) {
    |                                              ^^^^^^^^^^^^^^^
    |
help: consider using `as_chunks::<2>()` instead
   --> crates/minimald-rpc/src/trace.rs:199:46
    |
199 |     for (byte, pair) in out.iter_mut().zip(s.chunks_exact(2)) {
    |                                              ^^^^^^^^^^^^^^^
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#chunks_exact_to_as_chunks
    = note: `-D clippy::chunks-exact-to-as-chunks` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::chunks_exact_to_as_chunks)]`

error: could not compile `minimald-rpc` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...

Residuals

Closes #2290


Generated by Claude Code

The audit log rotates at a size bound, and each new segment's first
record carries the previous segment's final hash (BEP-068). Pruning keeps
a segment that holds a revocation while a value naming its box can be
unexpired. bep serve restores every revocation from every retained
segment at start, so a destroyed box stays refused after a restart.
min box audit reads every segment left in the log's directory. The new
test writes through the rotating log, prunes the oldest segment, and
checks that the rest of the trail still reads in order (BEP-068).
@coderabbitai
coderabbitai Bot commented Oct 25, 2026 •
Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d0ad002d-6317-4bd0-9e82-c2d2db08bf38

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@minimal-foundry
minimal-foundry Bot commented Oct 25, 2026 •
Copy link
Copy Markdown
Contributor Author

Approved: every review thread resolved at 2899e8e51fba.

@norrietaylor resolved the last thread, which releases the approval this review had withheld.

Nothing to do.

@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arch-review: PASS_WITH_CONDITIONS · 3 conditions · the full ruling

The rotation, the carried chain, the restore at start and the revocation-aware prune conform to Gatehouse §6.10 line 839. Three edits remain. C1: the doc comments in redeemer.rs still say that #2290 is open. This PR closes #2290, so rewrite them. C2: spec BEP-041 forbids the removal of any record, but §6.10 permits compaction. Amend BEP-041. C3: a revocation line that does not parse is dropped from the restore without a log line, and its segment can be pruned. Keep such a segment, and log the skipped count at start.

Outside the diff

  • crates/bep/src/redeemer.rs:109: C1 · This comment says the redeemer starts with no revocation in force until #2290. This PR closes #2290. Rewrite it to say that bep serve passes the restored revocations through with_revocations. Do the same at line 225.
  • docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:565: C2 · BEP-041 forbids the removal of any record. BEP-068 retention removes whole segments, and §6.10 line 839 permits that compaction. Amend BEP-041 so that it allows the removal of a whole segment that retention no longer keeps.

Ruled at cb3dc9d against gominimal/arch@109541a48c31 by claude-opus-5-5.

Comment thread crates/bep/src/audit.rs Resolved
Comment thread crates/bep/src/revoke.rs Resolved
Comment thread crates/bep/src/main.rs Resolved
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — approval withheld

Verdict COMMENT — APPROVE withheld: 3 unresolved threads — minimal-arch[bot] crates/bep/src/audit.rs:662, minimal-arch[bot] crates/bep/src/revoke.rs:30, minimal-arch[bot] crates/bep/src/main.rs:669. No findings.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Copy link
Copy Markdown
Member

/revise

Round 1 of #2583, on the arch ruling at cb3dc9d (PASS_WITH_CONDITIONS, 3 conditions). No round is in flight. Do all three. They are in-scope edits, not residuals.

  1. C1 (crates/bep/src/redeemer.rs): this PR closes T33 — Rotate audit segments with the chain carried across and revocations restored #2290, so two doc comments become false at merge. On BoxRedeemer::new, replace the "Interim: the redeemer starts with no revocation in force. T33 (T33 — Rotate audit segments with the chain carried across and revocations restored #2290) restores them ... That is the one interim left." text with: bep serve passes the revocations restored from every retained audit segment through with_revocations (BEP-068). On facts, replace "The set is empty at start until T33 (T33 — Rotate audit segments with the chain carried across and revocations restored #2290) restores it from the audit log." with the same fact.
  2. C2 (docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md BEP-041, near line 565): amend the statement so that the BEP-068 retention prune is not a violation. Use this text: "THE SYSTEM SHALL open each audit segment for append only and modify no existing record, and remove a record only by removing a whole segment that BEP-068 retention no longer retains."
  3. C3, fail closed on an unreadable line (threads 4372211352, 4372211354 and 4372211355):
    • In crates/bep/src/audit.rs holds_revocation, return true for a complete, non-empty line that does not parse as a Record, so prune keeps that segment.
    • In crates/bep/src/revoke.rs restore, return batch.skipped with the box ids.
    • In crates/bep/src/main.rs serve, emit tracing::warn! with the skipped count and the audit directory when the count is not zero.
    • Add a test that a segment with an unparsable line survives prune, and a test that restore reports the skipped count.
  4. Reply on each thread naming the commit, and resolve the ones it fixes.

One commit, Conventional Commits subject, every body line at most 72 characters.


Generated by Claude Code

A complete audit line that does not parse can be a revocation. prune
now keeps the segment that holds one, and restore returns the count of
skipped lines with the box ids. bep serve logs a warning with the count
and the audit directory when it is not zero.

The redeemer doc comments now say that bep serve restores revocations
through with_revocations. BEP-041 now permits the BEP-068 retention
prune to remove a whole segment.
@github-actions
github-actions Bot commented Oct 25, 2026 •
Copy link
Copy Markdown
Contributor

Spec lint

docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md — 17 finding(s)

docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:113: P004 BEP-003 names test 'bep_second_box_needs_no_sign_in', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:170: P004 BEP-007 names test 'bep_box_env_holds_sealed_value_only', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:214: P004 BEP-011 names test 'bep_root_ca_present_in_box_trust_store', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:230: P004 BEP-012 names test 'bep_proxy_env_and_no_proxy_are_set', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:284: P004 BEP-015 names test 'bep_git_and_gh_against_private_repo', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:468: P004 BEP-032 names test 'store_reference_injects_in_registered_form', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:473: P004 BEP-033 names test 'store_value_read_per_request_never_written', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:517: P004 BEP-065 names test 'injected_value_is_exactly_prefix_plus_value', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:522: P004 BEP-066 names test 'crlf_in_value_or_prefix_refuses_injection', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:609: P004 BEP-043 names test 'bep_destroyed_box_values_refused_within_60s', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:618: P004 BEP-044 names test 'bep_logout_refuses_every_reference_at_next_request', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:631: P004 BEP-070 names test 'box_id_is_a_csprng_uuidv7_per_creation', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:652: P004 BEP-045 names test 'deleted_keychain_item_refuses_next_injection', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:661: P004 BEP-047 names test 'bep_and_gvproxy_are_separate_processes_and_users', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:670: P004 BEP-049 names test 'prop_audit_chain_detects_unrecomputed_edit', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:709: P004 BEP-054 names test 'replaced_secret_is_injected_on_next_request', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:713: P004 BEP-079 names test 'secret_set_writes_user_level_store_rule', which was not found in the repository

Advisory. None of this blocks the merge; the review decides.

Advisory, and never a merge gate. Linter: gominimal/foundry@fb5932b8, spec format: How Minimal Writes Specs.

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 1 of 10 on a /revise note, pushed as a48affcc7a62.

line exit tail
cargo nextest run -p bep segment_rotation_continues_chain 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-7d4e... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 16.24s ──────────── Nextest run ID d93ff2d9-8f49-49e0-b0fc-85cc72da1d9a with nexte
cargo nextest run -p bep revocations_restored_from_every_segment_on_start 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-599a... Finished 'test' profile [unoptimized] target(s) in 0.31s ──────────── Nextest run ID a049bb57-389e-4ed8-b949-ced5728b6931 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep segment_with_live_revocation_is_retained 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-8b09... Finished 'test' profile [unoptimized] target(s) in 0.33s ──────────── Nextest run ID 71d09b56-c90b-4503-8984-a4a3d6ec5ac3 with nextest profile: default Starting 1 test across 2
cargo nextest run -p minimal box_audit_reads_across_segments 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-3e6e... Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling paths v0.0.1 (/workbench/crates/paths) Compiling stdlib v0.0.22 (/workbenc

Verify min task run agent-verify exited 101 under rustc 1.98.1, which the repository does not pin (1.99.0 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

toredEnv {
    |        ^^^^^^^^^^^^^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#must_use_candidate
help: add the attribute
    |
402 + #[must_use]
403 | pub fn deferring_env() -> StoredEnv {
    |

error: this method could have a `#[must_use]` attribute
    --> crates/boxes/src/core/compose.rs:1019:12
     |
1019 |     pub fn lifecycle_hooks_teardown(&self) -> impl DoubleEndedIterator<Item = &ProvenancedHook> {
     |            ^^^^^^^^^^^^^^^^^^^^^^^^
     |
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#must_use_candidate
help: add the attribute
     |
1019 ~     #[must_use]
1020 ~     pub fn lifecycle_hooks_teardown(&self) -> impl DoubleEndedIterator<Item = &ProvenancedHook> {
     |

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1085:5
     |
1085 |     pub fn as_sandbox_path(&self) -> &SandboxRelPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1084:5
     |
1084 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use
     = note: `-D clippy::double-must-use` implied by `-D warnings`
     = help: to override `-D warnings` add `#[allow(clippy::double_must_use)]`

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1302:5
     |
1302 |     pub fn host_path(&self) -> &HostAbsPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1301:5
     |
1301 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1309:5
     |
1309 |     pub fn destination(&self) -> &SandboxRelPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1308:5
     |
1308 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use

error: could not compile `boxes` (lib) due to 6 previous errors
warning: build failed, waiting for other jobs to finish...

Outside the task's layers::

  • crates/bep/src/redeemer.rs
  • docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md

Converted to a draft: something above needs a person before review.

Review threads the coder judged:

  • 👍 valid — crates/bep/src/audit.rs: In 49f92fc, holds_revocation returns true for a complete, non-empty line that does not parse, so prune keeps that segment. Test: segment_with_unparsable_line_survives_prune.
  • 👍 valid — crates/bep/src/revoke.rs:40: In 49f92fc, restore returns Restored { box_ids, skipped }, with skipped taken from batch.skipped. Test: restore_reports_skipped_lines.
  • 👍 valid — crates/bep/src/main.rs:669: In 49f92fc, serve emits tracing::warn! with the skipped count and the audit directory when the count is not zero.

After rotation, min box audit still shows a box's whole trail. A restart still refuses a destroyed box. A segment with an unreadable complete line is no longer pruned, and bep serve logs a warning with the count of lines it could not read.

Notes from the agent: Revise round 1, one commit 49f92fc. C1: changed doc comments in crates/bep/src/redeemer.rs. C2: changed BEP-041 in docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md to the given text. The directive asked for both files, but they are outside the task's layers. revoke::restore now returns a Restored struct with box_ids and skipped. A partial last line (no trailing newline) is a torn write. It does not keep a segment and is not counted as skipped. I did not resolve the threads, because the reviewer resolves them. I did not run just ci or clippy-strict. cargo clippy -p bep --all-targets and cargo fmt -p bep --check are clean.

@minimal-foundry
minimal-foundry Bot marked this pull request as draft October 25, 2026 23:09
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — advisory review

Verdict COMMENT — 1 finding, 1 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/bep/src/main.rs Resolved
@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arch-review: PASS · the full ruling

The change conforms to Gatehouse §6.10 line 839. Segments rotate with the chain carried across. bep serve restores the revocations from every retained segment. prune keeps a segment with a revocation, or with a line it cannot parse, until the ceiling passes. The new commit reports skipped lines and amends BEP-041 to permit removal of a whole segment only. There are no conditions.

Ruled at a48affc against gominimal/arch@109541a48c31 by claude-opus-5-5.

BD0
A complete audit line that does not parse can be a revocation. bep serve
started without it and redeemed values for a destroyed box. It now
refuses the start and names each segment that holds such a line.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 2 of 10 on the runtime's review findings, pushed as c25f20d4f666.

line exit tail
cargo nextest run -p bep segment_rotation_continues_chain 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-b8cd... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 12.54s ──────────── Nextest run ID 70fa1ced-65fc-4d32-b47c-65cd5f5fc295 with nexte
cargo nextest run -p bep revocations_restored_from_every_segment_on_start 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-e957... Finished 'test' profile [unoptimized] target(s) in 0.24s ──────────── Nextest run ID 823be054-d8bb-49dc-b12b-4363f3234321 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep segment_with_live_revocation_is_retained 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-10bb... Finished 'test' profile [unoptimized] target(s) in 0.24s ──────────── Nextest run ID 58c4abd9-e85f-444e-940e-eadde5cba7ed with nextest profile: default Starting 1 test across 2
cargo nextest run -p minimal box_audit_reads_across_segments 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-0c51... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling paths v0.0.1 (/workbench/crates/paths) Compiling stdlib v0.0.22 (/workbenc

Verify min task run agent-verify exited 101 under rustc 1.98.1, which the repository does not pin (1.99.0 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

toredEnv {
    |        ^^^^^^^^^^^^^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#must_use_candidate
help: add the attribute
    |
402 + #[must_use]
403 | pub fn deferring_env() -> StoredEnv {
    |

error: this method could have a `#[must_use]` attribute
    --> crates/boxes/src/core/compose.rs:1019:12
     |
1019 |     pub fn lifecycle_hooks_teardown(&self) -> impl DoubleEndedIterator<Item = &ProvenancedHook> {
     |            ^^^^^^^^^^^^^^^^^^^^^^^^
     |
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#must_use_candidate
help: add the attribute
     |
1019 ~     #[must_use]
1020 ~     pub fn lifecycle_hooks_teardown(&self) -> impl DoubleEndedIterator<Item = &ProvenancedHook> {
     |

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1085:5
     |
1085 |     pub fn as_sandbox_path(&self) -> &SandboxRelPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1084:5
     |
1084 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use
     = note: `-D clippy::double-must-use` implied by `-D warnings`
     = help: to override `-D warnings` add `#[allow(clippy::double_must_use)]`

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1302:5
     |
1302 |     pub fn host_path(&self) -> &HostAbsPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1301:5
     |
1301 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1309:5
     |
1309 |     pub fn destination(&self) -> &SandboxRelPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1308:5
     |
1308 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use

error: could not compile `boxes` (lib) due to 6 previous errors
warning: build failed, waiting for other jobs to finish...

Outside the task's layers::

  • crates/bep/src/redeemer.rs
  • docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md

Converted to a draft: something above needs a person before review.

Review threads the coder judged:

  • 👍 valid — crates/bep/src/main.rs: bep serve now refuses to start when restore skips a line. The error names each segment that holds one and says to repair or remove those lines, then run bep serve again.

After rotation, min box audit still shows a box's whole trail across every retained segment. A restart of bep serve restores every revocation from every retained segment, so it still refuses a destroyed box. If the log holds a line that bep cannot read, bep serve now refuses to start and names the segment, in place of starting without that revocation.

Notes from the agent: Round 2: bep serve now fails closed when revoke::restore skips a complete line. It returns an error that names each segment with such a line and tells the person to repair or remove those lines, then run bep serve again. Restoring is the only path to a start, so no override flag was added. A new flag is CLI surface that no spec names. ReadBatch gains skipped_in and Restored gains unreadable so the message can name the segments. A torn tail is cut when the log opens, so only corruption or an edit makes a complete line that does not parse. New test: serve_refuses_start_on_unreadable_audit_line in crates/bep/src/main.rs.

@norrietaylor
norrietaylor marked this pull request as ready for review October 26, 2026 01:35
@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arch-review: PASS · the full ruling

The new commit makes bep serve refuse to start when an audit line does not parse. Before, it logged a warning and started without that line's revocation. Gatehouse §6.10 line 839 restores every revocation from every retained segment at start, so this refusal fails closed and conforms. bep still opens the log for append only. The PASS on a48affcc stands for the rest of the change, and this head has no conditions.

Ruled at c25f20d against gominimal/arch@109541a48c31 by claude-opus-5-5.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — advisory review

Verdict COMMENT — 1 finding, 1 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/bep/src/audit.rs Resolved
The lock is per segment. A writer that listed the directory before
another writer rotated could lock the closed segment and fork the
chain there. open_dir now lists again after the lock and moves to the
newest segment.
@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arch-review: PASS · the full ruling

The new commit makes AuditLog::open_dir list the segments again after it takes the lock. A writer with a stale listing now moves to the newest segment, or it refuses with WouldBlock. This keeps the single hash chain that Gatehouse §6.10 line 839 requires. The merge of main is clean and is not part of this ruling. The PASS on c25f20d4 stands for the remainder of the change.

Ruled at d84c1d7 against gominimal/arch@109541a48c31 by claude-opus-5-5.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — advisory review

Verdict COMMENT — 1 finding, 1 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/bep/src/main.rs Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 3 of 10 on the runtime's review findings, pushed as d84c1d7d0a9b.

line exit tail
cargo nextest run -p bep segment_rotation_continues_chain 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-67fd... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 14.59s ──────────── Nextest run ID 4b24032c-0cdd-4eed-b92a-21249814e2ac with nexte
cargo nextest run -p bep revocations_restored_from_every_segment_on_start 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-622b... Finished 'test' profile [unoptimized] target(s) in 0.30s ──────────── Nextest run ID a21614ba-9621-419c-8874-257d63735292 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep segment_with_live_revocation_is_retained 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-7534... Finished 'test' profile [unoptimized] target(s) in 0.26s ──────────── Nextest run ID 9f75f58e-8b0e-4b30-9cf3-ab343c4a0559 with nextest profile: default Starting 1 test across 2
cargo nextest run -p minimal box_audit_reads_across_segments 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-0b01... Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling paths v0.0.1 (/workbench/crates/paths) Compiling stdlib v0.0.22 (/workbenc

Verify min task run agent-verify exited 101 under rustc 1.98.1, which the repository does not pin (1.99.0 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

toredEnv {
    |        ^^^^^^^^^^^^^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#must_use_candidate
help: add the attribute
    |
402 + #[must_use]
403 | pub fn deferring_env() -> StoredEnv {
    |

error: this method could have a `#[must_use]` attribute
    --> crates/boxes/src/core/compose.rs:1019:12
     |
1019 |     pub fn lifecycle_hooks_teardown(&self) -> impl DoubleEndedIterator<Item = &ProvenancedHook> {
     |            ^^^^^^^^^^^^^^^^^^^^^^^^
     |
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#must_use_candidate
help: add the attribute
     |
1019 ~     #[must_use]
1020 ~     pub fn lifecycle_hooks_teardown(&self) -> impl DoubleEndedIterator<Item = &ProvenancedHook> {
     |

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1085:5
     |
1085 |     pub fn as_sandbox_path(&self) -> &SandboxRelPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1084:5
     |
1084 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use
     = note: `-D clippy::double-must-use` implied by `-D warnings`
     = help: to override `-D warnings` add `#[allow(clippy::double_must_use)]`

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1302:5
     |
1302 |     pub fn host_path(&self) -> &HostAbsPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1301:5
     |
1301 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1309:5
     |
1309 |     pub fn destination(&self) -> &SandboxRelPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1308:5
     |
1308 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use

error: could not compile `boxes` (lib) due to 6 previous errors
warning: build failed, waiting for other jobs to finish...

Outside the task's layers::

  • crates/bep/src/redeemer.rs
  • docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md

Converted to a draft: something above needs a person before review.

Review threads the coder judged:

  • 👍 valid — PRRT_kwDOPdfcxM6wHKgP: open_dir now lists the segments again after it takes the lock. If a newer segment exists, it releases the stale one unwritten and opens the newest, which a live writer holds (WouldBlock). Pinned by open_dir_relists_after_a_rotation. (not posted on the thread: not an open thread on the pull request)

After rotation, min box audit still shows the whole trail of a box, and a restart of bep serve still refuses a destroyed box. A second bep serve that listed the segments before a rotation can no longer append to the closed segment.

Notes from the agent: This round fixes the one review thread. AuditLog::open_dir lists the directory again after it takes the segment lock. If a newer segment is in the second listing, it drops the stale segment unwritten and tries the newest one. It tries 8 times at most, then returns WouldBlock. The new test open_dir_relists_after_a_rotation in crates/bep/src/audit.rs pins this. The four test: lines and the new test pass. cargo clippy -p bep --all-targets shows no warnings. Known edge: a stale listing can name a segment that prune already removed. Then open creates an empty file with that number before the second listing finds the newer one. That empty file holds no record and does not change the chain.

@minimal-foundry
minimal-foundry Bot marked this pull request as draft October 26, 2026 01:47
@norrietaylor
norrietaylor marked this pull request as ready for review October 26, 2026 02:22
Copy link
Copy Markdown
Member

/revise

Round 4 of #2583, on review finding 4373323173 (security, rated LOW), by the epic owner's decision on 2026-10-26. No round is in flight. Keep the fail-closed refusal as it is. The chain-preserving way out is #2588 and is not part of this round.

The finding: each audit segment has its own lock, and AuditLog::rotate creates the next segment before it drops the old file. A second bep serve that listed the directory before the rotation can lock the old segment after it is released, append there, and fork the hash chain.

  1. In crates/bep/src/audit.rs, make AuditLog::open_dir take an exclusive lock on the audit directory: a lock file in it, such as .lock, held for the life of the AuditLog. A second open of the same directory then fails, and the error names the directory and says another bep serve holds it.
  2. Make bep serve print that refusal as a user-facing error, saying what it refused, why, and the next step (stop the other bep serve and run it again). Pin the exact text in a test.
  3. Add a test: while one AuditLog holds the directory, a second open_dir fails, and rotation no longer lets a second writer append to a released segment.
  4. Reply on thread 4373323173 naming the commit, and resolve it.

Keep to crates/bep/src/audit.rs, crates/bep/src/main.rs and their tests. One commit, Conventional Commits subject, every body line at most 72 characters.


Generated by Claude Code

Each audit segment had its own lock, and a rotation released the
closed segment. A second `bep serve` with a stale listing could lock
that segment, append there, and fork the hash chain.

`AuditLog::open_dir` now locks a `.lock` file in the directory and
holds it for the life of the log, across rotations. A second open of
the directory fails. `bep serve` takes the lock before it restores
the revocations, and prints a refusal that names the directory and
says to stop the other `bep serve` and run it again.
@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arch-review: PASS · the full ruling

The compare adds one lock on the audit directory. bep serve holds this lock for the life of the log and across each rotation. This keeps one writer on the hash chain that §6.10 line 839 and architecture.md line 501 require. A second writer is refused, so the change fails closed. The baseline PASS stays. There are no conditions.

Ruled at 2899e8e against gominimal/arch@109541a48c31 by claude-opus-5-5.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — approval withheld

Verdict COMMENT — APPROVE withheld: 1 unresolved thread — minimal-foundry[bot] crates/bep/src/audit.rs:609. No findings.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

@minimal-foundry
minimal-foundry Bot comme 10BC8 nted Oct 26, 2026
Copy link
Copy Markdown
Contributor Author

Round 4 of 10 on a /revise note, pushed as 2899e8e51fba.

line exit tail
cargo nextest run -p bep segment_rotation_continues_chain 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-b0cf... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 21.90s ──────────── Nextest run ID 7b3e875d-46ad-4b73-a211-cf9198e05b56 with nexte
cargo nextest run -p bep revocations_restored_from_every_segment_on_start 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-6b82... Finished 'test' profile [unoptimized] target(s) in 0.36s ──────────── Nextest run ID 69a2a634-e409-477a-a76b-1dfe0eadffd3 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep segment_with_live_revocation_is_retained 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-94d9... Finished 'test' profile [unoptimized] target(s) in 0.36s ──────────── Nextest run ID 178a18bb-b5cc-4e96-806b-41abb73d7ce1 with nextest profile: default Starting 1 test across 2
cargo nextest run -p minimal box_audit_reads_across_segments 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-4482... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling paths v0.0.1 (/workbench/crates/paths) Compiling stdlib v0.0.22 (/workbenc

Verify min task run agent-verify exited 101 under rustc 1.98.1, which the repository does not pin (1.99.0 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

toredEnv {
    |        ^^^^^^^^^^^^^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#must_use_candidate
help: add the attribute
    |
402 + #[must_use]
403 | pub fn deferring_env() -> StoredEnv {
    |

error: this method could have a `#[must_use]` attribute
    --> crates/boxes/src/core/compose.rs:1019:12
     |
1019 |     pub fn lifecycle_hooks_teardown(&self) -> impl DoubleEndedIterator<Item = &ProvenancedHook> {
     |            ^^^^^^^^^^^^^^^^^^^^^^^^
     |
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#must_use_candidate
help: add the attribute
     |
1019 ~     #[must_use]
1020 ~     pub fn lifecycle_hooks_teardown(&self) -> impl DoubleEndedIterator<Item = &ProvenancedHook> {
     |

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1085:5
     |
1085 |     pub fn as_sandbox_path(&self) -> &SandboxRelPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1084:5
     |
1084 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use
     = note: `-D clippy::double-must-use` implied by `-D warnings`
     = help: to override `-D warnings` add `#[allow(clippy::double_must_use)]`

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1302:5
     |
1302 |     pub fn host_path(&self) -> &HostAbsPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1301:5
     |
1301 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1309:5
     |
1309 |     pub fn destination(&self) -> &SandboxRelPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1308:5
     |
1308 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use

error: could not compile `boxes` (lib) due to 6 previous errors
warning: build failed, waiting for other jobs to finish...

Outside the task's layers::

  • crates/bep/src/redeemer.rs
  • docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md

Converted to a draft: something above needs a person before review.

Review threads the coder judged:

  • 👍 valid — crates/bep/src/audit.rs:609: Fixed in 2899e8e. open_dir locks /.lock for the life of the AuditLog, and rotate carries the lock to the new segment. A second open_dir and a second bep serve are refused, so no writer can take a released segment. open_dir_refuses_a_second_writer_across_rotation tests this.

After rotation, min box audit still shows a box's whole trail, and a restart still refuses a destroyed box. A second bep serve on the same audit directory now stops at start with a message that names the directory and says to stop the other bep serve and run it again. It can no longer append to a closed segment.

Notes from the agent: Round 4 is in commit 2899e8e. AuditLog::open_dir locks <dir>/.lock (mode 0600, symlinks refused) and holds it for the life of the log. rotate() moves the lock to the new segment. The io error is 'audit directory

is held by another bep serve'. In bep serve, open_audit_log maps WouldBlock to: 'bep did not start. Another bep serve writes the audit log in .' / 'Only one bep serve can write the audit log at a time.' / 'Stop the other bep serve, then run bep serve again.' serve_refuses_a_second_writer_of_the_audit_log pins this text. bep serve now opens the log before revoke::restore, so no other writer rotates segments during the read. The per-segment lock and the re-list after it stay as a guard against an older writer that takes no directory lock. The .lock file does not match the segment name pattern, so segments(), prune and min box audit ignore it. The fail-closed refusal on unreadable lines is unchanged. Directive item 4 asks me to resolve the thread. I cannot resolve threads, so the thread verdict names the commit and the reviewer can resolve it. I did not run just clippy-strict.

@minimal-foundry
minimal-foundry Bot marked this pull request as draft October 26, 2026 03:22
@norrietaylor
norrietaylor marked this pull request as ready for review October 26, 2026 04:45
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minimal-review — approved

Verdict APPROVE — every review thread is resolved; @norrietaylor closed the last one. The review of 2899e8e51fba withheld this approval on the threads open then. Posted as a comment: the App cannot approve its own pull request; the check and the label carry it.

@minimal-foundry minimal-foundry Bot added the review:approved minimal-review approved the current head label Oct 26, 2026
@norrietaylor norrietaylor changed the title T33 — Rotate audit segments with the chain carried across and revocations restored feat(bep): rotate audit segments, carry the chain and restore revocations at start (T33) Oct 26, 2026
@norrietaylor norrietaylor left a comment
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Babysitter approval at 2899e8e: every merge gate passes at this head (arch PASS at 2899e8e, zero open threads, every lane aggregator green). Squash title feat(bep): rotate audit segments, carry the chain and restore revocations at start (T33) and the squashed message pass commitlint. Queued for squash merge.


Generated by Claude Code

@norrietaylor
norrietaylor added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit 9d3dc96 Oct 26, 2026
38 checks passed
@norrietaylor
norrietaylor deleted the agent/2290-rotate-audit-segments-with-the-chain-carried-acr branch October 26, 2026 05:17
minimal-foundry Bot added a commit that referenced this pull request Oct 26, 2026
The revocation helper that main added in #2583 builds a Decision
without jti, which failed the merge-queue build with E0063.
yermakoffivan pushed a commit to yermakoffivan/minimal that referenced this pull request Oct 26, 2026
…l#2596)

* feat(bep): carry the handle jti into the audit record (T68)

The forwarder asks the redeemer for the handle's jti beside its verdict,
and writes it into each decision record. min box audit shows it. The
jti is empty when the request carries no handle or the handle does not
parse.

* test(bep): expect jti in the audit record field set

* docs(bep): say a refused record's jti is the handle's claim

Only an admitted record's jti is verified, since decide admits only a
verified handle. A refusal keeps the claimed jti so a forged handle
still correlates.

* fix(bep): log the handle jti escaped in the redeemer

A refused handle's jti is an unverified claim, so a sender controls
it. Debug formatting escapes control characters in the log line.

* fix(bep): set an empty jti on the revocation test record

The revocation helper that main added in gominimal#2583 builds a Decision
without jti, which failed the merge-queue build with E0063.

---------

Co-authored-by: minimal-foundry[bot] <327719912+minimal-foundry[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review:approved minimal-review approved the current head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

T33 — Rotate audit segments with the chain carried across and revocations restored

1 participant

0