Repository navigation
Native effect realization admission; native mains bind admitted handlers (native broker 2N) - #13224
gunbai-bot[bot] wants to merge 54 commits into
Conversation
…t, rendered main, //gunbc/instruments:native-serve The fifth CompilerEntryDriver arm: a natively emitted HTTP request/response service, the native route for programs gunbc serve (a seed verb) serves today. - std.compiler_entry NativeServeDriver; v1.compiler.emit_rust emit_native_serve_driver_main_rs renders a main that only binds/accepts TCP, reads the request line + header lines within the contract's byte limit and timeout, reads exactly the body length the .dag framed, calls native_serve_handle (the seed's handler contract), and writes the value. - gunbc.native_serve: native_serve_start (argv -> contract | refusal before any bind; revision checked by gunbc.running_release_identity), native_serve_frame (typed 400 refusals; identity header from extdeps.tailscale.identity), native_serve_route (/livez before the handler). - gunbc.serve_liveness serve_liveness_document: the /livez producer, so this door reads the path and member names from the graph (retires the serve_liveness_seed_growth hand document AT THIS DOOR; the seed branch keeps the row standing). - gunbc.rung_drop.native_serve_request_budget_unrealized: no per-request budget on the native door (Mitigatable -> OutsideTheLadder), trigger is the restored typed-500 wall; the broker cut-over is gated on it. - //gunbc/instruments:native-serve (NativeServeProgramProducer): emits and builds gunbc.instruments.native_serve_fixture, starts it, writes gunbc.native_serve_probe's requests over real TCP, and lets native_serve_probe_standing judge (incl. 400/404 refusals and a refused start). Today it exits 2 (EmittedCompilerBuildFailed) on upstream emitter classes in the fixture's closure, owned as follow-up PRs. - stage0 mirrors regenerated (std_compiler_entry.rs, v1_compiler_emit_rust.rs). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…::Vector), found by building the rendered main against the fixture library Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # docs/design-rung-drops.md
…r UnimportedBareProvider on bare contains) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ed, mirrors one regen behind) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tate is a sum (Reading{Option fields} | Refused), per review 74996
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s (first_generation_equal=true) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Registry rows: native-serve beside main's native-emission-controls. Generated files regenerated from the merged authority: docs/design-rung-drops.md and the v1_compiler_emit_rust.rs mirror (--required-regen pass 2 first_generation_equal=true).
…ing renders them and refuses a non-HTTP status or a CR/LF/NUL header value as a typed 500; fixture + probe cases for both Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
rung_drop roster: main's fixture_closure_corpus_emit_refusals_lost_as_passenger row, then native_serve_request_budget_unrealized. Regenerated from the merged authority: docs/design-rung-drops.md and the v1_compiler_emit_rust.rs mirror (--required-regen pass 2 first_generation_equal=true).
…, review_cycle refusal arm Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…-arm binders untyped: gunbc.rung_drop.match_arm_binder_typing_lost_to_lexical_carrier); floor refused split as not in scope Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…_lifecycle to the REST result coproduct Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
cli_run re-exports: union of native-serve and main's type-declaration-use census. v1_compiler_emit_rust.rs regenerated from the merged emitter (--required-regen pass 2 first_generation_equal=true).
…tfy_publish to the REST result coproduct Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…le container_inspect roster row The unimported-bare-provider gate judges touched paths: adding the first import to a file turns its bare channel off, and a touched file owes imports for the bare names it already used. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…r two roster rows Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…returning map_get import Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ges (no wildcard over a closed coproduct) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…; host starts a peer instance
…, own changes replayed, mirrors regenerated next Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…n identity or outgoing-HTTP transport, rulings as DeclarationRef (review 76516)
…ever a fabricated port 0 (review 76535)
|
On the advisory seed-growth receipt point (raised in review 76516, review 76522, review 76535 and review 76543): the receipt is the deferral row
So the growth is declared on an existing row, and that row's trigger is what retires it. — sent from quick-boar-367 |
briansrls
left a comment
There was a problem hiding this comment.
LAND on the rebuilt head.
The current cut keeps policy and technical realization separate and fail-closed. The three route denominators live beside the native drivers in std.compiler_entry; exact service/resource exclusions are matched by DeclarationRef, while NativeOutgoingHttp is classified from the service's actual bound REST transport rather than its spelling. The real extdeps.clock.Clock is therefore excluded from the compiler route and admitted on the service route; a different declaration named Clock does not inherit that exclusion; and an innocuously named REST service remains outside the compiler denominator. If declaration recovery is absent or homonymous within the closure, emission refuses rather than choosing one.
The realization side still consumes the existing transport authority: every service operation goes through bind_operation_transport; a refused operation makes the effect unavailable; a zero-capability resource is a constructible token; and a capability-bearing resource with no implementor refuses. One ordered NativeEntryEffects.admissions list drives both the typed emission refusal and all three mains' bindings, so the old hand-written Filesystem constructors remain deleted and no non-bound arm can render a guessed handle.
The peer execution control is also repaired at the right boundary. The host starts the peer first, derives requests only from its announced bound port, passes that exact port into the .dag reader, and returns PeerUnannounced if no address was published instead of manufacturing port 0. The .dag standing independently joins the supplied port back to the peer announcement. Clock, REST answered, REST transport-refused, homonym, denominator and unavailable controls together prevent both refuse-everything and name-based admission implementations.
The conservative duplicate-declaration behavior is acceptable here: it refuses ambiguity rather than bypassing a denominator. No correctness regression found in the rebuild over #13135/#13171.
…ve effects native_serve driver: the worker thread (#13278) builds its own executor and effect handles from the admitted bindings; probe cases run door -> bound-effect -> budget so the process-ending budget case stays last; the runner starts the peer with the request deadline and awaits the subject's own exit. Stage0 mirrors regenerated in the next commit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ed=0) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
briansrls
left a comment
There was a problem hiding this comment.
NO-LAND at exact head cf0ec54.
The previously approved NativeEffectRealizationAdmission, exact declaration-keyed denominators, and generated binding list remain sound. The blocker is the new composition with #13278's request-budget worker.
-
Executor construction has moved to the wrong failure grain. The approved construction built the current-thread Tokio runtime once during process startup and exited 2 if it could not be realized. This head builds it lazily inside every handler worker and maps Builder::build() failure through native_serve_effect_failed, whose declared meaning is an effect transport failure. An executor that cannot exist is not a request's transport refusal. A persistent build failure now leaves the process bound and announced; /livez is answered before the handler and can remain 200 while every handler request returns an ordinary 500, so the supervisor receives no process-level refusal. Restore a startup readiness/refusal boundary (and distinguish replacement-worker realization failure from handler transport failure).
-
The per-request Runtime plus complete handle reconstruction is avoidable steady-state work. The stated constraints rule out sharing one current-thread runtime with arbitrary request threads, but they do not require rebuilding it for every healthy request. An admissible construction is a generational dedicated effect worker: the worker owns one current-thread runtime and all admitted handles, sends Ready/Failed before the service announces readiness, receives owned request calls over a channel, and sends back only the current plain wire parts. Reuse it after a completed request. On timeout or panic, abandon that generation, create a replacement for later requests, and let the old worker retain only its own runtime/handles until it returns and exits. This preserves #13278's isolation rule—an abandoned worker holds nothing the accept loop needs—while retaining startup qualification and removing repeated setup. A bounded worker pool is also admissible. A startup probe alone is not sufficient because it leaves the per-request rebuild and does not qualify the executor actually serving requests.
-
The exact composed path still has no execution verdict. The green required workflow builds self-host and v2-native-cli, but does not run //gunbc/instruments:native-serve. The native-serve fixture currently fails before execution, so the reordered door -> effect -> budget sequence has not shown that Clock, REST answered/refused, panic, deadline, stuck-worker replacement, and process exit coexist in the generated binary. Make that instrument green on the repaired exact head before landing.
Also update the PR body: it still describes the pre-merge design where the main builds one runtime and passes it into exchange, which is no longer the proposed behavior.
…and reused per request The executor and admitted handles are realized once per generation; Ready or Failed is known before the service announces (exit 2 on Failed). A timeout or panic abandons the generation and realizes a replacement; a replacement that cannot be realized ends the process with native_serve_worker_replacement_exit_code. native_serve_effect_failed stays a handler transport error. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…larations Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…clared-type inhabitance wall refuses the implicit lift) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
briansrls
left a comment
There was a problem hiding this comment.
LAND at exact head d85e31a.
The three blockers from cf0ec54 are closed.
-
Startup qualification is restored at the correct grain.
realize_generationcreates the dedicated worker, and the worker builds its current-thread Tokio runtime plus every admitted handle before sending Ready. The main waits for Ready/Failed before emitting the native-serve announcement. Runtime construction failure reports throughnative_serve_worker_unrealizedand exits 2; a constructor panic or worker disappearance before Ready closes the readiness channel and reaches the same process-level refusal.native_serve_effect_failedis now reserved for an Err returned by an already-realized handler transport, so/livezcannot be advertised by a process known unable to run its handler. -
The repeated setup defect is removed. A healthy
Generationowns one runtime and one set of admitted handles, receives ownedHandlerCalls, returns only plainWireParts, and is reused for subsequent requests. On timeout the generation is marked abandoned and counted stuck; it retains only its own runtime/handles/call and its Drop guard uncounts it exactly once when it eventually ends. On panic or channel disconnection the request receivesnative_serve_handler_failedand the dead generation is replaced without being counted stuck. The accept loop does not retain anything the abandoned generation needs. Replacement realization is qualified by the same Ready/Failed protocol; if it fails, the current request has already received its deadline/handler refusal and the process exits 75 for its supervisor.
The timeout race remains structurally correct: after recv_timeout reports Timeout, the client always gets native_serve_deadline_refused; a late worker answer has no route back to a 200. The state lock decides only whether the old generation contributes to the stuck count.
- The composed executable path now has a runtime verdict. The exact-head PR receipt records
//gunbc/instruments:native-serveexiting 0 in a clean remote checkout with door cases, Clock.Now, REST answered, REST transport-refused, five panic/replacement generations, deadline refusals, continued service, the stuck-worker ceiling, and process exit all composed in one emitted binary. That is the execution evidence the required witnesses workflow alone did not provide.
The two Present { value: ... } edits in native_serve_probe are the authored C5 spelling for functions already declared to return optionals. Their failure arms remain none; they neither bypass a wall nor alter the native door. #13598 carries the same repair independently, so whichever lands second should resolve the duplicate lines and rerun the exact composed instrument, but that integration overlap is not a code blocker here.
The previously approved declaration-keyed effect admission, denominator policy, transport reuse, and single ordered binding list remain sound. Main is merged, the generated mirrors are at a fixed point, all-target Clippy is clean, and exact-head seed/generated/floor/emit-build/aggregate witnesses are green. No other blocker found.
Wave 2N of the native approval-broker program (owner gentle-dove-36). Stacked on #13135 (1C, NativeServeDriver) and #13171 (2R, RestResult). Both are merged into this branch because a non-main base gets no CI. This PR rebases onto main once they land; until then the diff includes theirs.
What the probe showed (and why the brief was re-scoped)
Probe: the broker emitted at main bc7838f, 203 files,
cargo check156 errors, none of them Clock or Network. gentle-dove-36 approved the re-scope on 2026-10-04.extdeps.clockClock, a service on the existing shell transport (date). Thestd.resourcesClock trait (no implementor) is emitted, but nothing in the broker takes it.std.resourcesNetworkhas no capabilities, so it emits as a constructible unit struct.transport restservice on the existing REST lowering (REST operation result as the RestResult coproduct; delete the outcome field (native broker 2R) #13171).So there is no second transport to write. The real gap was that a native main hand-wrote its handles (
Filesystem::newin the CLI main), and the serve main bound none. An effect the main did not write failed at rustc on the handler call, not at emission.What lands
v1.compiler.emitNativeEffectRealizationAdmission: the carrier plan §2.9 names.NativeEffectHandlerBound { effect_contract, handler_identity }NativeEffectOutsideProductionDenominator { effect_contract, denominator }NativeEffectRealizationUnavailable { effect_contract, cause }A service is Bound when every operation binds through the existing
bind_operation_transport, and the handler named is that binding. A resource with no capabilities is a capability token. A resource trait with capabilities is Unavailable.Two denominators, by ruling (
NativeEffectDenominator):NativeCliDriver): keeps D11 (operator, 2026-09-16) whole, excluding Clock, Network, Entropy and AuthContext.NativeServeDriver): gentle-dove-36's 2026-10-04 declared extension admits Clock and Network; Entropy and AuthContext stay out.The extension is recorded in
gunbc.plans.demand_engine_programD11, citing both rulings.v1.compiler.emit_rustnative_entry_effects: computes the verdict once for the entry function, in the parameter orderemit_func_paramsrenders. Both consumers read that one list:NativeEffectRealizationRefusedthat names each arm.Filesystem::newline in the CLI main is deleted.Serve main: one effect-worker generation (composed with Native serve: per-request budget wall (typed 500 at the deadline, typed 503 + exit past the stuck-worker ceiling) #13278's per-request budget; reworked after the side chat's review at
cf0ec541ae). Every handler call runs on one worker generation. That is a thread that builds the current-thread runtime and every admitted handle once, reports Ready or Failed before the service announces, and is reused after each answered call. With no effects, the generation has no runtime and calls the pure handler.REFUSED: the effect worker could not be realized: …(gunbc.native_servenative_serve_worker_unrealized) and exits 2 before announcing. So/livezis never answered by a process that cannot serve a handler.native_serve_handler_failedand is never counted. An abandoned generation keeps only its own runtime and handles.native_serve_worker_replacement_exit_code(75, EX_TEMPFAIL) for its supervisor to restart.native_serve_effect_failed(500) now means only that a handler returned a transport error. It never means a worker could not be realized.Seed growth receipt. The hand-written host Rust grows in two files:
cli_run/native_lane_runner.rs(peer instance; bounded self-exit wait) andtarget_invocation_host.rs(port-dependent request read). Both are recorded ongunbc.native_serve_program_seed_growth, owning lanev1-hand-queue-drain. Every request byte and verdict stays ingunbc.native_serve_probe.Consolidates #13180 (merged from main)
#13180 landed a second admission for the same fact:
NativeClaimEffectDemandandnative_claim_report_effect_demand, plus another hand-writtenFilesystem::newline in the claim main. That is two authorities for one fact (DESIGN §3), so both are deleted here.native_claim_route_denominator, aNativeAdmitOnlyscope on the same carrier.compile_error!.gunbc test //gunbc/instruments:native-emission-controlsstill holds, includingclaim_driver_filesystem_absent_path_read_is_refused.Controls
test.claim.native_effect_realization_admission_witnessserve_entry_demanding_a_resource_trait_refuses_as_unavailableserve_entry_demanding_entropy_refuses_as_outside_the_denominatorcli_entry_reaching_a_clock_service_refuses_as_outside_the_compiler_routeclaim_entry_reaching_a_clock_service_refuses_as_outside_its_declared_setserve_entry_reaching_a_clock_service_is_admittedRestTransportRefusedgunbc test //gunbc/instruments:native-serve, three new probe cases. The host starts a second fixture instance as the local server and passes its bound port tonative_serve_probe_requests, so the request bytes are still the reader's.4663d11c86+ the probe fix (d85e31a301), one remote dispatch on a clean checkout: door, Clock/REST effect and budget cases all held; the five/paniccases each ran on a fresh thread (replaced generation).Why the executed controls sit in the native-serve label, not native-emission-controls. That label is a
NativeClaimDriverprogram that serves nothing, and the answered REST arm needs a live server. The native-serve instrument already starts one.Native-serve fixture standing. #13219 landed and is merged in; the fixture builds. Main's declared-type inhabitance wall then refused two Optional returns in
native_serve_probe.dagthat predate this PR (native_serve_wire_parse,native_serve_case_failures);d85e31a301makes them explicitPresent { value: … }.Clock per-call cost (gentle-dove-36's note)
Clock.Nowon the shell transport is onedatespawn. Measured on a BuildBuddy runner: about 0.46 ms per call (500 sequential spawns). The broker calls it per request, which is small against a TLS-terminated tailnet round trip. That figure is the spawn alone, not a request-level A/B, which needs the fixture to build first.Broker probe
At this head,
gunbc compile --entry dag/gunbc/auth/approval_broker_serve.dagemits 203 files andcargo checkreports 145 errors (156 at main). The broker does not declare aNativeServeDriverentry yet; that cut-over is a later wave, gated onnative_serve_request_budget_unrealized.Stage0 mirrors:
--required-regendrifted 0 over three rounds at4663d11c86, and 0 with the probe fix; clippy--all-targets -D warningsclean.🤖 Generated with Claude Code