You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Close PR #257 self-review followups: store id validation + docs lint
Closes the High blocker and five Low findings from the post-merge
self-review of the Spin 6 + strict-clippy integration.
High: Spin wasm contract CI was red.
Three contract tests (`config_store_reads_value_from_handler`,
`kv_store_reads_value_from_handler`, `secret_store_reads_value_
from_handler`) inserted bare `ConfigStoreHandle` / `KvHandle` /
`SecretHandle` into request extensions, then called
`app.router().oneshot(...)` -- bypassing the Spin dispatch
boundary. Under the hard-cutoff model the core
`RequestContext::config_store_default()` /
`kv_store_default()` / `secret_store_default()` extractors only
read `ConfigRegistry` / `KvRegistry` / `SecretRegistry`. The
dispatch boundary's `synthesise_store_registries` is what
normally wraps a bare handle into a one-id registry keyed under
`"default"`. Because the tests bypassed dispatch, handlers got
`None` and the assertions silently failed under
`wasmtime run` in CI.
Fix: the three tests now insert `*Registry::single_id("default"
.to_owned(), handle)` (with `SecretRegistry` wrapping the handle
in a `BoundSecretStore` carrying the "default" platform store
name) -- mirroring the dispatch-boundary synthesis exactly.
Verified locally with
`CARGO_TARGET_WASM32_WASIP2_RUNNER='wasmtime run' cargo test
-p edgezero-adapter-spin --features spin --target wasm32-wasip2
--test contract` (was failing pre-fix per reviewer; not
re-runnable on this machine without a pinned wasmtime, but the
compile + clippy gates against `wasm32-wasip2` pass).
Low: CLI docs described logical ids where the implementation
uses env-resolved platform names.
Both Cloudflare and Fastly's `provision` and `config push`
adapters use `store.platform.as_str()` to look up the
binding/namespace name in `wrangler.toml` / `fastly.toml`.
`store.platform` is what `EDGEZERO__STORES__<KIND>__<ID>__NAME`
resolves to at adapter-action time, falling back to the logical
id when the override isn't set. Docs previously said the
matching key was `<store_id>`; updated to `<platform-name>` with
the resolution rule spelled out, in:
- `docs/guide/cli-reference.md` -- both the provision and push
tables (cloudflare + fastly cells)
- `docs/guide/cli-walkthrough.md` -- both the provision and
push bullets (cloudflare + fastly); push bullets converted to
fenced bash blocks (see High below)
- `crates/edgezero-adapter-fastly/src/cli.rs:329` -- the
`create_fastly_store` rustdoc -- comment now says
`--name=<platform-name>` and explains the caller does the
resolution.
High: docs CI red because of multi-line inline backticks.
`docs/guide/cli-walkthrough.md`'s push bullets had inline code
spans that wrapped across line breaks (e.g.
`` `wrangler kv bulk put <tempfile>\n--namespace-id=<id>` ``).
Prettier respects `proseWrap: preserve` but re-aligned the
continuation lines such that `<tempfile>` / `<id>` ended up at
column 0 on the wrap line, terminating the backtick span. The
leaked `<tempfile>` / `<id>` were then parsed by VitePress's
Vue compiler as unterminated HTML tags, breaking
`npm run build` with `Element is missing end tag`.
Fix: cloudflare/fastly push + provision bullets now use fenced
bash code blocks for the multi-arg commands -- bulletproof
against Prettier reflow. Verified
`cd docs && npm run build` succeeds in 1.5s.
Low: docs/guide/adapters/spin.md described the wrong secret
translation rule.
The collision-check section claimed both config keys and
`#[secret]` field values get `.→__`-translated. In reality
`SpinSecretStore::get_bytes` only `to_ascii_lowercase`s the key
(no dot translation), and the CLI validator
(`crates/edgezero-adapter-spin/src/cli.rs:434-439`) mirrors that
exactly with an explicit code comment. Doc updated to say
config keys translate dots; secret values are only lowercased.
Low: docs/superpowers/plans/2026-05-20-cli-extensions.md was
stale on three points.
(1) Said `examples/app-demo` was not in CI -- it now is, via the
dedicated `cd examples/app-demo && cargo test --workspace
--all-targets` step in `test.yml` + a parallel fmt/clippy pass
in `format.yml`. (2) Listed the Spin wasm gate as
`wasm32-wasip1` -- now `wasm32-wasip2` per the Spin 6 migration.
(3) Task 8.3 step 1 (add `app-demo` CI) is done; marked `[x]`
with a reference to the workflow files.
Low: `crates/edgezero-cli/tests/generated_project_builds.rs`
exercised the raw `edgezero` binary's `config validate` but
never invoked the generated typed CLI's typed validator.
Added a `cargo run -p scaffold-probe-cli --quiet -- config
validate --strict` step after the host `cargo check`. Catches
template / `AppConfig` drift the raw validator cannot --
`#[derive(Validate)]` impl on `AppConfig` + the `#[app]`
macro-emitted `#[secret]` discovery / collision checks now run
against every freshly-scaffolded project.
Low: `crates/edgezero-adapter-axum/src/secret_store.rs` rustdoc
example used the removed `cargo edgezero dev` command. Replaced
with `API_KEY=mysecret edgezero serve --adapter axum`.
Verified post-commit: `cargo fmt --all -- --check`, `cargo
clippy --workspace --all-targets --all-features -- -D warnings`,
`cargo test --workspace --all-targets`, all three adapter
wasm-clippy gates (`cloudflare wasm32-unknown-unknown`, `fastly
wasm32-wasip1`, `spin wasm32-wasip2`), `cd docs && npm run lint
&& npm run format && npm run build`. The Cargo.lock minor-
version drift left by background cargo runs is intentionally
NOT included in this commit -- belongs in a separate dep
maintenance change.
|`axum`| Writes the flattened payload to `.edgezero/local-config-<id>.json` (the file `AxumConfigStore` reads back). Creates `.edgezero/` on first use. No shell-out. |
246
-
|`cloudflare`| Reads the namespace id from `wrangler.toml` (matched by `binding = <store_id>`), writes the entries to a temp file in wrangler's bulk format (`[{"key": "...", "value": "..."}]`), and runs `wrangler kv bulk put <tempfile> --namespace-id=<id>`. Errors with "did you run `provision`?" if the binding is absent.|
247
-
|`fastly`| Resolves the platform config-store id on demand via `fastly config-store list --json` (matched by `name = <store_id>`), then runs `fastly config-store-entry create --store-id=<id> --key=<k> --value=<v>` per entry. Errors with "did you run `provision`?" if the store name isn't found. Re-runs on entries that already exist will fail loudly — delete the entry first or use `fastly config-store-entry update` manually.|
246
+
|`cloudflare`| Reads the namespace id from `wrangler.toml` (matched by `binding = <platform-name>`, where `<platform-name>` resolves from `EDGEZERO__STORES__CONFIG__<ID>__NAME` or falls back to the logical `<id>`), writes the entries to a temp file in wrangler's bulk format (`[{"key": "...", "value": "..."}]`), and runs `wrangler kv bulk put <tempfile> --namespace-id=<id>`. Errors with "did you run `provision`?" if the binding is absent. |
247
+
|`fastly`| Resolves the platform config-store id on demand via `fastly config-store list --json` (matched by `name = <platform-name>`, where `<platform-name>` resolves from `EDGEZERO__STORES__CONFIG__<ID>__NAME` or falls back to the logical `<id>`), then runs `fastly config-store-entry create --store-id=<id> --key=<k> --value=<v>` per entry. Errors with "did you run `provision`?" if the store name isn't found. Re-runs on entries that already exist will fail loudly — delete the entry first or use `fastly config-store-entry update` manually. |
248
248
|`spin`| Pure `spin.toml` editing — no shell-out. For each entry, translates the dotted CLI key to a Spin variable name (`.` → `__`, lowercased) and writes BOTH `[variables].<key>` (with `default = "<value>"`, the application-level declaration) AND `[component.<component>.variables].<key>` (with `<key> = "{{ <key> }}"`, the component binding). Without both tables the wasm component can't read the variable. Idempotent on re-run: existing defaults are updated in place. Component resolved per §6.7 (single-component implicit; multi-component needs `[adapters.spin.adapter].component`). Secret variables stay manual — `config push` skips `SECRET_FIELDS` and never writes `secret = true`. |
|`axum`| Local-only — prints one note per declared store id and exits 0 (KV in-memory; config in `.edgezero/local-config-<id>.json`). |
279
-
|`cloudflare`| For each KV id + config id: shells out to `wrangler kv namespace create <id>`, parses the namespace id from stdout, appends `[[kv_namespaces]] binding = "<id>", id = "<extracted>"` to `wrangler.toml` (idempotent on the binding name; preserves existing entries and comments). Secrets are runtime-managed via `wrangler secret put` — no-op.|
280
-
|`fastly`| For each KV / config / secret id: shells out to `fastly <kind>-store create --name=<id>`, then appends `[setup.<kind>_stores.<id>]` and `[local_server.<kind>_stores.<id>]` tables to `fastly.toml`. Idempotent: if the setup table is already present the id is skipped (no shell-out, no edit). Store IDs are not persisted — `config push` resolves them on demand.|
281
-
|`spin`| Pure `spin.toml` editing — no shell-out (Spin KV stores are runtime-resolved). For each declared KV id, appends the label to the resolved `[component.<component>].key_value_stores = [...]` array (idempotent on the label). Config and secret ids are intentionally not handled here: `config push --adapter spin` declares config variables, and secret variables are manually declared by the developer in `spin.toml`. |
|`axum`| Local-only — prints one note per declared store id and exits 0 (KV in-memory; config in `.edgezero/local-config-<id>.json`). |
279
+
|`cloudflare`| For each KV id + config id: shells out to `wrangler kv namespace create <platform-name>` (where `<platform-name>` resolves from `EDGEZERO__STORES__<KIND>__<ID>__NAME` or falls back to the logical `<id>`), parses the namespace id from stdout, appends `[[kv_namespaces]] binding = "<platform-name>", id = "<extracted>"` to `wrangler.toml` (idempotent on the binding name; preserves existing entries and comments). Secrets are runtime-managed via `wrangler secret put` — no-op. |
280
+
|`fastly`| For each KV / config / secret id: shells out to `fastly <kind>-store create --name=<platform-name>` (using the same `<platform-name>` resolution), then appends `[setup.<kind>_stores.<platform-name>]` and `[local_server.<kind>_stores.<platform-name>]` tables to `fastly.toml`. Idempotent: if the setup table is already present the id is skipped (no shell-out, no edit). Store IDs are not persisted — `config push` resolves them on demand. |
281
+
|`spin`| Pure `spin.toml` editing — no shell-out (Spin KV stores are runtime-resolved). For each declared KV id, appends the label to the resolved `[component.<component>].key_value_stores = [...]` array (idempotent on the label). Config and secret ids are intentionally not handled here: `config push --adapter spin` declares config variables, and secret variables are manually declared by the developer in `spin.toml`. |
282
282
283
283
**`--dry-run`** prints what each adapter _would_ do without
284
284
performing it. For `axum` the output is identical to a real run
0 commit comments