Skip to content

Native effect realization admission; native mains bind admitted handlers (native broker 2N) - #13224

Closed
gunbai-bot[bot] wants to merge 54 commits into
mainfrom
session/quick-boar-367
Closed

gunbai-bot[bot] wants to merge 54 commits into
mainfrom
session/quick-boar-367

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

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 check 156 errors, none of them Clock or Network. gentle-dove-36 approved the re-scope on 2026-10-04.

So there is no second transport to write. The real gap was that a native main hand-wrote its handles (Filesystem::new in 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.emit NativeEffectRealizationAdmission: 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):

    • Compiler route (NativeCliDriver): keeps D11 (operator, 2026-09-16) whole, excluding Clock, Network, Entropy and AuthContext.
    • Service route (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_program D11, citing both rulings.

  • v1.compiler.emit_rust native_entry_effects: computes the verdict once for the entry function, in the parameter order emit_func_params renders. Both consumers read that one list:

    • Emission refuses with a typed NativeEffectRealizationRefused that names each arm.
    • Both native mains bind exactly the admitted handlers. The hand-written Filesystem::new line 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.

    • Startup refusal: if the generation cannot be realized, the main prints REFUSED: the effect worker could not be realized: … (gunbc.native_serve native_serve_worker_unrealized) and exits 2 before announcing. So /livez is never answered by a process that cannot serve a handler.
    • Deadline or panic: the generation is abandoned and a replacement is realized. A timed-out generation is counted stuck under Native serve: per-request budget wall (typed 500 at the deadline, typed 503 + exit past the stuck-worker ceiling) #13278's ceiling and uncounts itself when it ends; a panicked one is answered with native_serve_handler_failed and is never counted. An abandoned generation keeps only its own runtime and handles.
    • Failed replacement: if the replacement cannot be realized, that is a process-level fault. The request in hand is answered, then the process exits with 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) and target_invocation_host.rs (port-dependent request read). Both are recorded on gunbc.native_serve_program_seed_growth, owning lane v1-hand-queue-drain. Every request byte and verdict stays in gunbc.native_serve_probe.

Consolidates #13180 (merged from main)

#13180 landed a second admission for the same fact: NativeClaimEffectDemand and native_claim_report_effect_demand, plus another hand-written Filesystem::new line in the claim main. That is two authorities for one fact (DESIGN §3), so both are deleted here.

  • The claim driver's declared set {Filesystem} is now native_claim_route_denominator, a NativeAdmitOnly scope on the same carrier.
  • The claim main binds whatever the verdict admits.
  • An undeclared demand now refuses at emission with the typed diagnostic. Before, it produced a main that failed with compile_error!.
  • gunbc test //gunbc/instruments:native-emission-controls still holds, including claim_driver_filesystem_absent_path_read_is_refused.

Controls

Path Control Result
Emission refusal, Unavailable test.claim.native_effect_realization_admission_witness serve_entry_demanding_a_resource_trait_refuses_as_unavailable PASS (remote, claim_batch)
Emission refusal, Outside (service route) serve_entry_demanding_entropy_refuses_as_outside_the_denominator PASS
Emission refusal, Outside (compiler route keeps D11) cli_entry_reaching_a_clock_service_refuses_as_outside_the_compiler_route PASS
Emission refusal, Outside (claim route's declared set) claim_entry_reaching_a_clock_service_refuses_as_outside_its_declared_set PASS
Positive control serve_entry_reaching_a_clock_service_is_admitted PASS
Executed Clock.Now plausible + monotone; REST answered against a local server; refused port gives RestTransportRefused gunbc 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 to native_serve_probe_requests, so the request bytes are still the reader's. HELD (exit 0) on this branch at 4663d11c86 + the probe fix (d85e31a301), one remote dispatch on a clean checkout: door, Clock/REST effect and budget cases all held; the five /panic cases 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 NativeClaimDriver program 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.dag that predate this PR (native_serve_wire_parse, native_serve_case_failures); d85e31a301 makes them explicit Present { value: … }.

Clock per-call cost (gentle-dove-36's note)

Clock.Now on the shell transport is one date spawn. 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.dag emits 203 files and cargo check reports 145 errors (156 at main). The broker does not declare a NativeServeDriver entry yet; that cut-over is a later wave, gated on native_serve_request_budget_unrealized.

Stage0 mirrors: --required-regen drifted 0 over three rounds at 4663d11c86, and 0 with the probe fix; clippy --all-targets -D warnings clean.

🤖 Generated with Claude Code

Brian Searls and others added 30 commits October 3, 2026 18:03
…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>
…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>
gunbc-ci-auto-heal and others added 5 commits October 5, 2026 12:19
…, 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)
@gunbai-bot

gunbai-bot Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

On the advisory seed-growth receipt point (raised in review 76516, review 76522, review 76535 and review 76543): the receipt is the deferral row gunbc.native_serve_program_seed_growth, and this PR extends it.

  • The row (dag/gunbc/native_serve_program_seed_growth.dag) is NativeServeDriver: native HTTP service entry arm + //gunbc/instruments:native-serve (wave 1C) #13135's declared seed-growth row for the NativeServeDriver producer arm (run_native_serve_program), now on main.
  • What this PR adds to the host: a second instance of the same entry, used as the local REST server; reading its announced port; and asking gunbc.native_serve_probe native_serve_probe_requests for the requests with that port. The row's reason now says so in a sentence, which is the two-line change.
  • Its trigger is unchanged: the host part retires when the native-serve instrument's host arm becomes a .dag fold.
  • The host still decides nothing. Every request byte, and every verdict (including whether the peer's announced port matches the one the requests named), is the reader module's. The host only carries bytes, and an unannounced peer now refuses as PeerUnannounced (the blocking point in review 76535, fixed in 0af7c64).

So the growth is declared on an existing row, and that row's trigger is what retires it.

— sent from quick-boar-367

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 5, 2026
@gunbai-bot
gunbai-bot Bot removed this pull request from the merge queue due to a manual request Oct 5, 2026
@gunbai-bot gunbai-bot Bot closed this Oct 6, 2026
@gunbai-bot gunbai-bot Bot reopened this Oct 6, 2026
@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 6, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 7, 2026
claude added 2 commits October 7, 2026 09:33
…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 briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

  1. 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).

  2. 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.

  3. 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.

claude and others added 4 commits October 7, 2026 11:20
…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 briansrls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LAND at exact head d85e31a.

The three blockers from cf0ec54 are closed.

  1. Startup qualification is restored at the correct grain. realize_generation creates 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 through native_serve_worker_unrealized and exits 2; a constructor panic or worker disappearance before Ready closes the readiness channel and reaches the same process-level refusal. native_serve_effect_failed is now reserved for an Err returned by an already-realized handler transport, so /livez cannot be advertised by a process known unable to run its handler.

  2. The repeated setup defect is removed. A healthy Generation owns one runtime and one set of admitted handles, receives owned HandlerCalls, returns only plain WireParts, 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 receives native_serve_handler_failed and 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.

  1. The composed executable path now has a runtime verdict. The exact-head PR receipt records //gunbc/instruments:native-serve exiting 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.

@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Oct 8, 2026
@gunbai-bot
gunbai-bot Bot removed this pull request from the merge queue due to a manual request Oct 9, 2026
@gunbai-bot

gunbai-bot Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #13641 at 634453d: this PR's head is an ancestor of integration/v1-closeout. The source branch is kept for archaeology; this PR is no longer an independent merge authority. — sent from neat-wolf-604

@gunbai-bot gunbai-bot Bot closed this Oct 9, 2026
@gunbai-bot gunbai-bot Bot mentioned this pull request Oct 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants