Conversation
- Commit 2 bisectability vs AppDemoConfig: §8 now states commit 2's app-demo handler migration is store-accessor-only (ctx.kv_store(id), config_store, the refactored extractors). AppDemoConfig and any typed-app-config handler work are commit 3 — commit 2 never references a type that lands in commit 3. - #[secret(store_ref)] vs Single-secrets capability: §6.8 spells out that axum/cloudflare/spin are all Single for secrets, so any app including one of them has exactly one secrets id, and every #[secret(store_ref)] field must resolve to it. store_ref only buys multiple secret stores on a Fastly-only project. §15 / the walkthrough show this for the all-four-adapter app-demo. - Spin variable-name rule drift guard: commit 7 gets a golden-file test on the generated spin.toml — asserts every variable name matches ^[a-z][a-z0-9_]*$ and that the generated manifest parses (round-trips through the same parser the runtime uses), so the rule cannot drift from Spin's actual manifest behaviour. Reviewer confirms no blocking design issues remain.
- Spin manifest validation strength: the spin.toml golden test now specifies a strongest-first ladder — (1) the spin CLI's own manifest validation when present (the wasm32 spin CI job already installs it), (2) a spin_sdk validation entry point if exposed, (3) toml + regex as the weakest acceptable fallback. The regex is the floor, not the ceiling; real Spin validation is preferred wherever reachable. - Generated template vs app-demo example made explicit: `edgezero new` scaffolds the common case — greeting, nested service section, a single plain #[secret] — and deliberately does NOT include #[secret(store_ref)] (a commented line shows how to add it). store_ref only helps Fastly-only projects, so it should not be the default in every fresh scaffold. app-demo remains the full-capability showcase that exercises both secret forms. - Commit 2 flagged as the explicit review hotspot in §16: the atomic manifest+runtime rewrite warrants the most reviewer attention; its per-adapter contract tests are the primary mitigation and should be reviewed alongside the code. Reviewer confirms no blocking issues; spec is implementation-ready.
- Spin config-push --dry-run never mutates: plan Task 8.1 and spec §15
reworded — dry-run PRINTS the would-be both-table content and the
test asserts spin.toml is unchanged on disk. (The real push writing
both tables is covered by commit 7's non-dry-run tests.)
- Spin `component` field location: it belongs on the
[adapters.<x>.adapter] definition struct (with `crate`/`manifest`),
not the top-level ManifestAdapter — otherwise the accepted TOML
would wrongly be [adapters.spin] component = ...
- load_app_config API made consistent: AppConfigLoadOptions
{ env_overlay } struct; simple load_app_config / _raw apply the
overlay (default); load_app_config_with_options / _raw_with_options
take the struct; --no-env calls the _with_options form with
env_overlay: false. No hidden bool param. Updated spec §4 + §6.10
and plan Tasks 3.1 / 3.3.
- Axum multi-KV path rule: one redb file per logical id, file stem
from [adapters.axum.stores.kv.<id>].name -> .edgezero/kv-<name>.redb.
Prevents multi-store collapsing into one backing file.
- Generator manual check: stop assuming the project lands in CWD or
/tmp/throwaway; generate into an explicit mktemp dir via --dir.
- Removed references to a non-existent crates/edgezero-core/src/
hooks.rs — Hooks + ConfigStoreMetadata both live in app.rs.
- Macro compile-fail tests: Task 3.2 now adds `trybuild = "1"` to edgezero-macros [dev-dependencies] explicitly (only `tempfile` was there), with a tests/ui/*.rs fixture + .stderr golden per rejected case. - External-consumer test env guard: tests/lib_consumer.rs must restore EDGEZERO_MANIFEST via an RAII EnvOverride guard and stay a single #[test] (no in-binary parallelism); a shared Mutex guard is required if more env-touching tests are ever added. - WASM contract test commands pinned: Task 2.7 step 6 names the exact target / features / runner per adapter (cloudflare wasm32-unknown- unknown + wasm-bindgen; fastly wasm32-wasip1 + Viceroy; spin wasm32-wasip1 + Wasmtime), deferring to test.yml as source of truth. - app-demo e2e lifecycle: Task 8.1/8.2 now require an ephemeral port (no hard-coded 8787), a readiness poll (no bare sleep), and RAII teardown that kills the demo server even on assertion failure; the loop is preferably a Rust integration test, not shell-in-YAML.
…ign gate - Default `edgezero` binary wiring (High): commits 4-7 now have explicit steps to add Auth / Provision / Config(Validate|Push) to the default edgezero-cli `Command` enum and `main.rs` dispatch (raw run_* — the default binary has no app struct), with `edgezero --help` / parse tests. Previously only the original five commands and app-demo-cli were wired; the spec requires the new subcommands on the default binary too. New Task 4.2 covers `config`; Task 5.2/6.1/7.2 extended. - Generated `<name>-cli` template upgrade (Medium): new Task 8.2 updates templates/cli/src/main.rs.hbs to the full eight-command set once auth/provision/config exist, wiring the scaffold's config arm to the typed functions with the generated project's config struct. Generator test asserts it. - Full-gate alignment (Medium): added a canonical "## The full gate" section with the exact five CI commands from CLAUDE.md / the workflows (cargo check uses --features "fastly cloudflare spin", not --all-features). Every "run the full gate" step references it; fixed the commit-1 and commit-8 gate steps and the Codebase-facts CI line that had drifted to --all-features. Commit-8 tasks renumbered (8.2 CI wiring -> 8.3; walkthrough/audit -> 8.4).
- app-demo-cli missing app-demo-core dep (High): Task 4.3 now adds
`app-demo-core = { path = "../app-demo-core" }` to
app-demo-cli/Cargo.toml — it references AppDemoConfig once typed
`config validate` / `config push` are wired, but its deps were only
edgezero-cli/clap/log.
- Generated <name>-cli template missing core-crate dep (High): Task 8.2
now also updates templates/cli/Cargo.toml.hbs to depend on
`{{name}}-core` (path dep), and the generator test asserts the
scaffold builds with that dependency and resolves the typed config
type.
- AppConfig macro + validator availability (Medium): chosen route
stated explicitly — `edgezero-core` re-exports the `AppConfig` derive
(matching the existing `action`/`app` re-exports), so a config crate
needs only `edgezero-core` for the macro, no direct edgezero-macros
dep. Task 3.4 updates templates/core/Cargo.toml.hbs to add
`validator` (with derive); Task 3.5 verifies app-demo-core already
carries edgezero-core + validator + serde. Generator test checks the
scaffolded core crate builds.
Task 3.4 / 4.3 / 8.2 steps renumbered to fit the inserted dependency
steps.
- Generated config type placeholder (Medium): Task 3.4 step 1 now
explicitly adds a `NameUpperCamel` key to the generator Handlebars
context (derived from `name`: split on -/_, upper-case each segment,
join — `my-app` -> `MyApp`), with a unit test. Templates reference
`{{NameUpperCamel}}Config`; the key was previously unset (generator
data only had name/proj_core/proj_core_mod/proj_mod).
- validator workspace-dep plumbing (Medium/Low): Task 3.4 step 3 now
names the generator change explicitly — `templates/core/Cargo.toml.hbs`
uses `validator = { workspace = true }`, so `validator` must also be
added to the generator's workspace-dependency seed
(`seed_workspace_dependencies` in generator.rs), which omits it today.
- Duplicate Step 4 in Task 3.4 (Low): Task 3.4 renumbered cleanly to
Steps 1-6.
…ident
- Generated CLI import (Medium): the cli template's `use` must
reference the core crate's Rust module name, not the package name.
`use {{name}}_core::...` renders `my-app_core` for `my-app` (invalid
Rust). Task 8.2 now uses `{{proj_core_mod}}` — the hyphen-to-
underscore module form the generator already exposes.
- NameUpperCamel validity (Medium/Low): Task 3.4 step 1 derivation now
guarantees a valid Rust type identifier — derive from the sanitized
crate name, drop empty segments (absorbs a leading `_`), and prefix
with `App` when the result would start with a non-letter (digit-
leading project names). Unit test covers `123-app` -> `App123App`,
`_foo` -> `Foo`, etc.
ChristianPavilonis review:
* Generator dependency seeds were stale relative to the adapters:
worker 0.7 → 0.8
fastly 0.11 → 0.12
simple_logger 4 → 5
Scaffolded projects pinned older provider SDKs than the adapters
expect, risking public-type mismatches in generated entrypoints.
* `PersistentKvStore::list_keys_page` lost keys when the scan cap was
hit. On a cap-hit the loop broke with `reached_end = false`, but the
cursor was only emitted when `live_keys.len() > limit`. An
under-filled page (cap reached while skipping a long expired run)
therefore returned `cursor: None` and callers stopped paginating,
silently missing live keys past the expired run. Now a cap-hit is
tracked and the last scanned key is returned as the resume cursor.
Added `list_keys_page_returns_resume_cursor_when_scan_cap_is_hit`.
`LIST_SCAN_BATCH_SIZE`/`MAX_SCAN_BATCHES` are lowered under
`cfg(test)` so the cap path is reachable with a 43-entry fixture
instead of 25k; pagination correctness is batch-size-independent.
Turn edgezero-cli into lib + bin so downstream projects can build their own CLI binary reusing any subset of the built-in commands. - Promote Command variant fields into standalone #[derive(clap::Args)] structs (BuildArgs / DeployArgs / ServeArgs; NewArgs already standalone), each #[non_exhaustive] + Default for external construction. - Add src/lib.rs exposing the public API: run_build / run_deploy / run_serve / run_new / run_demo, init_cli_logger, and the args module (pub mod, not pub use — restriction lint). main.rs becomes a thin wrapper over the library. - Rename the `dev` subcommand to `demo` (dev is reserved for a future dev-workflow command): dev_server.rs -> demo_server.rs, run_dev -> run_demo (now Result<(), String>), Command::Dev -> Command::Demo. - Extend the generator to scaffold a crates/<name>-cli crate from new templates/cli/ Handlebars templates; seed clap + edgezero-cli as workspace dependencies; add crates/<name>-cli to the workspace members. - Add the handwritten examples/app-demo/crates/app-demo-cli crate as the canonical downstream consumer, with a --help smoke test. - Add crates/edgezero-cli/tests/lib_consumer.rs: external-consumer integration test proving the public API is usable from outside. - Docs: cli-reference.md (demo rename + "Building Your Own CLI"), getting-started.md, CLAUDE.md. All gates green: fmt, clippy -D warnings, cargo test --workspace, feature cargo check, spin wasm32; app-demo workspace fmt/clippy/test.
After the dev->demo rename, `demo` should mean "run the bundled example", not "run the project's axum adapter". Drop `try_run_manifest_axum` (and its `load_manifest_optional` helper) from `demo_server`: `edgezero demo` now always starts the built-in example server on 127.0.0.1:8787 and never reads `edgezero.toml`. `edgezero serve --adapter axum` is now the single, unambiguous way to run a project's axum adapter (it runs `[adapters.axum.commands].serve`). This removes the demo / serve --adapter axum behavioral overlap. Docs updated.
…mit 2 - Commit 1 marked DONE (landed 1d582dd + follow-up 06f4b72) with a Status section, so workers don't redo already-landed work. - Working-branch reference corrected: feature/extensible-cli (was the stale docs/extensible-cli-library-spec). - app-demo edgezero-cli dep path fixed to ../../crates/edgezero-cli (relative to the workspace manifest; the four-up path was wrong and would break the demo workspace). - Task 2.7 Fastly step expanded from one line to explicit per-kind registry steps + contract tests: Fastly is Multi for KV/config/ secrets, two logical stores per kind, per-id name resolution, id-keyed contract coverage under Viceroy — parity with the cloudflare/spin acceptance criteria.
Commit 1 shipped `pub mod args` rather than crate-root re-exports: a
root `pub use args::{...}` trips clippy::pub_use (the restriction group
is -D-denied workspace-wide). §4 now documents the supported API as
edgezero_cli::args::BuildArgs etc., with run_* staying at the crate
root, and updates every run_* signature to &args::<T>. Matches what
1d582dd actually exposes and what lib_consumer.rs / cli-reference.md
already use.
(Reviewer's second finding — demo overlapping serve --adapter axum in
1d582dd — was already resolved by 06f4b72; no action.)
Review comment #7: a freshly generated project failed its own restriction-deny clippy gate immediately. - Core handler template: mirror app-demo's passing structure — fallible `stream`/`IntoResponse` usage (no production `.expect`), alphabetically ordered structs, grouped test items, `IntoResponse` imported anonymously. - Adapter host stubs: add `#[expect(clippy::print_stderr, reason)]` and `allow(dead_code, reason)`; the axum entrypoint returns `anyhow::Result` instead of `eprintln!` + `process::exit`. - Inline the project-core `App` type in adapter entrypoints so import order stays stable regardless of project name. - key_value_store: replace `#[cfg(not(test))]` consts with `if cfg!(test)` and rename a `cursor` binding that shadowed the parameter (clippy `cfg_not_test` / `shadow_unrelated`). - Add scaffold lint-coverage assertions to the generator test.
# Conflicts: # crates/edgezero-adapter-axum/src/cli.rs # crates/edgezero-adapter-axum/src/dev_server.rs # crates/edgezero-cli/src/dev_server.rs # crates/edgezero-core/src/lib.rs
…e/extensible-cli # Conflicts: # crates/edgezero-cli/src/dev_server.rs
`edgezero demo` now delegates to `edgezero_adapter_axum::dev_server::run_app`, running the bundled app-demo example the same way its own axum adapter does. This wires the complete manifest setup (routing, KV/config/secret stores, logging, host/port) instead of a hand-rolled echo router. The demo path requires the `dev-example` feature; without it `run_demo` returns an actionable error.
The eight numbered work units are now "stages" rather than "commits" — each stage may span multiple git commits. Literal git-commit actions (commit steps, `git commit -m`, the PR head commit) keep the "commit" wording.
Addresses review findings on the demo subcommand: - demo is exposed only when built with the new `demo-example` feature. Generated CLIs and app-demo-cli no longer expose `Demo` at all — a downstream project has no bundled app-demo to run. The default `edgezero` binary gates `Command::Demo` on `demo-example`, so the advertised `--help` surface matches what actually works. - `demo-example` (renamed from `dev-example`) now also pulls in `edgezero-adapter-axum`, making the feature self-contained. - getting-started.md points generated projects at `edgezero serve --adapter axum`; cli-reference.md documents `demo` as contributor-only. - NewArgs now derives Default and is #[non_exhaustive], matching the other public *Args structs. - Generated handler tests serialize API_BASE_URL access behind a mutex + RAII env guard. - Refreshed README, CLAUDE.md, architecture docs, and agent docs for the dev->demo / dev-example->demo-example rename.
Addresses review findings on the Stage 1 surface: - Add a `[[bin]] name = "edgezero"` target so `cargo build` produces `target/debug/edgezero` — the name every doc and the clap `about` already use. - Remove the inert `--local-core` flag from `NewArgs`; it was never read by the generator. - Warn when `edgezero new` falls back to a Git dependency for `edgezero-cli`: the generated CLI crate needs `edgezero-cli` as a published library, so an out-of-repo scaffold only builds once that is available on the referenced remote. In-repo generation uses a path dependency and is unaffected. - Replace removed `edgezero dev` references with `edgezero serve --adapter axum` in the root README, architecture, and axum adapter docs. - Drop `run_demo` from the "build your own CLI" surface (it is contributor-only), and add the generated `*-cli` and Spin adapter crates to the scaffold structure docs.
Fixes fresh `edgezero new` projects failing to build outside the repo. The generated CLI crate imports `edgezero_cli`, but dependency resolution fell back to a Git dependency whenever the output directory was outside the repo root — and the published `edgezero-cli` has no library target, so every `edgezero_cli::...` import failed. - Locate the edgezero checkout via `CARGO_MANIFEST_DIR` (baked in at build time) instead of the current directory, so generation finds the checkout regardless of where the project is created or where the command runs. - When the output directory is outside the checkout, emit an absolute path dependency rather than the Git fallback. The Git fallback now only applies to a binary detached from its source tree. - Assert in the generator test that the scaffold resolves edgezero crates to path dependencies, so a regression to the Git fallback is caught by `cargo test -p edgezero-cli`. - Add an opt-in (`#[ignore]`) integration test that runs `cargo check` on the generated CLI crate, proving it compiles against the local `edgezero-cli` library. - Drop the stale `--local-core` option from the CLI reference docs.
Broaden the opt-in scaffold test to `cargo check --workspace` and drop `--offline`: a freshly generated project has no lockfile, so offline resolution of transitive registry crates is unreliable (true of any scaffolded project). Online, the full generated workspace compiles.
# Conflicts: # .github/workflows/test.yml # crates/edgezero-adapter-spin/src/lib.rs # crates/edgezero-adapter-spin/src/request.rs # crates/edgezero-adapter-spin/src/templates/src/lib.rs.hbs # crates/edgezero-core/src/config_store.rs # crates/edgezero-core/src/manifest.rs # crates/edgezero-core/src/secret_store.rs
- Adapter README `dev_steps` snippets advised `edgezero-cli serve --adapter ...`, but the binary is `edgezero` (the `edgezero-cli` package builds `target/debug/edgezero`). Corrected all four adapters (axum, cloudflare, fastly, spin) so generated-project READMEs show a working command. - Updated the plan and spec acceptance notes: generated and app-demo CLIs expose the four downstream built-ins (build/deploy/new/serve), not five — `demo` is contributor-only and absent from downstream CLIs. Also corrected the Stage 8 generated-CLI command count.
The key_value_store and secret_store modules are already gated at their mod declaration in lib.rs (#[cfg(all(feature = "spin", target_arch = "wasm32"))]), so every per-item copy of that same cfg inside the files was a tautology. Removing the 14 no-op attributes makes both files consistent with their sibling request.rs/response.rs/proxy.rs, which already rely on the gated mod declaration.
- Add a CI step that runs the `generated_project_builds` test (`-- --ignored`), so the Stage 1 scaffold regression — a fresh `edgezero new` project failing to compile — is caught by CI rather than only by manual runs. - Correct two stale Stage 1 plan steps: a default `cargo build -p edgezero-cli` exposes four subcommands, not five; `demo` is gated behind the `demo-example` feature.
aram356
marked this pull request as ready for review
September 14, 2026 15:38
aram356
marked this pull request as draft
September 14, 2026 16:15
aram356
marked this pull request as ready for review
September 14, 2026 20:27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.AbortController, Fastly's low-levelstream_to_client, and Spin's WASI response/body/result writers.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.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, phase-budget, and isolation limitations.Validation
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"