Honor EdgeZero app config store default - #879
Conversation
prk-Jr
left a comment
There was a problem hiding this comment.
Summary
Replacing the Fastly hardcode with a manifest-derived constant is the right direction — Spin already resolved its config store through ctx.config_store_default() (adapter-spin/src/platform.rs:726), so Fastly's literal was the last one holding out, and the Cloudflare key alignment keeps a legacy fallback rather than breaking existing Workers. Read all 13 changed files.
Two blocking items. One is the red cargo fmt job, a one-line import reorder. The other is the documentation paragraph introducing the EDGEZERO__STORES__CONFIG__<ID>__KEY override: ts config push does not honor that variable, so following the instruction as written puts the blob at one key and the runtime read at another, and the service fails closed at startup. I confirmed both halves with dry-run probes against this branch's own CLI (details inline). The rest is non-blocking — mostly a question about whether the new build script earns its coupling, given the test added alongside it already catches the same drift.
Blocking
🔧 wrench
cargo fmtfails, CI red: import ordering (crates/trusted-server-core/src/settings_data.rs:6). Only formatting diff in the workspace.- The newly documented
…__KEYoverride fails the deploy closed: the runtime reads it,ts config pushignores it, so the push and the read disagree on the blob key (docs/guide/configuration.md:1473-1476). Probe output and theedgezero-clicall site are in the inline comment.
Non-blocking
🤔 thinking
- Does the build script earn its coupling?
config_defaults_match_edgezero_manifestalready catches manifest drift, while the build script adds a host build ofedgezero-coreand its transitive deps, a hard repo-layout dependency on the library crate, and full-manifest validation as a build gate — so an error in an unrelated[adapters.spin]block now breaks the build of every adapter (crates/trusted-server-core/build.rs:14). - Cloudflare gets a legacy fallback; Fastly and Axum do not: undocumented asymmetry, and
LEGACY_CONFIG_BLOB_KEYhas no deprecation note or removal condition (crates/trusted-server-adapter-cloudflare/src/app.rs:106). - A Cloudflare startup error can name a key the operator never set: manifest key absent plus a non-string at
app_configreports "missing string value attrusted_server_config" (crates/trusted-server-adapter-cloudflare/src/app.rs:97).
♻️ refactor
CONFIG_BLOB_KEYnow fills five roles and is named for one: it is derived from a logical store id, then used as env-override lookup id, platform store name, blob key, Cloudflare JSON object key, and Viceroy store name and key. Suggest aDEFAULT_CONFIG_STORE_IDconst withCONFIG_BLOB_KEYdefined from it (crates/trusted-server-core/src/config_payload.rs:15).- Build-script panics omit the path they looked at (
crates/trusted-server-core/build.rs:15).
⛏ nitpick
- Comment line overruns the block's wrap width (
edgezero.toml:19).
📝 note
- Base is two commits behind
origin/main(d744b764vsf6a2fb85) — worth a refresh before merge so CI runs against current main.
👍 praise
config_defaults_match_edgezero_manifestasserts both directions, so an accidentaledgezero.tomledit fails a test instead of silently repointing every adapter (crates/trusted-server-core/src/settings_data.rs:248).cloudflare_config_does_not_mask_malformed_manifest_valuepins the one behavior anor_elsechain would have gotten wrong (crates/trusted-server-adapter-cloudflare/src/app.rs:646).- The docs correct a claim that was wrong: the config store is not optional-and-possibly-empty; an absent entry fails startup closed. Both new CLI invocations check out against
edgezero-cliv0.0.4's argument definitions (docs/guide/configuration.md:1479).
CI Status
- fmt: FAIL (
crates/trusted-server-core/src/settings_data.rs:6) - clippy (fastly / axum / cloudflare native + wasm / spin native + wasm): PASS
- rust tests (fastly, axum, cloudflare, spin, cross-adapter parity, ts CLI): PASS
- js tests (vitest): PASS
- browser + integration + Fastly EC lifecycle: PASS
- CodeQL: PASS
5649ed0 to
2760e42
Compare
prk-Jr
left a comment
There was a problem hiding this comment.
Summary
Re-review of 2760e429 against 044cd7e9. All eight findings from the previous round are answered: the cargo fmt regression is gone (verified locally, exit 0, matching the now-green CI job), the build script's load failure carries the attempted path, DEFAULT_CONFIG_STORE_ID is split from CONFIG_BLOB_KEY and threaded through the store-identity call sites, the Cloudflare fallback gained a typed error plus three new tests, and the manifest comment is rewrapped.
One documentation change introduced in the fix commit reproduces the failure mode the previous round flagged, this time as a copy-pasteable command, and one sentence next to it conflicts with the Fastly deployment mechanism documented elsewhere in this repo. Everything else is non-blocking.
Blocking
🔧 wrench
--keyexample sets the runtime override on the push process:ts config pushnever readsstore_key(verified in the pinned CLI atedgezero-cli/src/config.rs:435), so the env prefix is inert and the runtime never sees it — the operator lands in the fail-closed startup described three lines below (docs/guide/configuration.md:1480-1483)
❓ question
- Does the Fastly runtime honor
EDGEZERO__STORES__CONFIG__<ID>__NAME?:.claude/skills/deploying-trusted-server-to-fastly/SKILL.md:28-34documentsfastly resource-linkas the runtime mapping instead, which would makeNAMEpush-side only on this adapter (docs/guide/configuration.md:1473-1476)
Non-blocking
♻️ refactor
- Missing
[stores.config]still panics without the path: the load-failure path was fixed, the section-missing path one line later was not (crates/trusted-server-core/build.rs:32) normalize_env_segmenthand-rolled in the integration harness: duplicatesadapter-axum/src/platform.rs:24, doubled by this commit, and already diverges onto_ascii_uppercase()vsto_uppercase()(crates/trusted-server-integration-tests/tests/environments/axum.rs:37-44)- Dead match guard: when the two keys are equal, the guarded arm and the final arm produce the same
Missing(crates/trusted-server-adapter-cloudflare/src/app.rs:149)
🤔 thinking
KEYoverride is adapter-dependent: Fastly and Axum resolve throughdefault_config_key(); Cloudflare reads the constant directly, so the override does not reach it (crates/trusted-server-core/src/config_payload.rs:20)
⛏ nitpick
- Comment between
#[cfg]and item: belongs above the attribute, and reads better as///(crates/trusted-server-adapter-cloudflare/src/app.rs:78-82)
👍 praise
- Typed Cloudflare envelope error: names the key that is actually malformed rather than the one the operator was expected to set, and the malformed-legacy test asserts the rendered message, not just the variant (
crates/trusted-server-adapter-cloudflare/src/app.rs:84-110) - Formatting regression fixed:
cargo fmt --all -- --checkis clean locally
CI Status
- fmt: PASS (also verified locally)
- clippy (cloudflare native + wasm32-unknown-unknown, spin native + wasm32-wasip1): PASS
- rust tests: PASS (
cargo test, axum native, cloudflare, spin, cross-adapter parity, ts CLI native) - integration tests: PASS (Fastly EC lifecycle, browser, general)
- js tests: PASS (vitest)
- format-typescript / format-docs: PASS
Analyze (javascript-typescript): FAIL — GitHub infrastructure, not code. The CodeQLinitaction died withNo server is currently available to service your requestbefore producing any artifacts.Analyze (rust)andAnalyze (actions)passed on the same commit.
2760e42 to
391fbb5
Compare
prk-Jr
left a comment
There was a problem hiding this comment.
Summary
Third pass on this branch. All eight findings from the previous round check out as addressed in 391fbb52: the runtime KEY example is gone from the Fastly guide, the resource-link runtime model is documented, a missing [stores.config] now emits cargo::error with the resolved manifest path (reproduced locally by corrupting the manifest), the axum harness calls config_env_var, the dead equality guard is gone, and the legacy-key rationale is a /// doc above the #[cfg]. The Rust side of this PR is clean; what is left is one operator-facing gap in the new Fastly documentation plus a scope note on where the EDGEZERO__* overrides can actually be read.
2 of the inline comments below carry a one-click GitHub
suggestion— use Commit suggestion (or Add suggestion to batch for both) to apply them as commits on this branch. Both were verified in a scratch worktree at this head:cargo fmt --all -- --check,cargo check --testsandcargo clippy --all-targets -- -D warningson the integration-tests manifest, and the pinneddocs/Prettier, with pre/post-verification patch snapshots byte-identical. The remaining comments describe the change in prose.
Blocking
🔧 wrench
fastly resource-link createsequence cannot run on a deployed service, and never takes effect — see inline atdocs/guide/configuration.md:1481-1484
Non-blocking
🤔 thinking
EDGEZERO__…__NAME/__KEYare inert on Fastly, and the new test reads as a guarantee that they are not — see inline atcrates/trusted-server-core/src/settings_data.rs:32- This section now forks the deploy skill's multi-service guidance, minus its guardrails — see inline at
docs/guide/configuration.md:1487-1490
♻️ refactor
- Hand-built
Mapwhere the same PR usesjson!with a const key — see inline atcrates/trusted-server-integration-tests/tests/common/config.rs:39-43
👍 praise
main.rsandapp.rsnow resolve the same store name — see inline atcrates/trusted-server-adapter-fastly/src/main.rs:52- Override test injects
EnvConfiginstead of mutating global env — see inline atcrates/trusted-server-core/src/settings_data.rs:258
Cross-cutting / body-level findings
- 📌 Last shipped
app_configreference points the JWKS store at the app-config store —trusted-server.example.toml:38andcrates/trusted-server-integration-tests/fixtures/configs/trusted-server.integration.toml:31both set[request_signing].config_store_id = "app_config". That field is the Fastly Config Store ID used for JWKS (docs/guide/configuration.md:516documents it as such, anddocs/guide/request-signing.md:219usesjwks_store), so the value is wrong on two counts: wrong store, and a store name where an ID belongs. Inert today because[request_signing].enabled = falsein both files, and genuinely outside this PR's scope — but after this sweep it is the onlyapp_configleft in shipped configuration, so it is now the one place an operator could still read the old name as current. Worth a follow-up issue rather than a change here.
CI Status
- browser integration tests: PASS
- integration tests: PASS
- integration tests (Fastly EC lifecycle): PASS
- prepare integration artifacts: PASS
- CodeQL: PASS
- Analyze (rust): PASS
- Analyze (actions): PASS
- Analyze (javascript-typescript): PASS
- cargo fmt: PASS (required)
- cargo test: PASS (required)
- cargo test (axum native): PASS
- cargo test (ts CLI, native): PASS
- cargo test (cross-adapter parity): PASS
- cargo check (cloudflare native + wasm32-unknown-unknown): PASS
- cargo check/build/test (spin native + wasm32-wasip1): PASS
- vitest: PASS
- format-docs: PASS (required)
- format-typescript: PASS (required)
prk-Jr
left a comment
There was a problem hiding this comment.
Summary
Fourth pass. All four findings from the previous round check out as addressed in 953f0df2: fastly resource-link create now carries --autoclone and is followed by service-version activate, the hand-built Map in the integration harness became json! with the const key, and both default_config_store_name / default_config_key carry a /// note that the process-env path is native-adapter-only. The Rust side of this PR reads clean and its test coverage pins the manifest-to-constant relationship in the right place. What is left is one operator-facing correctness bug in the new Fastly deployment sequence, plus a merge conflict against current main.
1 of the inline comments below carries a one-click GitHub
suggestion— use Commit suggestion to apply it as a commit on this branch. It was verified in a scratch worktree at this head against the pinneddocs/Prettier, with pre/post-verification patch snapshots byte-identical. The remaining comments describe the change in prose.
Blocking
🔧 wrench
- Shared-account first-deployment path links the store under the physical name, not the logical one — see inline at
docs/guide/configuration.md:1503-1519 - Merge conflict against
main— see the cross-cutting section below
Non-blocking
🤔 thinking
__KEYoverride is honoured on read but never on write — see inline atcrates/trusted-server-core/src/settings_data.rs:46
♻️ refactor
- Hand-built
Value::Objecthelper where the same file usesjson!with a const key — see inline atcrates/trusted-server-adapter-cloudflare/src/app.rs:685 - Error type does not follow the
derive_more::Display+impl Errorconvention — see inline atcrates/trusted-server-adapter-cloudflare/src/app.rs:92
👍 praise
- Build script fails closed with the resolved manifest path — see inline at
crates/trusted-server-core/build.rs:20 - Manifest-to-constant alignment is pinned by a test — see inline at
crates/trusted-server-core/src/settings_data.rs:237
Cross-cutting / body-level findings
-
🔧 Merge conflict against
main—mergeStateStatusisDIRTY. One file conflicts:crates/trusted-server-adapter-cloudflare/src/app.rs, at the import block (lines 13-18 of the merged result), against42a34ae3(#860, configurable cache header policies) which landed onmainafter this branch's merge commit. The two sides add adjacentuselines and the resolution is keep-both:#[cfg(any(test, target_arch = "wasm32"))] use trusted_server_core::config_payload::CONFIG_BLOB_KEY; use trusted_server_core::cache_policy::EdgeCacheHeader;
Flagged at the body level because the conflict is not inside this PR's diff, so it has no inline anchor. Note that CI is green at
29cd794e, but that run predates42a34ae3; the checks have not seen the two changes combined.
CI Status
- integration tests: PASS
- browser integration tests: PASS
- integration tests (Fastly EC lifecycle): PASS
- prepare integration artifacts: PASS
- CodeQL: PASS
- Analyze (rust): PASS
- Analyze (actions): PASS
- Analyze (javascript-typescript): PASS
- cargo fmt: PASS (required)
- cargo test: PASS (required)
- cargo test (axum native): PASS
- cargo test (ts CLI, native): PASS
- cargo test (cross-adapter parity): PASS
- cargo check (cloudflare native + wasm32-unknown-unknown): PASS
- cargo check/build/test (spin native + wasm32-wasip1): PASS
- vitest: PASS
- format-docs: PASS (required)
- format-typescript: PASS (required)
29cd794 to
81adefc
Compare
prk-Jr
left a comment
There was a problem hiding this comment.
Summary
Small functional delta wrapped in a large docs rewrite, and the functional part is a genuine fix: the Fastly entry point previously opened a hardcoded trusted_server_config in main.rs while app.rs resolved the same store through default_config_store_name() — under a __NAME override those two would have opened different stores. The Cloudflare JSON key migration (app_config → the manifest default) is done carefully, with the legacy fallback firing only on absence of the primary key. CI is green across all 19 checks; cargo test -p trusted-server-core --lib settings_data and cargo clippy-cloudflare re-run clean locally against this head.
Approving. Nothing here blocks merge — the notes below are a redundancy question about the new build script plus a few doc/comment sharpenings.
3 of the inline comments below carry a one-click GitHub
suggestion— use Commit suggestion (or Add suggestion to batch for several at once) to apply them as commits on the PR branch. Each was applied to a scratch worktree at this head and verified withcargo fmt --all -- --check,cargo clippy-fastly,cargo doc -p trusted-server-core --no-deps, and the pinneddocsPrettier, individually and as a batch, with a byte-exact pre/post drift check.
Non-blocking
🤔 thinking / ♻️ refactor
CONFIG_BLOB_KEYanddefault_config_key()are not interchangeable — see inline atcrates/trusted-server-core/src/config_payload.rs:20- Doc comment understates why Fastly can't take overrides — see inline at
crates/trusted-server-core/src/settings_data.rs:30 - Local-dev step inherits the shared-account env export, and writes secrets to a tracked file — see inline at
docs/guide/configuration.md:1780
Cross-cutting / body-level findings
-
♻️ The build script is redundant with the alignment test it ships alongside —
config_defaults_match_edgezero_manifestalready fails if[stores.config].defaultdrifts from the compiled constant, and its third assertion re-pins the literal"trusted_server_config"anyway, so the manifest value still cannot change without editing Rust. Against that, the build script costs a hostedgezero-corebuild-dependency, a../..path escape out of the crate directory, and a new failure mode: every compile oftrusted-server-corenow hard-fails if the repo-rootedgezero.tomlis missing or trips a future EdgeZeroManifestLoader::validate()tightening. Previously a manifest-validation change could only breaktscommands; now it breakscargo buildfor the whole workspace. A plainconst DEFAULT_CONFIG_STORE_ID: &str = "trusted_server_config";with the new test as the gate gets the same guarantee for none of that. Not blocking — just worth a second look before the machinery settles in. -
🌱 Per-environment config overrides remain unreachable on Fastly. EdgeZero's own Fastly adapter reads
EDGEZERO__*from a config store namededgezero_runtime_env(created byts provision --adapter fastly, which even prints afastly config-store-entry update ... --key=EDGEZERO__STORES__CONFIG__..__KEYhint). Trusted Server's Fastly entry point is bespoke — it never callsedgezero_adapter_fastly::run_app— so that store is inert here and a staging-style__KEY=trusted_server_config_stagingcannot work by any route. Wiringedgezero_runtime_envintodefault_config_store_name()/default_config_key()would close it. Follow-up issue, not this PR. -
🌱 Stale
app_configliterals in the Cloudflare environment tests —crates/trusted-server-integration-tests/tests/environments/cloudflare.rs:206,210,221,232still hardcode{"app_config":"blob"}. They only exercise template injection so they still pass, but they now read as thoughapp_configis the current key.tests/common/config.rswas switched toCONFIG_BLOB_KEYin this PR; these could follow. (No inline comment — the file isn't in the diff.) -
👍 Two things worth calling out. First, unifying
main.rswithapp.rsondefault_config_store_name()closes a real latent split that only a__NAMEoverride would have exposed. Second,cloudflare_config_envelopefalls back to the legacy key only when the primary key is absent, never when it's present-but-malformed — andcloudflare_config_does_not_mask_malformed_manifest_valuepins exactly that. That's the right call and the easy thing to get wrong.
CI Status
- browser integration tests: PASS
- integration tests (Fastly EC lifecycle): PASS
- integration tests: PASS
- CodeQL: PASS
- Analyze (rust): PASS
- Analyze (actions): PASS
- Analyze (javascript-typescript): PASS
- cargo test (axum native): PASS
- cargo test (cross-adapter parity): PASS
- cargo test (ts CLI, native): PASS
- cargo test: PASS
- vitest: PASS
- format-typescript: PASS
- format-docs: PASS
- cargo fmt: PASS
- cargo check/build/test (spin native + wasm32-wasip1): PASS
- cargo check (cloudflare native + wasm32-unknown-unknown): PASS
- prepare integration artifacts: PASS
| /// Default config-store key containing the Trusted Server app-config blob. | ||
| pub const CONFIG_BLOB_KEY: &str = "trusted_server_config"; | ||
| pub const CONFIG_BLOB_KEY: &str = DEFAULT_CONFIG_STORE_ID; |
There was a problem hiding this comment.
🤔 thinking — CONFIG_BLOB_KEY is now an alias for the store id, but it is not interchangeable with default_config_key().
default_config_key() honours EDGEZERO__STORES__CONFIG__<ID>__KEY; this constant never does. The consumers are already split along that line — crates/trusted-server-adapter-fastly/src/app.rs and crates/trusted-server-adapter-axum/src/app.rs read through the env-aware accessor, while generate-viceroy-config.rs and tests/common/config.rs use the constant. That is safe today (the Fastly guest has no process environment, and the generator is host-side), but after this PR the two names read as synonyms and one silently ignores an override the other honours. A line of doc keeps the next reader out of that trap:
| /// Default config-store key containing the Trusted Server app-config blob. | |
| pub const CONFIG_BLOB_KEY: &str = "trusted_server_config"; | |
| pub const CONFIG_BLOB_KEY: &str = DEFAULT_CONFIG_STORE_ID; | |
| /// Default config-store key containing the Trusted Server app-config blob. | |
| /// | |
| /// This is the *un-overridden* default. Runtime readers that must honour | |
| /// `EDGEZERO__STORES__CONFIG__<ID>__KEY` call | |
| /// [`crate::settings_data::default_config_key`] instead. | |
| pub const CONFIG_BLOB_KEY: &str = DEFAULT_CONFIG_STORE_ID; |
(Verified in a scratch worktree at this head: cargo fmt --all -- --check, cargo clippy-fastly, and cargo doc -p trusted-server-core --no-deps all clean — the new intra-doc link resolves.)
| /// Returns the default `EdgeZero` app-config store name. | ||
| /// | ||
| /// Process-environment overrides apply to native adapters such as Axum. Fastly | ||
| /// has no process environment, so it uses the manifest default as the logical | ||
| /// name and resolves the physical store through a resource link. |
There was a problem hiding this comment.
🤔 thinking — "Fastly has no process environment" is true but leaves out the part that trips people up.
EdgeZero's own Fastly adapter does have an override path: env_config_from_runtime_dictionary reads EDGEZERO__STORES__CONFIG__<ID>__NAME / __KEY out of a config store literally named edgezero_runtime_env, and ts provision --adapter fastly creates that store and prints a fastly config-store-entry update ... --key=EDGEZERO__STORES__CONFIG__..__KEY hint on the way past. An operator who follows that hint will reasonably expect it to move this store name — and it will not, because the Trusted Server entry point is bespoke and never routes through edgezero_adapter_fastly::run_app. Saying so here is cheaper than the debugging session:
| /// Returns the default `EdgeZero` app-config store name. | |
| /// | |
| /// Process-environment overrides apply to native adapters such as Axum. Fastly | |
| /// has no process environment, so it uses the manifest default as the logical | |
| /// name and resolves the physical store through a resource link. | |
| /// Returns the default `EdgeZero` app-config store name. | |
| /// | |
| /// Process-environment overrides apply to native adapters such as Axum. Fastly | |
| /// has no process environment, so it uses the manifest default as the logical | |
| /// name and resolves the physical store through a resource link. `EdgeZero`'s | |
| /// own Fastly adapter reads `EDGEZERO__*` overrides from an | |
| /// `edgezero_runtime_env` config store; the Trusted Server entry point does not | |
| /// consult that store, so overrides placed there are ignored here. |
(Verified in a scratch worktree at this head: cargo fmt --all -- --check and cargo clippy-fastly clean.)
| **Local development** (writes the entry used by Viceroy in `fastly.toml`): | ||
|
|
||
| ```bash | ||
| # Create the store once and attach it to the service. | ||
| fastly config-store create --name trusted_server_config | ||
| ts config push --adapter fastly --local | ||
| ``` |
There was a problem hiding this comment.
🤔 thinking — Two footguns land on this one command, and the section above sets up the first of them.
--local writes to [local_server.config_stores.<physical-name>], where <physical-name> is the env-resolved store name — but the Viceroy guest opens the logical trusted_server_config. An operator who exported EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__NAME for the shared-account block a few lines up still has it in their shell here, so the blob lands in a block nothing reads and startup fails with a confusing missing-key error.
Second, this command writes the full app-config blob — proxy_secret, the EC passphrase, and the admin password among it — into fastly.toml, which is tracked. That is pre-existing behaviour (docs/guide/auction-orchestration.md:848 documents the same command without a warning), so one line here rather than a rework:
| **Local development** (writes the entry used by Viceroy in `fastly.toml`): | |
| ```bash | |
| # Create the store once and attach it to the service. | |
| fastly config-store create --name trusted_server_config | |
| ts config push --adapter fastly --local | |
| ``` | |
| **Local development** (writes the entry used by Viceroy in `fastly.toml`): | |
| Unset any `EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__NAME` override | |
| first: `--local` writes to `[local_server.config_stores.<physical-name>]`, while | |
| the Viceroy guest opens the logical `trusted_server_config` store. This command | |
| also writes the full app-config blob — including `proxy_secret`, the EC | |
| passphrase, and the admin password — into the tracked `fastly.toml`; do not | |
| commit the result. | |
| ```bash | |
| ts config push --adapter fastly --local | |
| ``` |
(Verified in a scratch worktree at this head: the pinned docs Prettier reports All matched files use Prettier code style!. The outer fence is widened to five backticks so the nested ```bash block does not close it early.)
Summary
[stores.config].defaultinedgezero.tomlat build time.trusted_server_configas the current default while retaining EdgeZero store name/key environment overrides.Changes
crates/trusted-server-core/build.rsedgezero.tomland expose its default config-store ID as a compile-time value.crates/trusted-server-core/src/config_payload.rs,settings_data.rscrates/trusted-server-adapter-fastly/src/main.rscrates/trusted-server-adapter-cloudflare/app_configkey.docs/guide/configuration.mdCloses
Closes #866
Test plan
cargo test-fastly && cargo test-axumcargo clippy-fastly && cargo clippy-axumcargo fmt --all -- --checkcd crates/trusted-server-js/lib && npx vitest runcd crates/trusted-server-js/lib && npm run formatcd docs && npm run formatcargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1fastly compute servecargo test-cloudflare;cargo clippy-cloudflare && cargo clippy-cloudflare-wasm; integration config generator and non-ignored integration tests; Spin, CLI, and parity testsChecklist
unwrap()in production code — useexpect("should ...")tracingmacros (notprintln!) — no runtime logging added; build-scriptprintln!calls emit required Cargo directives