Repository navigation
Conversation
Round 50 — Fastly SDK correctness pass (commit
|
Round 51 — round-50 carry-overs + Spin WASI write mechanics (commit
|
…ladder, smoke tests F3 (Cloudflare timeout did not cancel the fetch): dropping the raced future does NOT abort the subrequest -- the Workers runtime keeps the POST going after EdgeZero returns 504. Verified worker 0.8.3 exposes AbortController + Fetch::send_with_signal(&signal). §4.2 now issues via send_with_signal and calls controller.abort() on expiry; §8 risk 3 marked resolved. F8 (Spin map_spin_send_err was referenced but unspecified): added the WASI ErrorCode classification mirroring Fastly's SendFailure -- DnsTimeout/ConnectionTimeout/ ConnectionReadTimeout -> 504 (must NOT fall through to 502), transport -> 502, local-invalid -> internal, `_` -> 502. F4 (only Axum had a 502/504 response-synthesis mapping): specified that Fastly and Spin buffered-drain converters also synthesize the platform response from err.status() + into_response(), not a stringly platform error; §5.4 tests 502/504/over-cap/500 per adapter. F5 (Fastly Tier 2 had no transport seam): the recording builder + clock can't script PendingRequest::poll/wait or connect/header hangs. Added a `test-utils` FastlyDispatch trait so tests script the exchange; rows that need real TCP/TLS timing are Tier 3 only. F10 (capability model couldn't express "exact deadlines required"): defined the support-level ladder -- `required` is satisfied by Native OR BoundedCooperative, hard- fails on BestEffort/Unsupported; exact-only need is a deferred outbound-deadlines-exact capability (§8 risk 14), not an ad-hoc "declare exactness". Low: required/optional capabilities validated unique + disjoint (a from_baked_json -> Malformed / config-validate failure); added public smoke tests for Deadline::remaining() /is_expired() using after(ZERO) (no checked_sub underflow on a fresh WASM Instant).
…nown_fields, Spin timeouts H2 (my regression, compile error): OutboundHttpClient: Send + Sync (the handle stores Arc<dyn ..> in http::Extensions, which require Sync), but last round I "corrected" the Fastly cache to a RefCell field -- RefCell is !Sync, so it cannot compile. The ORIGINAL Mutex<HashMap> field was correct. Reverted to a per-session Mutex<HashMap> field on the per-request client: uncontended on the single-threaded WASM guest (satisfies Sync, does not serialize), fresh each request. This is now the third and final correction of a cache model I kept getting wrong from one false premise (names persist across requests) -- the verified truth is session-scoped names + a per-session Mutex field. H3: deleted the stale cross-request/process-scoped cache block (3896) that still contradicted the authoritative per-session section, plus the RefCell heading and the "no Mutex needed" prose. H7 (Spin timeout misclassification): verified wasip3-0.4.0 has FIVE http timeout ErrorCode variants -- DnsTimeout, ConnectionTimeout, ConnectionReadTimeout, ConnectionWriteTimeout, HttpResponseTimeout. map_spin_send_err mapped only three, leaking ConnectionWriteTimeout + HttpResponseTimeout to 502. All five -> 504, with an exhaustive classifier test. H1 (capability declarations fail open): ManifestCapabilities/ManifestOutboundCapability lacked #[serde(deny_unknown_fields)], so a typo (require/host) is silently ignored -> enforcement disabled or broad default invoked. Added deny_unknown_fields to both, and attached the validate_capabilities_disjoint schema validator (rejects duplicates and required∩optional overlap). §5.4 tests typo/duplicate/overlap. Low: purged the last stale "bucketed" wording (identity uses exact ceil-to-ms; the snapshot-vs-arm slack is the pre-existing guard, unrelated to bucketing).
…bility non_exhaustive) H4 (Axum decompression): reqwest's auto-decode matches exact-lowercase content-encoding from one map entry -- it cannot honour the portable contract (case-insensitive GZIP, identity/unknown/stacked/repeated passthrough). Axum now does NOT enable reqwest gzip/brotli; it routes through the SAME shared §3.4.1 decoder as the other adapters, so all four share one decompression contract. H5 (Cloudflare streamed-response expiry did not cancel the fetch): the buffered path aborts via AbortController, but the streamed wrapper only emitted an error chunk. The AbortController now moves INTO the body wrapper and aborts on deadline, chunk-read failure, or early consumer drop -- so a timed-out stream cancels the subrequest, not just stops reading. H6 (Spin buffered uploads were not cancellable): spin_sdk::http::send spawns a detached uncancellable pump even for a buffered Body::Once (which can block on host backpressure after the send future is dropped). ALL Spin uploads -- buffered and streamed -- now use the hand-built wasi:http request + owned in-race pump, so both body kinds are genuinely cancellable and streamed-upload-deadlines stays Native. Mediums: Capability/CapabilitySupport enums -> #[non_exhaustive] and Adapter::capability gets a default returning Unsupported (out-of-tree adapters compiled against older core still build; unknown capability fails closed). Optional capabilities logged at BestEffort AND Unsupported (not just Unsupported). Axum response converter drains async on the Tokio reactor (NOT block_on inside a worker), with a timer-backed-stream test. Tier 2 seam- dependent rows are required (the seam is a deliverable), not waived. CF redirect uses RequestRedirect::Manual (enum, not "manual" string). Spin wasip3 setters use .map_err (they return Result<(),()>, bare ? won't compile). Provision host-data: locked option (a) (add hosts to the provision context). Plan Task 3 renamed/scoped to the five core gates (Phase 1a is additive core-only).
Review-round fixes: - Rename BudgetCause -> BudgetSource and mark #[non_exhaustive]; document that it is the budget source (tightest bound), not the physical phase-timer fired - Exclude `connection` from the generic non-UTF-8 header drop so response passthrough normalization stays fail-closed (no nominated-header leak) - Carry req_method on the Fastly PendingSlot so a HEAD reply with representation Content-Encoding/Content-Length is passed through, not decoded to a false 502 - Apply Spin deadline-wins precedence to both the success and failure arms - Scope the CF repeated-header guarantee to requests + response set-cookie - Qualify the wire Content-Length cap rejection to identity/undecoded responses - Cross-reference the Fastly streamed-upload drop from the cancellation table - Correct the Phase-1a exhaustive-match inventory (9 matches: 5 impl + 4 test) Spec conventions: - Remove the extracted review-history file and all appendix cross-reference danglers; fix two truncated references and a duplicated sentence - Redate the inbound-body design spec to its creation date (2026-08-22)
Cover callback exits and response terminal paths across adapters. Share bounded fixture protocol validation, correct causal cap timing, and bind four complete core acceptance legs. Preserve outbound contracts and explicitly open qualification gates.
Prove connect-setting carriage, encoded ranges, facade upload rules and marker inheritance through existing private clients. Bind seven complete core acceptance legs without changing runtime behavior, frozen requirements or provider qualification.
Exercise native response policy and stream ownership without changing outbound contracts. Bind fourteen verified local acceptance legs through grouped CI gates. Preserve original timing boundaries and leave unproved Spin host-wire and hosted qualification work explicit.
Exercise independent decoded, encoded and buffered limits through the real Spin wire path while retaining the redirect inventory and local-only qualification scope. Recognize interrupted Fastly test records without weakening sentinel checks, and drain bounded recovery request bodies in the controlled startup responder.
Summary
Implements the portable outbound HTTP and request-lifecycle contracts across core and the Axum, Cloudflare, Fastly, and Spin adapters.
start_batch_until, indexednext, explicitcancel, orderedcollect, and thesend_all_untilconvenience path.start_batch_untiltakes an observation cutoff: reaching it stops observation even when individual slots retain budget, while earlier cutoffs remain a supported partial-result operation.PlatformMetadatafacts for memory ceilings, live inbound request population, and host-ingress accounting, preserving source and explicit unknown reasons.Fits,Exceeds, and typedIndeterminateoutcomes; publishes canonical Axum, Cloudflare, Fastly, generic Spin, and Akamai Functions profiles.CompletedorCutoff. OnlyCutoffpermits unresolved slots in a successful result. Explicit adapter failure, premature driver EOF, and duplicate or out-of-range indices returnOutboundBatchFailure, preserving the precise error and every terminal slot collected before failure.MonotonicClock::deadline_afteranchors relative cutoffs to that clock. Equality expiry wins and backwards time fails closed.ResponseEgressCompletion, transferred directly throughPreparedIngress,ResponseEgressEnvelope, and the soleResponseEgressAttempt. Terminal state is stored before the response-scoped completion and global observer run, each exactly once and behind independent panic boundaries. No response-extension carrier or envelope extraction bypass remains.ResponseEgressCompletion::joincomposition so independent response-owned resources share one exactly-once terminal report, and hard-cuts detached normalized errors toSend { completion, deadline } | Abortwith request-start-relative bounded deadlines.AdmissionDecision::Abort. Axum closes the owned HTTP/1 connection before body polling; Cloudflare, Fastly, and Spin return a fixed provider error at their strongest available pre-response boundary and remain conservatively classifiedBestEffort.Hooks::configureplus core-ownedApp::build::<A>(platform). Successful hooks must preserve the complete adapter-selected metadata; core rejects changed facts instead of restoring them. Typed configuration failures propagate unchanged and stop serving before request handling. The generated scaffold keeps finite ingress policy with core-owned empty lifecycle tokens; the checked-in demo retains the full application-owned resource-composition example.AbortController, Fastly's low-levelstream_to_client, and Spin's WASI response/body/result writers.Current Checkpoint: Lifecycle Phase Evidence
Checkpoint:
96fb2030. The implementation tracker records retained paths, diagnostic limitations and unchanged qualification gates. The prior measurement-repair checkpoint remains historical evidence. Hosted checks for this push are pending. Earlier counts below are historical.Earlier Checkpoint: Registration Observations And Connect Capabilities
outbound-connect-timeout: Fastly declares independent provider-backed support as Native; Axum, Cloudflare and Spin retain BestEffort acceptance. Fastly's split-free flexible phase budgeting is Native. These declarations do not qualify hosted connect precision, DNS/TLS coverage, blocking-call preemption, deadlines or cancellation.Checkpoint:
2cbbc549, following registration observations at00a9edbd. The implementation tracker records exact scope, evidence and remaining gates. This checkpoint is historical; current readiness and check status are recorded above.Earlier Checkpoint: Response-Owned Outbound Handoff
Checkpoint:
abbc2c11. Detailed local artifacts and failures are recorded in the D6 response-handoff tracker checkpoint.Earlier Checkpoint: Core Mixed-Observation Acceptance Binding
EZ-6/acceptance/22@unit/core, replacing its placeholder with five executable receipt/accumulator proofs. Run six exact three-slot cases: fresh and mixed check/next/collect under Completed, Cutoff and an original typed driver failure. Stage terminal production after both mixed receipts transfer.--lib outbound::; discover all 56 exact sentinels, including the original 51 redirect proofs. No new job or per-test CI list. The old redirect-only selection fails before execution because it omits the five batch proofs. Use the shared raw Cargo log path without relabelling older evidence.Checkpoint:
279c5a93.Earlier Checkpoint: Buffered Cap During Batch Progress
caps-while-waitinglifetime case through the production clients: 128 KiB decoded gzip trickling over 300 ms, independent 1 MiB decoded / 64 KiB buffered / 256 KiB encoded limits, healthy B/C siblings, and the original 34/534 ms batch timeline.Checkpoint:
8c11307d. Final local artifacts and individual failed timing preconditions are recorded in the D6 buffered-cap tracker checkpoint.Earlier Checkpoint: Reviewed Spin SDK Integration
6c59ed5b4fcb377072763aee0b27a3abdd8edb72(WASIp3 0.10.0 / wit-bindgen 0.63.0). Align the root, demo, reusable/request-fidelity fixtures, generator and all four application lockfiles.Checkpoint:
87b5e1ce. These are local regression results, not a frozen candidate or hosted qualification. D6 closure, native body-entry evidence, consumer acceptance and hosted qualification remain open. The acceptance inventory remains 32 ready / 479 planned and unqualified.Earlier Checkpoint: Fourth-Slot Per-Call Expiry
Implemented in cd8e77e4, within the approved D6 acceptance work:
Earlier Checkpoint: Readiness-Driven Test Origins
Implemented in d3f48564, under the requester's scoped fixture-accept approval:
CI diagnostic follow-up 11b8f2c4 adds underlying errors to two existing request-fidelity version-test assertions. The prior Linux workspace failure occurred before the collector tests; its OS/process cause was not exposed and remains unproven. The fresh run passes both the request-fidelity step and the complete shared-harness tests on Linux. No test conditions, retries, version policy or subprocess behavior changed; other CI checks and provider qualification remain separate.
Earlier Checkpoint: Same-Client CPU Calibration
Implemented in 0d09236a, under the requester's scoped calibration approval:
Earlier Checkpoint: CPU Stretch Characterization
Implemented in 44baaf3b:
Earlier Checkpoint: Pending Cutoff Lifetime Probes
Implemented in 73ed39ba, following the main integration:
host-head-written-only, not proof that the SDK consumed headers or entered a body read. Full criterion 16, remaining D6 work and hosted qualification stay open. Production outbound code, signed SDK/decoder pins, frozen requirements/interfaces and capability levels are unchanged; inventory remains 32 ready/479 planned and unqualified.Earlier Checkpoint: Coordinated Spin SDK Executor Upgrade
Implemented in 7b68f737:
e65ff90dadf4173b08b14194e878ad3acd555337; its DCO check passes. Upstream Rust 1.94 CI remains separate from local Rust 1.98.1 verification.spawn_local. Root, demo, reusable fixture and generator use identical Git URL/revision and package version; guards compare all three. Lock changes stay within the affected SDK/bindings/macro graph. Decoder/map contracts remain unchanged.qualified: false; full acceptance binding, candidate freeze and authorized hosted qualification remain open.Earlier Checkpoint: Begin/Batch Lifetime Probes and SDK Gap
Implemented in 834a009e:
wit-bindgen 0.57.1.qualified: false. CPU-stretch, remaining lifetime/registration/consumer acceptance, candidate freeze and authorized hosted qualification remain open.This is a verified test/diagnostic checkpoint, not a passing full lifetime suite or completed feature qualification.
Earlier Checkpoint: Standalone Test Harness and Redirect Bindings
Implemented in b5f94c70:
crates/edgezero-test-harness, with a non-published standalone workspace and its own lockfile. Remove the old runner package/directory without an alias or duplicate implementation. Keep deployable fixture apps separate and unit/contract tests in their owning crates.qualified: false. Readiness records executable assertions, not consumer adoption, applicability approval or exact-candidate qualification.This completes the approved runner relocation and local redirect binding checkpoint, not the whole adoption plan. Lifetime/registration acceptance, successful native optimizer transformation/redirect/performance evidence, consumer integration, candidate freeze and authorized hosted qualification remain open. Optional idle-body policy is not silently adopted. No deployment, capability promotion or production outbound behavior change occurs.
Earlier Checkpoint: Local Workerd Redirect Acceptance
Implemented in 4e64db99:
outbound-wirematrix and bounded two-origin servers as Spin. A fixture-only route obtains the production client from the canonical request context; no Fetch replacement, parallel outbound transport or new host-language runner is used.Successful native optimizer redirects, conditional idle-policy coverage, complete criterion-level binding and consumer/hosted acceptance remain open. The inventory remains eleven ready legs, 500 planned and
qualified: false.Earlier Checkpoint: Rust Spin Wire Harness
Implemented in 361da4f1:
--adapter spin --suite outbound-wirein the existing Rust fixture harness. No Python or Node replacement runner, compatibility wrapper, dependency changes or second process supervisor remains.This checkpoint is test-tooling only. No hosted deployment, capability promotion, candidate freeze or optimizer performance claim is made. The inventory remains eleven ready legs, 500 planned and
qualified: false.Earlier Checkpoint: Provider Redirect Boundaries
Implemented in 699cd49c:
Workerd/origin parity, successful optimized-first-hop redirects, conditional idle-policy coverage, complete criterion-level acceptance binding, candidate freeze and consumer/hosted qualification remain open. Native optimizer dispatch is preserved; unavailable-boundary controls do not establish transformation or performance. No deployment is performed. The inventory remains eleven ready legs, 500 planned and
qualified: false.Earlier Checkpoint: Fastly Cooperative-Wake CI Repair
Implemented in f57b8130:
qualification: false.Still open: provider-runtime redirect acceptance, exact-candidate criterion bindings, resource-policy decisions and consumer/hosted qualification. All previous outbound guarantees and direct native Image Optimizer integration remain intact.
Earlier Checkpoint: Explicit Redirect Following
Implemented in e02c2360:
HttpClient::followAPI with a required synchronous application policy and validated 1..=20 redirect limit. Plain Send, Begin and Batch remain unfollowed.Still open: other provider-runtime Follow parity, actual Fastly optimizer-hop mapping, consumer composition, exact-candidate criterion bindings, resource-policy decisions and hosted qualification. The inventory remains eleven ready legs, 500 planned and
qualified: false. No deployment or capability promotion occurred, and the PR remains draft. Current tracker.Earlier Checkpoint: Shared Outbound Pipeline and Ownership
Implemented in 2be1dd87:
Open at that checkpoint: explicit same-client redirect following (now implemented in the checkpoint above), remaining exact-candidate acceptance bindings, resource-policy decisions and hosted qualification. Local runtime evidence does not promote capabilities or prove end-client delivery. The PR remains draft. See the current tracker.
Earlier Checkpoint: Live Paired Time
Implemented in d86d4c0d, with the isolated Cloudflare test-ordering correction in f4d660b1:
with_time/ Axumtry_with_time, removing clock-only constructors without aliases.OutboundTimefor live asynchronous deadline handling. Check timers before source polling, preserve operational errors and diagnostics, retain clock high-water observations across chunks/yields, and release terminal/cancelled ownership.This completes only the approved sequencing checkpoint. The public head-first trait, settlement/context issuance, batch receipts, eager Begin, redirect integration and hosted qualification remain open. Requirements and frozen Plan 02 hashes are unchanged. See the verification audit.
Earlier Checkpoint: Bounded Synthesized Headers
Added in
d47c8026:This checkpoint is not completion of the broader adoption plan or hosted qualification. The full head-first/eager/redirect migration, Fastly's failed G2 preserving-read proof, all nine failed Fastly framing SDK parity controls, Cloudflare overflow recovery, consumer integration and exact-candidate hosted qualification remain open. No capability is promoted, no compatibility layer is restored, and the PR remains draft.
Reviewer correctness follow-up
Addressed R1–R4 in
92111839:Main merge and shared-policy follow-up
Addressed R5 and R6 in
91f4af38, merging main683202c6without restoring superseded APIs:OutboundBatch::from_futures; adapters retain preflight validation, exchange ownership, clocks, and terminal samples. Ready results remain retained, driver EOF/index invariants remain enforced, and cutoff releases pending futures before reporting termination. Fastly retains its distinct provider-handle driver.ResponseBodyPolicyandapply_response_body_policy, preserving declared-length checks, encoded limits, decoding/header rewriting, decoded limits, passthrough, and rechunk order. Transport reads, bodyless handling, deadlines, cancellation, and final buffering remain adapter-owned.Result<(), E>through a wrapper that prevents SDK-generated second responses.R7's dependency-audit-script deduplication remains deferred; dependency contract checks remain unchanged and pass.
Bounded native ingress and config follow-up
ConfigValuereturns. Axum constructors requireConfigStoreLimits;from_mapis fallible. Reads share payloads, and local config push updates require a server restart.serde_json 1.0.150, hash canonical JSON incrementally, and release extraction's raw handle before secret retrieval. A moving-reallocation malformed-root probe measured 36,428 requested peak bytes under a 100,060-byte staging cap, with zero retained bytes after rejection.Unsupportedand response egress remainsBestEffort. Local emulators and guest contract tests are not deployed runtime evidence.Bounded provider reads and lifecycle qualification infrastructure
FinalizationRegistrycleanup support. Generated and demo Workers explicitly enableenable_weak_ref, and regression tests inspect their TOML rather than assuming an exact one-flag string.try_opennow returns typedConfigStoreError.Downstream acceptance impact
Cloudflare
outbound-deadlinesandstreamed-upload-deadlinesareBestEffort, notNative. Downstream acceptance suites must use best-effort deadline wording and must not claim exact cancellation enforcement until deployed host-observed evidence exists.The response completion, ingress abort, and fallible application-assembly changes are intentional breaking changes. Consumers must provide a completion on every response-producing admission decision and propagate
App::build::<A>(platform)failures.Evidence boundaries
Capability declarations remain conservative where provider behavior is not proved:
Unsupportedon all adapters.Nativeon Axum andBestEfforton Cloudflare, Fastly, and Spin because finite host-side teardown is not proved or synchronously preemptible.Nativeon Axum's owned HTTP/1 connection andBestEfforton Cloudflare, Fastly, and Spin because provider transport reset/close is not directly observable.BestEfforton every adapter because no boundary proves end-client receipt; provider-specific blocking and teardown limits are documented.Unsupported; provider parsing/materialization, SDK copies, informational responses, trailers, allocator overhead, and other host-owned memory remain outside guest-visible caps.BestEfforton every adapter because provider operations are not proved cancellable within a finite wall-clock interval.BestEffortpending a deployed host-observed cancellation artifact. Fastly and Spin retain their separately documented deadline and isolation limitations. Fastly declares split-free phase budgeting Native; its independent connect setting remains unqualified for hosted timing precision.Validation
This checklist records validation across PR checkpoints. The exact latest reruns and remaining acceptance failures are listed above; it is not a claim of hosted qualification or a fresh run of every historical matrix.
scripts/run_tests.shcargo fmt --all -- --checkcargo clippy --workspace --all-targets --all-features -- -D warningscargo test --workspace --all-targetscargo check --workspace --all-targets --features "fastly cloudflare spin"Closes
Closes #436.