Repository navigation
fix(native): keep policy refusal reasons past the storage deadline and exclude only carrier proofs - #421
Merged
Conversation
…d exclude only carrier proofs - A carrier boundary now keeps a refusal's own observed reason (an issued helper policy boundary or a classified command cause) even when it is caught after the carrier deadline. `storage-budget` is reported only for a deadline-shaped refusal (timeout, budget, lifetime mark or no observed cause) whose carrier deadline expired and was not cancelled. - A recorded storage command that replaces an admission refusal with its fixed command refusal now carries the source's issued boundary, so a helper policy refusal observed during admission is no longer relabeled `storage-budget` at `storage-helper-start`. - The start deadline's storage exclusion now wraps only each carrier proof (carrier creation and witness verification). The guard's freshness checks and saved-state reads consume the start budget. - The exclusion-cap comment states that the uncapped sum is finite, (5B+3) x V x S, but can reach hours. Refs HACK-1361 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
roodboi
marked this pull request as ready for review
October 24, 2026 02:42
roodboi
added a commit
that referenced
this pull request
Oct 24, 2026
Combine HACK-1365's commandCause classification with #421's (HACK-1361) publication source: refuseCommand now takes both the classified cause of a rewrapped admission rejection and the original refusal as source, so a helper policy refusal keeps its issued boundary and a cancelled probe or exhausted budget keeps its reason. Both sides' command-record tests are kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fix unit G50: storage-budget diagnostics follow-ups after #375 (HACK-1361).
(1) A policy refusal caught after the carrier deadline was reported as
storage-budgetRoot cause, in
src/lib/native-compose-storage-witness-docker.ts:nativeComposeStorageCarrierBudgetReasonreturnedstorage-budgetfor anyrefusal once the carrier deadline had passed, without looking at the error's
own observed reason.
through
runNativeComposeStorageDockerCommandwithoriginalCommand. When itsadmission check (
assertAdmitted, which re-inspects the helper throughcheckNativeComposeStorageDockerCarrier) refuses with a helper policy reason,the runner replaces that error with its fixed command refusal
(
refuseCommand). The replacement dropped the issuedstorage-helper-policy/helper-*boundary. The outerinvokecatch thenlabelled it
storage-helper-startwithstorage-budgetif the deadline hadpassed (and
unclassifiedotherwise). The human summary then told theoperator to raise
HACK_NATIVE_STORAGE_TIMEOUT_MSfor a policy failure.Fix:
nativeComposeStorageCarrierBudgetReasonnow takes the error's issued reason(registry boundary, or the classified command cause) first. It reports
storage-budgetonly for a deadline-shaped reason (probe-timeout,probe-budget,storage-budget, or no observed cause) when the deadline hasexpired and the carrier was not cancelled. Any other observed reason, such as
helper-host-policy, is kept.refuseCommandtakes an optional source and copies its issued boundary ontothe replacement refusal (first attach still wins). The runner keeps the first
publication-stopping error (
publicationSource) for the bounded-await andchild-publication paths, and the outer
originalCommandcatch passes thecaught error.
Diagnostics only: no check, deadline, retention or recovery rule changes.
(2)
start.excludeStoragewrapped all ofstorage.verifyThe exclusion covered
assertFresh(twice) and bothstore.loadCurrentreadsas well as the carrier proofs. That non-carrier time also extended the start
deadline, and the per-carrier bound does not limit it. I narrowed it:
NativeComposeCommandStorageVerifyOptions.excludeProofnow wraps only eachenrolled volume's carrier proof (fresh carrier creation plus
verifyNativeComposeStorageXattrWitness).executePreparedGenerationpassesstart.excludeStoragethrough it. The cap entry and exit checks now run percarrier proof, which matches the documented "a proof that would start at or past
the cap is refused before it runs".
(3) Comment precision
The
NATIVE_STORAGE_EXCLUSION_MULTIPLEcomment no longer calls the uncapped sum"unbounded in practice". It now says the sum is finite, at most (5B+3) x V x S,
and can reach hours. For example, 4 builds and 3 volumes allow 69 carriers:
about 1.7 h at the 90 s default and 11.5 h at the 600 s maximum. The
createNativeComposeStartDeadlinedoc and the reference docs say which span isexcluded.
Tests (each proven by mutation)
tests/native-compose-storage-witness-docker.test.ts: "a helper policyrefusal caught after its carrier deadline keeps its policy reason". It uses a
real
checkNativeComposeStorageDockerCarrierhost-policy refusal and a fakeclock past the deadline. Mutation: drop the deadline-shaped precondition
(the old behavior) ->
storage-budgetreceived, test fails.tests/native-compose-storage-command-record.test.ts: "a helper policy refusal{before arming, after arming, after the stopped child is published} keeps its
own reason after the carrier deadline". It uses the real recorded-command
runner with a temp private directory, a child that is never resumed (marker
absent), and a fake clock for the reason. Mutations:
refuseCommandwithoutpublicationSource-> thechild-published case fails;
refuseCommandwithout the copy -> all 3 fail.tests/native-compose-start-storage-exclusion.test.tspasses (about 103 s onthis loaded Mac). Mutations, each killed (
upexits 1, workload notadmitted):
excludeProof: start.excludeStorageremoved from the guard;verifyignoringoptions.excludeProof.Evidence
mise x bun@1.4.2),bun install --frozen-lockfile; nobun.lockchange.bun run --cwd packages/cli typecheck, rootbun run typecheck, rootbun run check(privacy-check ok): 0 errors. Biome on the changed filesreports only 3 existing complexity warnings with unchanged scores (checked
against the
origin/nextcopy).gitleaks dirover thegit log -p origin/next..HEADexport): no leaks.
The files are: storage command record, docker witness, docker IO, xattr owner,
startup budget, effect diagnostics and build. The failure was
runEffect captures the original fresh carrier factory ..., which hit the 5 s defaulttimeout. It passes serially in 3.7 s with
--timeout 60000.tests/agent-instruction-source.test.ts(reads the changed docs) passes.Gaps
mutations. I did not add a unit test that pins
assertFreshor thesaved-state reads outside the excluded span. A full proof through
prepareNativeComposeCommandStorageneeds a real witness.refuse()when are-inspected carrier's
createdAtor policy string changed) still has noobserved reason. Past the deadline it is still reported as
storage-budget.Tagging those checks is a separate change (see findings).
qualifying host for them.
Refs HACK-1361
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.