Repository navigation
fix(runtime): retire authored starts refused before journal reservation - #355
Merged
Merged
Conversation
A native consumer that refuses before reserving its runtime journal (for example native_graph_admission for a repository@sha256 image, the M3 next.5 repro) leaves no receipt, so Removed retirement could never succeed and the start intent stayed pending with no supported recovery. graph native inspect now refuses the distinct native_graph_unreserved on macOS only when the run's journal directory is exactly absent under private state directories and no foreground owner is published. Startup retires the exact start intent and source for that refusal after its consumer settled, reporting outcome `unreserved` with the original native code. `down --recover` retires a pending start the same way under fresh startup admission. Every other inspection result keeps all evidence; receipt and ownership checks are unchanged. Refs HACK-1272 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
roodboi
marked this pull request as ready for review
October 20, 2026 17:58
roodboi
added a commit
that referenced
this pull request
Oct 21, 2026
Brings #355 (G4, HACK-1272: retire authored starts refused before journal reservation), #352, #341, #342 and #346 into the G1 closed-refusal branch. Both sides' behavior is kept. Conflict resolutions: - docs/reference/native-authored-commands.md: keep G1's explanation of `pending` (in-progress start or failed start with retained evidence; only the owner's cleanup proof may retire it) and append G4's pointer that `down --recover` retires an attempt that never reserved runtime state when no startup is running. - src/lib/native-authored-command.ts: the `pending` status note keeps G1's wording and adds G4's `hack down --recover` hint. Auto-merged code keeps G4's canceled `unreserved` exit 130 ahead of G1's stopped-runtime classification. Semantic fix: G1's stoppedRuntimeStartup message map had no entry for G4's new `unreserved` outcome, so a runtime_not_running start that was retired as unreserved would have produced an undefined message. It now reports E_DOCKER_UNAVAILABLE with an `unreserved` message (intent retired, `hack ps` reports not_started). G4's `down --recover` pending-start path already refuses through commandFailure as a closed coded HackCliError (E_LIFECYCLE_FAILED, values omitted), and unreserved startup failures map to E_STARTUP_INCOMPLETE or E_DOCKER_UNAVAILABLE, so no stack or help is printed. - tests/native-authored-project-start.test.ts: the fixture keeps G1's `stoppedDuringServe` and G4's `failBeforeReceiptCode` and `unreservedInspection` options. The stub serve emits G4's configurable failBeforeReceipt code and G1's wrapped stopped-runtime refusal. The stub inspect answers both failure modes, with `native_graph_unreserved` only when requested. The duplicate native-runtime-client import is merged into one. Semantic fix: G4's HACK-1272 call-log expectation now includes G1's read-only `runtime status --json` preflight between plan and serve. Both sides' tests are kept. - tests/native-authored-command.test.ts (auto-merged): add an `unreserved` case to the stopped-runtime startup table. Verification (Bun 1.4.2, macOS): - packages/cli typecheck and check: 0 errors. - native-authored-command, native-refusal-cli, native-authored-command-dispatch and cli-spawn-env-isolation: 124 pass. - Other native-authored-* and native-runtime-* suites: 409 pass, 5 fail. 2 are load-timing failures that pass when rerun alone. 3 are host live-session census failures in the recovery and incarnation suites, which fail identically on origin/next and ead8383 on this host. - native-authored-project-start (--timeout 60000): 48 pass. The 3 host-metadata cases are intermittent (HACK-1299). They also fail intermittently on origin/next and ead8383 and pass when run alone. - Six scripts/check-native-*-plan-cli.ts: all pass after build and build:config-compiler. - docs:cli-reference and generate:agent-plugins: no drift. 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.
Refs HACK-1272
Root cause
The M3 authored fixture on published
v5.0.0-next.5declaresdocker.io/library/busybox@sha256:<manifest digest>. That is a repositorydigest, not the
sha256:image content ID that the bounded native consumerrequires.
config validateandgraph native planaccept it. After startup hasreserved the frontend start intent and source envelope,
graph native servelowers the configuration and refuses
native_graph_admission(
configuration_with_source→image_id). This happens beforejournal::reserve, so no runtime journal or receipt exists. Because the journaldirectory is absent, the Rust foreground owner retires its own publication.
The TypeScript startup then runs Removed retirement: it calls
graph native inspect --run-id. With no journal,journal::loadrefuses withthe generic
native_graph_receipt. On next.5 that inspection code overwrote thestartup code. #331 (54cc4c9, already on
next) fixed that masking, so currentnextreports(native_graph_admission).The remaining defect is still on
next. A refusal before reservation leaves thestart intent and source retained forever:
psreportspending.downhas no Ready mapping to stop.down --recoverrequires a complete stored generation.The same thing happens for any refusal before reservation: image identity,
network intent, capacity, review change, cancellation, or engine connect. The
receipt path could not tell "this run never reserved anything" apart from
damaged or foreign receipt evidence, so nothing could safely retire the attempt.
The 6/6 refusal is consistent with this. All six variations use the same image.
Pool, branch, port,
HACK_SERVICE_NAMEand readiness are not part of the refusingcheck. This analysis is read-only, from the retained records and
s4-authored-start.shon the M3. Nothing was re-run there.Fix
Rust:
graph native inspect(macOS only). Inspect now refuses with adistinct
native_graph_unreservedbefore guest access. It does so only whenall of these hold:
foreground::require_unpublishedfinds no owner publication;A partial directory, a public ancestor, a published or abandoned owner, or a
present journal keeps the existing refusals and validation. No receipt or
ownership check is relaxed.
Startup. After the consumer process settles, Removed retirement may fail
with exactly
native_graph_unreserved. In that case startup retires the exactstart intent and the validated source, and the startup failure reports outcome
unreservedwith the original native code. This applies only when all of thesehold:
psthen reportsnot_started. A canceled unreserved startup exits 130, likeconfirmed removal. Every other inspection result retains everything, as before.
down --recoverfor a pending start. This is the supported recovery forattempts already retained, for example by next.5. It applies when a start
intent exists with no Ready mapping and no recovery intent. The command:
so the command refuses as busy.
native_graph_unreserved.It does not compile input, acquire values, serve, or touch the guest.
Stored-generation recovery is unchanged.
Status note and docs. The
pendingtext note now points todown --recover.docs/reference/native-authored-commands.mdand theruntime-core README are updated.
Tests
Each new test was checked by mutation: with the fix reverted the test fails, and
with the fix restored it passes.
inspect_names_only_an_exactly_absent_unpublished_journal_as_unreservedcovers an absent journal, a published owner, a finished owner, a partial
directory, a public ancestor, and a reserved journal. With the inspect check
reverted, the test reports
runtime_not_runningand fails.tests/native-authored-project-start.test.ts:native_graph_admission, then inspect refusesnative_graph_unreserved. The test expects outcomeunreserved, the nativecode kept, no artifacts left, and
psnot_started. With the fix reverted,the outcome is
retained.down --recoveron the next.5-style retained state:native_graph_receipt,native_graph_foreground,runtime_not_running, and a successful inspectioneach keep both files and
pending. Onlynative_graph_unreservedretiresthem. With the refusal-code check reverted, the test fails.
tests/native-authored-command.test.ts: a pending unreserved start goes tothe new
down --recoverowner, and its failures map toE_LIFECYCLE_FAILED. Acanceled unreserved startup exits 130. With either change reverted, these tests
fail.
Commands run locally on macOS (pinned Rust 1.97.1 and Bun 1.4.2 via mise):
bun run --cwd packages/cli typecheck: 0 errors. This includes the tests.bun run --cwd packages/cli check: 0 errors. The warnings already existed; thechanged files are clean.
bun run check:local: passed.cargo clippy -D warnings: passed.provider::graph::nativetests: 181 passed.tests/cli-spawn-env-isolation.test.tsplus the native-authored command,dispatch, observe, run, and mutation-lease suites: passed.
bun run test:local: 980 passed and 7 failed. The 7 failures areprovider::lifecycle::{host_filesystem,short_home,interrupted}tests failingwith
provider_timeout_uncertain. The same 7 fail with unmodifiedorigin/nextruntime-core on this host. The default-feature build does notinclude the changed module, which is gated by
native-config-plan.Remaining gaps
runners and the Rust filesystem fixture.
30–65). Under that load, the ready-path fixture in
native-authored-project-start.test.tsmisses its ready window. The firstfailure is "holds admission through exact ready publication", which fails the
same way on unmodified
origin/next. The fixture's settlement guard then skipsthe rest of the file. The three no-ready cases touched here pass when run on
their own. Some
native-runtime-clientdescendant-pipe timing cases were alsoflaky; this change only adds a constant to that module. A clean exact-head CI
run is required.
these is still retained, and no recovery is added for it.
reserving its journal and its own cleanup also fails, the journal-backed
attempt is still retained with no supported recovery.
config validateandgraph native planstill accept arepository@sha256:digest that executionthen refuses. This PR does not change that; it is reported as a separate
finding.
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.