Files
logos-scaffold/DOGFOODING.md
T
SashaandClaude Opus 4.7 94de994380 feat: add scaffold ai skills (#104)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 01:32:04 +02:00

60 KiB

logos-scaffold dogfooding Scenarios

This document is the canonical dogfooding runbook for logos-scaffold. Use it to evaluate the latest repository state, not as a dated findings report. Earlier one-off dogfood notes are historical context only; future runs should start here.

Maintenance note: update this document whenever first-class commands, templates, supported workflows, or major user-facing behaviors are added, removed, or materially changed. If the product surface changes and this runbook does not, the runbook is wrong.

Purpose and Audience

  • Dogfooders: use this as a repeatable checklist when validating the latest scaffold DX.
  • Contributors: use this document to decide which scenarios must be rerun for a given change.

This guide is intentionally scenario-oriented:

  • It defines what to exercise.
  • It defines what success looks like.
  • It calls out the failures and caveats that are worth recording.
  • It does not replace generated project READMEs or CLI help text.

Usage Model

The recommended dogfooding pattern is:

  1. Build the local scaffold binary from the repository under test.
  2. Create fresh generated projects in a scratch workspace outside the repo.
  3. Run project-level scenarios from inside the generated project root.
  4. Capture command, cwd, exit code, and a short output excerpt for each scenario.
  5. If the behavior differs from this runbook, update the runbook when the difference is intentional and file a bug when it is not.

For repo dogfooding, prefer the freshly built local binary over an already-installed global binary.

Scaffold now treats LEZ tooling as project-local state. For non-vendored projects, the shared cache layout is <cache_root>/repos/lez/<pin>/...; for vendored projects, LEZ lives under <project>/.scaffold/repos/lez. In both cases, the wallet binary under test is the LEZ-local build artifact at <lez>/target/release/wallet, invoked through logos-scaffold wallet ... rather than a wallet binary on PATH.

export REPO_ROOT=/absolute/path/to/logos-scaffold
export SCRATCH_ROOT=/absolute/path/to/dogfood-runs
cd "$REPO_ROOT"
cargo build
export SCAFFOLD_BIN="$REPO_ROOT/target/debug/logos-scaffold"
mkdir -p "$SCRATCH_ROOT"

You may replace "$SCAFFOLD_BIN" with logos-scaffold when the install path itself is part of what you are validating.

Execution Contexts

Context Purpose Typical commands
Repo root Build the latest CLI, inspect docs, validate help/version output, verify out-of-project errors cargo build, "$SCAFFOLD_BIN" --help, "$SCAFFOLD_BIN" --version, "$SCAFFOLD_BIN" build (expect error)
Scratch workspace Create fresh generated projects without polluting the repo; test advanced creation flags "$SCAFFOLD_BIN" new dogfood-default, "$SCAFFOLD_BIN" new ... --template ...
Generated project root Execute scaffold workflows and example runners against a fresh project setup, localnet, build, deploy, wallet, doctor, report, cargo run --bin run_*

Do not run project-scoped commands from the repository root unless the scenario is explicitly checking the "outside project" error path.

Shared Preconditions

  • Unix-like environment with git, rustc, cargo, lsof, ps, and kill.
  • Docker or Podman available for guest builds.
  • No conflicting listener on the scaffold localnet port before localnet start.
  • Network access available for setup/build flows that fetch dependencies.
  • No preinstalled wallet binary is required. If one exists on PATH, do not treat it as the runtime under test for scaffold wallet scenarios.
  • Optional but supported: LOGOS_SCAFFOLD_WALLET_PASSWORD when validating password override behavior.
  • For B-series (basecamp) scenarios: Nix with flakes enabled, plus a module project on disk whose flake.nix exposes a packages.<system>.lgx output (e.g., a tictactoe-style project built against the logos-module-builder tutorial-v1 convention). docs/basecamp-module-requirements.md (also reachable via "$SCAFFOLD_BIN" basecamp docs) is the canonical contract.

The lgs binary is a short alias for logos-scaffold produced by the same crate; "$SCAFFOLD_BIN" and lgs are interchangeable in the commands below.

Scenario Index

ID Template Level Goal Command surface
D1 default Core Fresh project creation and first-success bootstrap new, create, setup, localnet start, build, deploy, wallet topup, wallet -- check-health
D2 default Core Localnet lifecycle visibility and doctor checks localnet status, localnet logs, localnet stop, doctor, JSON variants
D3 default Advanced Deploy path variations and machine-readable single-program submission deploy [program-name], deploy --program-path, deploy --program-path --json
D4 default Core Wallet management, default-address behavior, and passthrough UX wallet list, wallet default set, wallet topup --dry-run, wallet topup, wallet -- ...
D5 default Advanced Diagnostics bundle and support artifact hygiene report, report --out, report --tail
D6 default Core Example runner interaction and account state verification cargo run --bin run_hello_world, cargo run --bin run_hello_world_with_move_function, wallet -- account get
D7 default Core One-step run pipeline and post-deploy hooks run, run --post-deploy, run --no-post-deploy, [run] config
L1 lez-framework Core Fresh LEZ project bootstrap to ready state new --template lez-framework, setup, localnet start, doctor, build
L2 lez-framework Core LEZ IDL regeneration build idl
L3 lez-framework Advanced LEZ client generation from current IDL build client
L4 lez-framework Core LEZ deploy and counter interaction deploy, cargo run --bin run_lez_counter
E1 N/A Core CLI discoverability and error quality --help, help, --version, unknown commands, out-of-project errors
E2 N/A Advanced Project creation with advanced flags and invalid inputs new --template, new --vendor-deps, new --cache-root
E3 N/A Core AI skills materialized into generated and adopted projects new, new --template lez-framework, init, init re-run
B1 external module project Core Basecamp + lgpm setup and idempotent re-run init, basecamp setup, basecamp doctor, basecamp docs
B2 external module project Core Module capture, install, and single-instance launch basecamp modules, basecamp modules --show, basecamp install, basecamp launch alice
B3 external module project Core Two-instance p2p dogfooding basecamp launch alice, basecamp launch bob (parallel)
B4 external module project Advanced Clean-slate scrub semantics on relaunch basecamp launch alice, basecamp launch alice --no-clean
B5 external module project Advanced Portable artefact build for AppImage hand-loading basecamp build-portable

Standing Validation Notes

  • Project context matters. Many scaffold commands are meant to be run only inside a generated project root. Running them elsewhere should produce a clear error, not silent misbehavior.
  • Localnet readiness, listener ownership, and wallet connectivity are high-value validation points. Record contradictions instead of smoothing over them.
  • Machine-readable paths matter for tooling. Preserve --json outputs when a scenario includes them.
  • report is sanitized on a best-effort basis, not on an absolute guarantee. Always inspect the archive before sharing it.
  • When wallet behavior depends on an omitted address, verify whether the project default wallet was seeded and persisted as expected.
  • Example runner programs (cargo run --bin run_*) are the final proof that the scaffold pipeline works end-to-end. A successful deploy means nothing if the runner cannot interact with the deployed program.

D1. Default Template Bootstrap and First Success

Goal

Validate that the default template can be scaffolded from the latest repo and reach the documented first-success path.

Preconditions

  • cargo build completed at the repo root.
  • "$SCAFFOLD_BIN" points to the freshly built binary.
  • Scratch workspace exists and is writable.

Commands / Actions

From the scratch workspace:

cd "$SCRATCH_ROOT"
"$SCAFFOLD_BIN" new dogfood-default
"$SCAFFOLD_BIN" create dogfood-default-create
cd dogfood-default
"$SCAFFOLD_BIN" setup
"$SCAFFOLD_BIN" localnet start
"$SCAFFOLD_BIN" build
"$SCAFFOLD_BIN" deploy
"$SCAFFOLD_BIN" wallet topup
"$SCAFFOLD_BIN" wallet -- check-health

Use new for the main runnable project and create as the lightweight alias-parity check in a separate directory. Both commands also accept --template, --vendor-deps, --lez-path, and --cache-root, but this scenario uses defaults only. See E2 for advanced flag coverage.

Expected Success Signals

  • Project creation succeeds and prints the destination path, pinned LEZ commit, and cache root.
  • setup completes after syncing LEZ to the configured pin, building both sequencer_service and wallet inside the project's LEZ tree, and either seeding the default wallet or reporting that a default wallet is already configured.
  • localnet start reports a ready localnet rather than only a spawned PID.
  • build exits successfully after preparing the project workspace, and — when the project has a methods/Cargo.toml (Risc0 guest crate excluded from the main workspace) — also prints Building guest methods... and produces a methods/target/.../release artifact.
  • deploy prints a submission summary with zero failures when built binaries are present.
  • wallet topup succeeds without an explicit address because the project default wallet was seeded during setup.
  • wallet -- check-health succeeds against the running localnet without requiring a global wallet install or manual PATH changes.
  • Generated scaffold.toml stores [wallet].home_dir but does not carry a wallet binary override; wallet location is derived from the pinned LEZ checkout.

Failure Signals / Common Pitfalls

  • Running setup, build, deploy, or wallet commands outside the generated project root should fail with a project-scoped message.
  • A foreign listener or stale state on the localnet port is a real dogfooding finding; capture localnet status, not just the final error.
  • If wallet topup without an address says no destination is configured, record that as a regression in default-wallet seeding or persistence.
  • If setup or wallet commands depend on wallet being installed globally or on PATH, record that as a regression in the self-contained project model.
  • If deploy fails due to missing binaries after a successful build, capture the exact missing path.

Evidence to Capture

  • Scaffold creation output for both new and create.
  • setup, localnet start, build, deploy, and wallet command excerpts.
  • The generated project path and the exact binary path used for the run.

Execution Notes

  • Use fresh directories per run. Do not reuse an old generated project unless the scenario explicitly targets upgrade or persistence behavior.
  • Keep the alias check isolated so a failure in create does not contaminate the primary bootstrap project.

D2. Default Template Operational Health: Localnet and Doctor

Goal

Validate that localnet lifecycle commands and doctor diagnostics provide usable human and machine-readable state.

Preconditions

  • A default-template project exists.
  • setup has already completed for that project.

Commands / Actions

From the generated project root:

"$SCAFFOLD_BIN" localnet status
"$SCAFFOLD_BIN" localnet status --json
"$SCAFFOLD_BIN" doctor
"$SCAFFOLD_BIN" doctor --json
"$SCAFFOLD_BIN" localnet logs --tail 200
"$SCAFFOLD_BIN" localnet stop
"$SCAFFOLD_BIN" localnet status

If the scenario begins with localnet stopped, run "$SCAFFOLD_BIN" localnet start first and capture both the started and stopped states.

Expected Success Signals

  • Human-readable localnet status clearly reports tracked PID, listener state, ownership, and readiness.
  • localnet status --json returns parseable JSON with at least tracked_pid, listener_present, ownership, and ready.
  • doctor returns actionable next steps rather than only raw failures.
  • doctor --json returns parseable JSON with at least status, summary, checks, and next_steps.
  • localnet logs --tail 200 returns useful recent log lines when logs exist.
  • localnet stop succeeds cleanly and subsequent status reflects the stopped state.

Failure Signals / Common Pitfalls

  • Contradictions between tracked PID, listener ownership, and readiness are high-value findings.
  • Empty or unhelpful logs after a failed startup are worth recording.
  • If doctor omits next steps or machine-readable output becomes malformed, treat that as a DX regression.

Evidence to Capture

  • Human-readable and JSON output for both localnet status and doctor.
  • A short localnet logs excerpt.
  • Stop behavior and the post-stop status output.

Execution Notes

  • Preserve raw JSON output exactly.
  • If state is contradictory, do not silently restart localnet before capturing the failing state.

D3. Default Template Deploy Variants and JSON Output

Goal

Validate targeted deployment flows, including the machine-readable single-program submission path via --program-path.

Preconditions

  • Default-template project has already completed build.
  • Localnet is reachable.
  • Guest binaries exist under the generated project's target/riscv-guest/.../release directory.

Commands / Actions

From the generated project root:

export EXAMPLE_PROGRAMS_BUILD_DIR="$PWD/target/riscv-guest/example_program_deployment_methods/example_program_deployment_programs/riscv32im-risc0-zkvm-elf/release"
"$SCAFFOLD_BIN" deploy hello_world
"$SCAFFOLD_BIN" deploy --program-path "$EXAMPLE_PROGRAMS_BUILD_DIR/hello_world.bin"
"$SCAFFOLD_BIN" deploy --program-path "$EXAMPLE_PROGRAMS_BUILD_DIR/hello_world.bin" --json
"$SCAFFOLD_BIN" deploy nonexistent_program

Use a known default-template program name such as hello_world. If the generated project exposes a different set of programs in methods/guest/src/bin, record the discovered list.

--json only produces structured JSON output when combined with --program-path. On the discovery-based path (deploy or deploy <name>), the --json flag is accepted but silently ignored. This scenario validates that distinction.

Expected Success Signals

  • deploy hello_world reports OK hello_world submitted and ends with a human-readable success summary.
  • deploy --program-path ... --json prints a parseable JSON object with at least status, program, and tx fields.
  • deploy --program-path ... without --json prints a human-readable OK line with the binary path.
  • deploy nonexistent_program fails with an error listing the available discovered programs.

Failure Signals / Common Pitfalls

  • If deploy hello_world --json starts producing JSON output (instead of the normal human-readable summary), record that as a behavior change worth verifying.
  • If localnet is unreachable, deploy should fail with a sequencer-unavailable hint instead of a vague wallet error.
  • Unknown program names should report the available discovered programs.
  • Missing binaries should point back to logos-scaffold build.

Evidence to Capture

  • One successful human-readable deploy excerpt from the discovery path.
  • One successful JSON deploy output from the --program-path path.
  • The error output for an unknown program name.
  • Any failure-path excerpt for unreachable sequencer or missing binary when intentionally probed.

Execution Notes

  • Keep the --program-path --json examples separate from discovery-based deploys. Only --program-path produces JSON.
  • When recording a custom --program-path, preserve the absolute path used in the run log.

D4. Default Template Wallet Workflows and Passthrough

Goal

Validate wallet-focused scaffold behavior beyond the basic bootstrap path.

Preconditions

  • Default-template project exists.
  • Setup completed successfully.
  • Localnet is running if you are validating non-dry-run topup or passthrough health checks.

Commands / Actions

From the generated project root:

"$SCAFFOLD_BIN" wallet list
"$SCAFFOLD_BIN" wallet list --long
"$SCAFFOLD_BIN" wallet default set Public/<account-id>
"$SCAFFOLD_BIN" wallet topup --dry-run
"$SCAFFOLD_BIN" wallet topup
"$SCAFFOLD_BIN" wallet -- account list
"$SCAFFOLD_BIN" wallet -- check-health

Use a real address from wallet list when explicitly validating wallet default set.

Expected Success Signals

  • wallet list and wallet list --long proxy wallet account enumeration from the project-scoped wallet home using the LEZ-local wallet binary.
  • wallet default set accepts either positional address or --address and persists the normalized project default.
  • wallet topup --dry-run renders the underlying faucet claim command instead of mutating state.
  • wallet topup without an explicit address uses the saved default wallet.
  • wallet -- ... preserves the project wallet environment while forwarding the raw wallet command to <lez>/target/release/wallet.

Optional: validate LOGOS_SCAFFOLD_WALLET_PASSWORD override behavior by setting the env var to a non-default value and observing whether wallet commands honor it.

LOGOS_SCAFFOLD_WALLET_PASSWORD="custom-pw" "$SCAFFOLD_BIN" wallet topup --dry-run

Failure Signals / Common Pitfalls

  • Invalid addresses should be rejected with an "Accepted formats" hint.
  • If both positional address and --address are supplied together, that is a user error and should remain clearly reported.
  • Connectivity failures during topup should mention localnet/sequencer reachability rather than only raw wallet output.
  • Passthrough flows require the literal --; if the CLI starts accepting or mangling passthrough without it, record that change.
  • If wallet flows only succeed when wallet is separately installed on PATH, or if missing-binary errors point anywhere other than the LEZ-local target/release/wallet, record that as a regression.

Evidence to Capture

  • wallet list output with account identifiers redacted only if needed for sharing.
  • wallet topup --dry-run output showing the rendered command.
  • One successful passthrough example, ideally wallet -- check-health or wallet -- account list.
  • If LOGOS_SCAFFOLD_WALLET_PASSWORD override was tested, the dry-run output showing the password was or was not forwarded.

Execution Notes

  • Do not let the shell consume the passthrough separator. Record the exact argv form you used.
  • If you redact account IDs for public sharing, keep the unredacted originals in a local evidence log so repeated runs stay traceable.

D5. Default Template Diagnostics Bundle

Goal

Validate that scaffold support artifacts can be collected and inspected safely.

Preconditions

  • Default-template project exists.
  • The project has enough state to make the report meaningful, ideally after setup and at least one localnet or build action.

Commands / Actions

From the generated project root:

"$SCAFFOLD_BIN" report
"$SCAFFOLD_BIN" report --tail 200
"$SCAFFOLD_BIN" report --out "$PWD/artifacts/support-report.tar.gz"

Inspect the produced archive before sharing it:

find .scaffold/reports -maxdepth 1 -name '*.tar.gz' -print | sort
REPORT_ARCHIVE="$(find .scaffold/reports -maxdepth 1 -name '*.tar.gz' | sort | tail -n 1)"
tar -tzf "$REPORT_ARCHIVE" | sort
tar -tzf "$PWD/artifacts/support-report.tar.gz" | sort

Expected Success Signals

  • report prints a completion message, archive path, and a warning to inspect files before sharing.
  • The default output lands under .scaffold/reports/.
  • A custom --out path is honored.
  • The archive contains support files such as README.txt, manifest.json, diagnostics/doctor.json, diagnostics/localnet-status.json, and summaries/build-evidence.json.

Failure Signals / Common Pitfalls

  • If raw wallet files under .scaffold/wallet/ appear in the archive, treat that as a severe regression.
  • If absolute local paths leak without scrubbing in human-facing report files, record it.
  • If the archive is produced but the warning about manual inspection disappears, record it.

Evidence to Capture

  • Report completion output.
  • Archive path(s).
  • A short file listing from the tarball.

Execution Notes

  • Never attach the archive to an external system without first listing its contents.
  • Keep the tar listing with the run evidence so redaction regressions can be compared across releases.

D6. Default Template Example Runner Interaction

Goal

Validate that deployed programs can actually be invoked via the generated example runner binaries and that account state changes are observable.

D1 validates the scaffold pipeline up to deploy and wallet health. This scenario validates the final step: running programs against the localnet and confirming observable state mutations.

Preconditions

  • Default-template project exists with D1 completed (setup, build, deploy done).
  • Localnet is running and wallet -- check-health succeeds.
  • Create a fresh public account for this scenario:
"$SCAFFOLD_BIN" wallet -- account new public

Capture the account ID from the output (format: Public/<base58>). Use the base58 portion as <account-id> below.

Commands / Actions

From the generated project root:

export NSSA_WALLET_HOME_DIR="$PWD/.scaffold/wallet"
cargo run --bin run_hello_world -- <account-id>
"$SCAFFOLD_BIN" wallet -- account get --account-id <account-id>
cargo run --bin run_hello_world_with_move_function -- write-public <account-id> "dogfood-test-message"
"$SCAFFOLD_BIN" wallet -- account get --account-id <account-id>

The first runner (run_hello_world) submits a basic public transaction. The second (run_hello_world_with_move_function write-public) writes a custom greeting string to the account, producing an observable data_b64 field change.

Expected Success Signals

  • Both runners print submitted transaction: status=... tx_hash=... on success.
  • Both runners print a verification hint: line pointing to wallet account get.
  • After run_hello_world_with_move_function write-public, wallet account get shows account data containing the encoded greeting string.
  • Runner exit code is 0.

Failure Signals / Common Pitfalls

  • If a runner exits 0 but the account remains Uninitialized, the transaction may have been submitted without effect. Record both the runner output and the account state.
  • Panic output from a runner (e.g., unwrap() on wallet/sequencer errors) instead of a structured error is worth recording.
  • Invalid account ID format (not base58) should produce a clear parse error from the runner, not a panic.
  • If localnet is down, runners should fail with a connection-refused error. Capture the exact error text.

Evidence to Capture

  • Runner output including status and tx_hash for at least one successful run.
  • wallet account get output showing account state after interaction.
  • The exact account ID used (for traceability across repeated runs).

Execution Notes

  • NSSA_WALLET_HOME_DIR must be set for runners that initialize WalletCore::from_env(). The scaffold wallet commands set this automatically, but direct cargo run does not.
  • Use the fresh public account created in the preconditions rather than reusing accounts from other scenarios. This avoids confusion about pre-existing state.
  • If additional runners are available (e.g., run_hello_world_private, run_hello_world_through_tail_call), exercising them is valuable but not required for this scenario.

D7. run Pipeline and Post-Deploy Hooks

Goal

Validate that lgs run collapses the build → IDL → localnet → topup → deploy chain into a single command, fires [run].post_deploy hooks with the documented environment, and that --post-deploy / --no-post-deploy flags override the configured hooks correctly.

Preconditions

  • A default-template project exists at $SCRATCH_ROOT/dogfood-default with setup already complete.
  • No existing scaffold localnet running on the configured port (the scenario will start one). If one exists from a prior scenario, stop it first.
  • wallet topup has worked at least once for this project (D1 or D4 covers this).

Commands / Actions

From the project root, exercise the bare pipeline:

"$SCAFFOLD_BIN" run

Then add a [run] section to scaffold.toml and re-run with hooks:

[run]
post_deploy = [
  "echo 'sequencer:' $SEQUENCER_URL",
  "echo 'idl:' $SCAFFOLD_IDL_DIR",
  "echo 'project root:' $SCAFFOLD_PROJECT_ROOT",
  "echo 'wallet home:' $NSSA_WALLET_HOME_DIR",
  "echo 'program id:' ${SCAFFOLD_PROGRAM_ID:-unavailable}",
  "echo 'guest bin:' ${SCAFFOLD_GUEST_BIN:-unavailable}",
]
"$SCAFFOLD_BIN" run
"$SCAFFOLD_BIN" run --post-deploy "echo override"    # one-shot override
"$SCAFFOLD_BIN" run --no-post-deploy                 # skip hooks
"$SCAFFOLD_BIN" run --post-deploy "x" --no-post-deploy  # expect clap conflict error

Expected Success Signals

  • The first run (no hooks configured) prints a numbered step header for each phase ([1/5] Building... through [5/5] Deploying...) and ends with a deployed-programs summary.
  • A second run reuses the running localnet (localnet already running (sequencer pid=...)) instead of starting a new sequencer.
  • After adding the [run] block, run reports [6/6] Running N post-deploy hook(s) and each hook prints a non-empty value for its env var. cwd for each hook is the project root (verifiable with a pwd hook). For a single-program project, $SCAFFOLD_PROGRAM_ID is the deployed program's risc0 image ID and $SCAFFOLD_GUEST_BIN is the absolute path to the guest binary.
  • --post-deploy "echo override" ignores [run].post_deploy and runs only the override.
  • --no-post-deploy skips the post-deploy step entirely; the run prints the deployed-programs summary instead.
  • --post-deploy with --no-post-deploy errors at clap parse time with a cannot be used with message; exit code is non-zero.
  • A non-zero hook exit aborts the run with a clear post-deploy hook exited with status N message.

Failure Signals / Common Pitfalls

  • A run invocation that restarts the sequencer when one is already running healthy is a regression in the localnet-reuse path.
  • Hooks running with cwd somewhere other than the project root, or missing any of SEQUENCER_URL / NSSA_WALLET_HOME_DIR / SCAFFOLD_PROJECT_ROOT / SCAFFOLD_IDL_DIR, is a regression in the env contract.
  • $SCAFFOLD_PROGRAM_ID unset after a successful deploy on a single-program project with a vendored spel binary is a regression. Hint: lgs setup builds the spel binary; if it's missing, program_id: unavailable will also appear in the deploy summary.

Evidence to Capture

  • Console output of the first run showing the step headers and the deployed-programs summary.
  • Output of run after the [run] block is added, showing the ===> post_deploy[i/n]: markers and the resolved env values.
  • Output of run --post-deploy "echo override" showing only the override hook fires.
  • Output of run --no-post-deploy showing the deployed-programs summary instead of hooks.

L1. LEZ Template Bootstrap

Goal

Validate that the LEZ template scaffolds and reaches a ready-to-build state.

Preconditions

  • Latest scaffold binary has been built from the repo root.
  • Scratch workspace exists.

Commands / Actions

From the scratch workspace:

cd "$SCRATCH_ROOT"
"$SCAFFOLD_BIN" new dogfood-lez --template lez-framework
cd dogfood-lez
ls -d idl crates/lez-client-gen methods/guest/src/bin src/bin
"$SCAFFOLD_BIN" setup
"$SCAFFOLD_BIN" localnet start
"$SCAFFOLD_BIN" doctor
"$SCAFFOLD_BIN" build

The ls step verifies that LEZ-specific directories were scaffolded before proceeding with the build pipeline.

Expected Success Signals

  • Project creation succeeds with the LEZ template.
  • The generated project contains idl/, crates/lez-client-gen/, methods/guest/src/bin/lez_counter.rs, and src/bin/run_lez_counter.rs.
  • setup, localnet start, and doctor behave the same way they do for the default template.
  • build succeeds for the LEZ project workspace and also runs IDL generation and client generation automatically.

Failure Signals / Common Pitfalls

  • If the generated project is missing LEZ-specific paths such as idl/, crates/lez-client-gen/, or methods/guest/src/bin/lez_counter.rs, record that immediately.
  • If LEZ bootstrap behavior diverges from the default template in setup/localnet/doctor flows, capture the difference explicitly.
  • If build does not automatically trigger IDL + client generation for the LEZ template, record that as a regression.

Evidence to Capture

  • LEZ project creation output.
  • Directory listing showing LEZ-specific scaffolded paths.
  • setup, localnet start, doctor, and build excerpts.

Execution Notes

  • Keep LEZ runs separate from default-template runs. The template-specific directories and follow-up commands are part of the validation.

L2. LEZ IDL Regeneration

Goal

Validate that LEZ projects can regenerate IDL from the current project source.

Preconditions

  • LEZ project exists.
  • The LEZ project build environment is working.

Commands / Actions

From the LEZ project root:

"$SCAFFOLD_BIN" build idl
find idl -maxdepth 1 -type f -name '*.json' | sort

Expected Success Signals

  • build idl writes one or more JSON files under idl/.
  • Command output includes explicit Wrote IDL ... lines.
  • The regenerated files are valid JSON and match the current program surface.

Failure Signals / Common Pitfalls

  • If the command prints that IDL build is being skipped due to framework kind, the scenario is running in the wrong project.
  • Missing IDL marker output or empty IDL generation is a real regression for the LEZ template.

Evidence to Capture

  • build idl output.
  • Listing of generated files under idl/.
  • If relevant, a diff between pre-existing and regenerated IDL.

Execution Notes

  • Preserve the raw Wrote IDL ... lines. They make it much easier to diagnose partial-generation failures.

L3. LEZ Client Generation

Goal

Validate that LEZ client bindings can be regenerated from the current IDL set.

Preconditions

  • LEZ project exists.
  • build idl has been run successfully, either directly or via build client.

Commands / Actions

From the LEZ project root:

"$SCAFFOLD_BIN" build client
find src/generated -type f | sort

Expected Success Signals

  • build client reports that it is regenerating IDL before generating client code.
  • Client artifacts are written under src/generated.
  • The generated files reflect the current contents of idl/.

Failure Signals / Common Pitfalls

  • If build client does not refresh IDL first, record that behavior change.
  • Missing src/generated output or missing generator crate paths are LEZ-specific regressions.

Evidence to Capture

  • build client output.
  • Listing of files under src/generated.
  • Any diff in generated client code when the scenario is rerun after a program change.

Execution Notes

  • Treat generated client output as part of the scenario evidence, not as disposable noise.
  • When the generator fails, capture the exact manifest path and working directory that were used.

L4. LEZ Template Deploy and Counter Interaction

Goal

Validate that the LEZ counter program can be deployed and that the generated runner binary can invoke init and increment subcommands against the running localnet.

Preconditions

  • LEZ project exists with L1 completed (setup, build, localnet running).
  • wallet -- check-health succeeds.
  • At least one public account exists. If not:
"$SCAFFOLD_BIN" wallet -- account new public

Commands / Actions

From the LEZ project root:

"$SCAFFOLD_BIN" deploy
export NSSA_WALLET_HOME_DIR="$PWD/.scaffold/wallet"
cargo run --bin run_lez_counter -- init --to <account-id>
cargo run --bin run_lez_counter -- increment --counter <account-id> --authority <account-id> --amount 5

Expected Success Signals

  • deploy submits the lez_counter program and prints a success summary.
  • run_lez_counter init prints confirmation that the counter was initialized at the target account.
  • run_lez_counter increment prints confirmation of the increment operation.

Note: as of this writing, the LEZ counter runner contains TODO placeholders for actual transaction submission. If the runner only prints diagnostic messages without submitting transactions, record that as the current state. When transaction submission is implemented, update this scenario with account-state verification steps matching D6.

Failure Signals / Common Pitfalls

  • If deploy cannot find lez_counter in the discovered program list, record the actual discovered list.
  • If the runner panics on wallet initialization, NSSA_WALLET_HOME_DIR may not be set.
  • If the runner accepts the subcommand but does nothing (due to TODO stubs), record the output and note the gap.

Evidence to Capture

  • deploy output for the LEZ project.
  • run_lez_counter init and increment output.
  • Whether the runner actually submitted transactions or only printed placeholder messages.

Execution Notes

  • NSSA_WALLET_HOME_DIR must be set for the runner. Scaffold wallet commands set this automatically, but direct cargo run does not.
  • Keep LEZ interaction evidence separate from default-template interaction evidence.

E1. CLI Discoverability and Error Quality

Goal

Validate that the scaffold CLI provides consistent, non-destructive help and version output, useful error messages for unknown commands, and clear project-context errors when commands are run outside a generated project.

Preconditions

  • Latest scaffold binary has been built from the repo root.
  • A scratch workspace exists (for verifying that help flags do not create files).

Commands / Actions

From the repo root:

"$SCAFFOLD_BIN" --help
"$SCAFFOLD_BIN" --version
"$SCAFFOLD_BIN" help
"$SCAFFOLD_BIN" setup --help
"$SCAFFOLD_BIN" setup --wallet-install auto
"$SCAFFOLD_BIN" nonexistent-command
"$SCAFFOLD_BIN" build
"$SCAFFOLD_BIN" deploy
"$SCAFFOLD_BIN" doctor
"$SCAFFOLD_BIN" localnet status
"$SCAFFOLD_BIN" wallet list

From the scratch workspace (verify help flags do not mutate the filesystem):

cd "$SCRATCH_ROOT"
ls -la before_help_test > /dev/null 2>&1 || true
"$SCAFFOLD_BIN" create --help
"$SCAFFOLD_BIN" new --help
ls -la

Check that no new directories were created by the --help invocations.

Expected Success Signals

  • --help prints a usage summary listing all top-level commands.
  • --version prints the version string and exits.
  • help prints the same top-level usage summary as --help and exits successfully.
  • setup --help documents the setup workflow without a --wallet-install flag.
  • Legacy setup --wallet-install auto is rejected during argument parsing as an unknown argument.
  • nonexistent-command fails with an error and directs the user to --help or an equivalent corrective hint.
  • build, deploy, doctor, localnet status, and wallet list run from outside a project fail with a message like Not a logos-scaffold project ... Run logos-scaffold create <name>.
  • create --help and new --help do not create directories or files in the current working directory.

Failure Signals / Common Pitfalls

  • If create --help or new --help creates a directory named --help, that is a significant UX regression. Record it and the exact argv used.
  • If project-context errors are missing or unhelpful (e.g., a raw file-not-found instead of a scaffold-specific message), record the exact output.
  • If some subcommands support --help and others do not, document the inconsistency.
  • If setup --help still advertises --wallet-install, or the deprecated flag is silently accepted, record that as a command-surface regression.

Evidence to Capture

  • --help output.
  • --version output.
  • Error output for unknown command and out-of-project commands.
  • Directory listing before and after create --help / new --help to confirm no side effects.

Execution Notes

  • Run the create --help test in an isolated temporary directory so any accidental file creation does not pollute the scratch workspace.
  • Do not interpret missing --help support on a subcommand as a blocker. Record it as a finding and move on.

E2. Project Creation with Advanced Flags

Goal

Validate that create/new handle the --template, --vendor-deps, --lez-path (legacy alias: --lssa-path), and --cache-root flags correctly, including error cases for invalid inputs.

Preconditions

  • Latest scaffold binary has been built from the repo root.
  • Scratch workspace exists and is writable.

Commands / Actions

From the scratch workspace:

cd "$SCRATCH_ROOT"
"$SCAFFOLD_BIN" new dogfood-invalid-template --template nonexistent-template
"$SCAFFOLD_BIN" new dogfood-lez-explicit --template lez-framework
ls -d dogfood-lez-explicit/idl dogfood-lez-explicit/crates/lez-client-gen
"$SCAFFOLD_BIN" new dogfood-vendor --vendor-deps
"$SCAFFOLD_BIN" new dogfood-cache --cache-root "$SCRATCH_ROOT/custom-cache"
find "$SCRATCH_ROOT/custom-cache/repos/lez" -maxdepth 2 -mindepth 1 -type d | sort
grep -n "^\[wallet\]\|^home_dir\|^binary" dogfood-cache/scaffold.toml

Expected Success Signals

  • Invalid --template name fails with a clear error listing the available templates (default, lez-framework).
  • --template lez-framework creates a project with LEZ-specific structure (same as L1).
  • --vendor-deps is accepted without error and creates a project that vendors the pinned LEZ repo under .scaffold/repos/lez.
  • --cache-root is honored and scaffold uses the specified directory for cache operations, with non-vendored LEZ clones isolated by pin under <cache-root>/repos/lez/<pin>/.
  • Generated scaffold.toml includes [wallet].home_dir and does not include a deprecated wallet.binary field.

Failure Signals / Common Pitfalls

  • If an invalid template name silently falls back to default, record that as a regression.
  • If --vendor-deps or --cache-root are silently ignored or produce an error, record the exact output.
  • If --lez-path is tested and the path does not exist, verify the error message points to the bad path.
  • If non-vendored cache reuse collapses different LEZ pins into a single shared repos/lez checkout, record that as a cache-isolation regression.

Evidence to Capture

  • Error output for invalid --template.
  • Creation output for --template lez-framework with directory listing.
  • Creation output for --vendor-deps and --cache-root if tested.
  • Directory listing proving the pin-isolated cache path.
  • scaffold.toml excerpt showing wallet home config without a wallet binary field.

Execution Notes

  • Clean up the generated projects after this scenario to avoid consuming disk space with multiple scaffolded projects.
  • The --lez-path flag is optional to test here because it requires a real LEZ checkout. Only probe it if one is available.

E3. AI Skills Materialized Into Every Project

Goal

Validate that lgs new and lgs init both drop the canonical AI skill set into a generated project so that Claude Code, Cursor, and Codex pick them up without manual configuration. Skills are version-controlled in the generated project (no .gitignore exclusion).

Preconditions

  • Latest scaffold binary built from the repo root ("$SCAFFOLD_BIN").
  • Scratch workspace exists.

Commands / Actions

From the scratch workspace:

cd "$SCRATCH_ROOT"
"$SCAFFOLD_BIN" new dogfood-skills-default
"$SCAFFOLD_BIN" new dogfood-skills-lez --template lez-framework

mkdir dogfood-skills-init && cd dogfood-skills-init
"$SCAFFOLD_BIN" init
shasum AGENTS.md .claude/skills/lgs-cli/SKILL.md .cursor/rules/lgs-cli.mdc
"$SCAFFOLD_BIN" init   # re-init must succeed and not change skill content
shasum AGENTS.md .claude/skills/lgs-cli/SKILL.md .cursor/rules/lgs-cli.mdc

Inspect the generated layout in each of the three projects:

find dogfood-skills-default/.claude/skills dogfood-skills-default/.cursor/rules -type f | sort
find dogfood-skills-lez/.claude/skills dogfood-skills-lez/.cursor/rules -type f | sort
ls dogfood-skills-default/AGENTS.md dogfood-skills-lez/AGENTS.md dogfood-skills-init/AGENTS.md

Expected Success Signals

  • Every generated project (default template, lez-framework template, and init-adopted bare directory) contains exactly four .claude/skills/<name>/SKILL.md files: lgs-cli, lez-template, lez-framework-template, basecamp.
  • The same four skills appear under .cursor/rules/<name>.mdc.
  • AGENTS.md exists at every project root, lists all four skills with their descriptions, and links to .claude/skills/<name>/SKILL.md.
  • Re-running init on an already-migrated project succeeds (no longer bails) and prints AI skills refreshed under .claude/skills/, .cursor/rules/, AGENTS.md. The shasum output before and after a re-init is byte-identical for all three skill files.
  • .claude/skills/<name>/SKILL.md is byte-identical to the canonical source under <scaffold-repo>/skills/<name>/SKILL.md (run diff if validating against a built-from-source binary).
  • .cursor/rules/<name>.mdc frontmatter contains description: and alwaysApply: false, and does not contain a name: field. The body after the closing --- is identical to the SKILL.md body.
  • The generated .gitignore does not exclude .claude/, .cursor/, or AGENTS.md.

Failure Signals / Common Pitfalls

  • A skill missing from one of the three locations in any generated project is a regression — every project gets the same four-skill set per the v0.1 contract.
  • A .cursor/rules/<name>.mdc that still carries the name: line from the source SKILL.md is a regression in the frontmatter rewrite.
  • A re-init that errors with "already at schema" is a stale build — that bail was removed when skill refresh became part of init's contract.
  • A re-init that mutates skill content without a corresponding canonical-source change is a regression in idempotency.
  • Skills appearing in .gitignore is a regression — they are version-controlled by design.
  • Hand-edited team skills under .claude/skills/<other>/ that get clobbered by init are a regression — apply_skills only owns the four shipped names.

Evidence to Capture

  • File listings under .claude/skills/, .cursor/rules/, and the existence of AGENTS.md for each of the three project flavors.
  • One .cursor/rules/<name>.mdc head excerpt showing the rewritten frontmatter.
  • AGENTS.md excerpt showing the four-row table.
  • shasum pairs from the re-init idempotency check.

Execution Notes

  • This scenario does not require setup, localnet, or any network access — it validates only the materialization contract.
  • Pair with E2 when validating template-related changes; pair with B1 when validating init behavior alongside basecamp adoption.

B1. Basecamp Setup From a Module Project

Goal

Validate that a module project can fetch the pinned basecamp + lgpm binaries, seed the alice and bob profiles, and that re-running setup is idempotent.

Preconditions

  • Nix with flakes enabled.
  • Latest scaffold binary built from the repo root ("$SCAFFOLD_BIN").
  • A module project on disk whose flake.nix exposes packages.<system>.lgx (see "$SCAFFOLD_BIN" basecamp docs). Reachable as $MODULE_PROJECT.
  • scaffold.toml is present at the project root; if not, run "$SCAFFOLD_BIN" init once.

Commands / Actions

From the module project root:

cd "$MODULE_PROJECT"
test -f scaffold.toml || "$SCAFFOLD_BIN" init
"$SCAFFOLD_BIN" basecamp --help
"$SCAFFOLD_BIN" basecamp docs | head
"$SCAFFOLD_BIN" basecamp setup
ls .scaffold/basecamp/profiles
"$SCAFFOLD_BIN" basecamp doctor
"$SCAFFOLD_BIN" basecamp doctor --json
"$SCAFFOLD_BIN" basecamp setup

Expected Success Signals

  • basecamp --help lists setup, modules, install, launch, build-portable, doctor, and docs.
  • basecamp docs prints the canonical project-compatibility rules (mirrors docs/basecamp-module-requirements.md).
  • First basecamp setup clones the pinned basecamp repo into a pin-isolated cache path, builds basecamp and lgpm via Nix, seeds .scaffold/basecamp/profiles/alice/ and .scaffold/basecamp/profiles/bob/, and reports completion.
  • basecamp doctor reports the basecamp + lgpm binaries as present and both profiles as seeded; --json returns parseable JSON with the same checks.
  • Second basecamp setup is idempotent: pin unchanged → no rebuild reported, exit 0.
  • All commands run only inside the project; running them from outside the project must fail with the existing scaffold "not a logos-scaffold project" message.

Failure Signals / Common Pitfalls

  • Raw nix or lgpm stack traces with no scaffold-side hint are a UX regression — the setup-missing path is supposed to be a single one-line hint.
  • A setup re-run that rebuilds when the pin has not changed is a regression in idempotency.
  • Profile directories under .scaffold/basecamp/profiles/ missing after first setup is a fail.
  • If basecamp commands write to the user's global ~/.local/share/Logos/ or ~/Library/Application Support/Logos/, that is a severe regression — basecamp state is project-local under .scaffold/basecamp/.
  • If the basecamp binary lands on PATH, that is a contract violation.

Evidence to Capture

  • basecamp --help output.
  • First and second basecamp setup output (to compare rebuild vs. no-rebuild).
  • basecamp doctor and basecamp doctor --json output.
  • Listing of .scaffold/basecamp/profiles/.

Execution Notes

  • Do not pollute the user's home; basecamp setup must stay under <project>/.scaffold/basecamp/. If something writes outside that root, stop and capture it before continuing.
  • Pin-changed re-runs (rebuild path) are a separate validation; capture them when intentionally bumping the pin, not as part of this scenario.

B2. Module Capture, Install, and Single-Instance Launch

Goal

Validate the per-project source of truth for module identity ([modules] in scaffold.toml), the install pipeline that builds .lgx artefacts and loads them via lgpm, and a single-profile launch.

Preconditions

  • B1 completed in the same project.
  • Module project's flake.nix (root or one or more sub-flakes) exposes packages.<system>.lgx. Sub-flake projects (e.g., tictactoe-ui-cpp/, tictactoe-ui-qml/) are valid.
  • A graphical environment if you intend to actually drive the launched basecamp UI; launch itself does not require X/Wayland to start, but interactive validation does.

Commands / Actions

From the module project root:

"$SCAFFOLD_BIN" basecamp modules
grep -n '^\[modules\.' scaffold.toml
"$SCAFFOLD_BIN" basecamp modules --show
"$SCAFFOLD_BIN" basecamp install
"$SCAFFOLD_BIN" basecamp install --print-output
"$SCAFFOLD_BIN" basecamp doctor
"$SCAFFOLD_BIN" basecamp launch alice

If your project does not auto-discover correctly, capture explicit sources:

"$SCAFFOLD_BIN" basecamp modules --flake "./tictactoe#lgx" --flake "./tictactoe-ui-qml#lgx"
"$SCAFFOLD_BIN" basecamp modules --path /abs/path/to/prebuilt.lgx

Expected Success Signals

  • basecamp modules either auto-discovers project sub-flakes exposing .#lgx or accepts explicit --path / --flake sources and writes one [modules.<name>] sub-section per source into scaffold.toml. The file remains human-editable; re-runs are byte-identical and never overwrite existing keys.
  • For each captured project source, scaffold also resolves declared dependencies and inserts role = "dependency" entries unless the dep is already keyed, is a basecamp preinstall (capability_module, package_manager, package_manager_ui, counter, counter_qml, webview_app, basecamp_main_ui; see BASECAMP_PREINSTALLED_MODULES in src/constants.rs for the authoritative list), or is resolvable via the source's own flake.lock / the scaffold-default table.
  • An unresolvable dep fails fast with a targeted error naming the dep and the two user-side fixes (capture as a project source, or add [modules.<name>] with role = "dependency"); no silent drop.
  • basecamp modules --show prints the captured set without mutating state.
  • basecamp install builds each project source (sibling --override-input rewrites apply for path:../<sibling> inputs in multi-flake projects) and shells out to lgpm to install into both alice and bob. By default it logs to .scaffold/logs/<ts>-install.log and prints a one-line status; --print-output (or LOGOS_SCAFFOLD_PRINT_OUTPUT=1) streams nix output directly.
  • basecamp doctor reports each profile's installed modules matching the captured set; drift between [modules] and on-disk profile state is flagged, not hidden.
  • basecamp launch alice kills any prior logos_host / logos-basecamp descendants for that profile, scrubs the profile's XDG dirs under .scaffold/basecamp/profiles/alice/, reinstalls each captured source for that profile, sets XDG_{CONFIG,DATA,CACHE}_HOME plus LOGOS_PROFILE=alice, and execs basecamp.

Failure Signals / Common Pitfalls

  • A flake that exposes only .#lgx-portable and not .#lgx must fail explicitly with a hint pointing at --flake <ref>#lgx-portable for opt-in. Silent fallback is a contract violation.
  • Re-running basecamp modules overwriting an existing key is a regression — manual edits in scaffold.toml must win.
  • An unresolved transitive logos-module-builder input that fails without naming the missing follows is a regression.
  • install succeeding when a build or lgpm install step actually failed is a fail; exit codes must be non-zero on any source failure.
  • launch alice with an empty [modules] and without --no-clean must bail (rather than scrubbing the profile and leaving it empty).
  • Sibling --override-input not being applied at probe time would surface as a build that resolves the wrong sibling pin during basecamp modules auto-discovery; record any such mismatch with the exact derived module names.

Evidence to Capture

  • scaffold.toml excerpt showing [modules.<name>] sub-sections with flake, role, and (for project sources) the in-project relative path used.
  • basecamp modules --show output.
  • basecamp install log path under .scaffold/logs/ plus the printed one-line status, or the --print-output stream.
  • basecamp doctor output post-install.
  • The first lines of basecamp launch alice showing the kill → scrub → reinstall → exec sequence.

Execution Notes

  • basecamp modules is the sole automated writer of [modules]. If the user manually edited an entry, do not re-run basecamp modules mid-scenario without recording the pre-edit state — manual entries are intentionally preserved.
  • Only path:../<sibling> flake inputs are sibling-rewritten; path:./sub, github:, and git+ schemes pass through. If a project uses multi-line input declarations, the line-level parser may not detect them — record any sibling-override miss along with the offending flake.nix excerpt.

B3. Two-Instance P2P Dogfooding

Goal

Validate the canonical basecamp use case: two profiles running simultaneously on one machine and exercising p2p features (chat, delivery, storage) of the project's .lgx modules.

Preconditions

  • B1 and B2 completed in the same project.
  • basecamp install has captured at least one project source and produced a successful install for both alice and bob.
  • A graphical environment for both basecamp windows.

Commands / Actions

From two terminals, both rooted at the module project:

Terminal 1:

"$SCAFFOLD_BIN" basecamp launch alice

Terminal 2:

"$SCAFFOLD_BIN" basecamp launch bob

Within the running UIs, exercise whatever p2p surface the module exposes (chat exchange, delivery between peers, storage round-trip). Capture screenshots or short transcripts.

Expected Success Signals

  • Both basecamp windows open against their own profile dirs under .scaffold/basecamp/profiles/{alice,bob}/.
  • Each window shows the project's .lgx modules installed and ready.
  • LOGOS_PROFILE=alice and LOGOS_PROFILE=bob are visible in each respective process environment (helpful for debugging).
  • The two instances do not collide on Qt remote-objects or any non-module port; per-profile port-override env vars (per the spec) are set on each launch.
  • A p2p interaction triggered from alice is observable in bob (and vice versa) within the module's expected latency window.

Failure Signals / Common Pitfalls

  • Two windows opening but sharing identity keys, profile state, or message history is a clean-slate / XDG-isolation regression.
  • A non-module port collision (Qt remote objects, etc.) is a real finding — file upstream against the affected component, do not patch around it inside scaffold.
  • A module that does not honor an externally-provided port override is documented as a known gap pending an upstream fix on that module; capture the module name, the env var that should have worked, and the observed collision.
  • One window crashing while the other survives is recordable evidence; capture the crashing instance's logs from .scaffold/basecamp/profiles/<name>/ before relaunching.
  • Running basecamp launch alice twice in parallel is undefined in v1 — record the behavior if you trip it accidentally, but don't treat it as a supported scenario.

Evidence to Capture

  • The exact two-terminal command sequence used.
  • A short transcript or screenshot pair showing a p2p interaction propagating from one instance to the other.
  • The env block of each running process (e.g., tr '\0' '\n' < /proc/<pid>/environ | grep -E 'XDG_|LOGOS_').
  • Any port-collision error text verbatim, with the module that owns the colliding port.

Execution Notes

  • Do not start alice and bob from the same shell with & backgrounding unless you also redirect their logs; use two terminals for clean log separation.
  • If the underlying module surface is not yet wired for p2p between profiles, record the gap and the module's TODO state rather than declaring B3 a pass.

B4. Clean-Slate Verification

Goal

Validate that basecamp launch <profile> scrubs profile state by default and that --no-clean is the only path to preserve state across launches.

Preconditions

  • B2 completed (alice has captured modules and at least one successful install).

Commands / Actions

From the module project root:

"$SCAFFOLD_BIN" basecamp launch alice    # let it come up, then close it
ls .scaffold/basecamp/profiles/alice
mkdir -p .scaffold/basecamp/profiles/alice/.scaffold-xdg-data/scratch
echo "marker-$(date -u +%s)" > .scaffold/basecamp/profiles/alice/.scaffold-xdg-data/scratch/marker.txt
"$SCAFFOLD_BIN" basecamp launch alice    # default: scrub-and-reinstall
test -e .scaffold/basecamp/profiles/alice/.scaffold-xdg-data/scratch/marker.txt && echo "REGRESSION: marker survived clean launch" || echo "OK: marker scrubbed"
"$SCAFFOLD_BIN" basecamp launch alice --no-clean   # escape hatch: preserve state

Expected Success Signals

  • The default launch alice removes any user-introduced files under the alice profile XDG dirs and reinstalls each captured source before execing basecamp.
  • launch alice --no-clean skips the scrub and reinstall; pre-existing files in the profile survive.
  • rm -rf on launch is bounded to <project>/.scaffold/basecamp/profiles/<profile>/. Never any path outside that root.
  • A launch that finds no modules in [modules] and is invoked without --no-clean bails before scrubbing (the empty-install + scrubbed profile combination is the regression we're guarding against).

Failure Signals / Common Pitfalls

  • The marker.txt file surviving the default launch alice is a regression: clean-slate is the v1 contract.
  • --no-clean triggering a scrub anyway is an escape-hatch regression.
  • A launch scrubbing a path outside the profile's XDG dirs is a severe safety regression — capture the offending path and stop.
  • An empty [modules] plus a default launch that wipes the profile and leaves it empty is the exact bug guarded by fix(basecamp): bail on empty [modules] in launch without --no-clean; if you can reproduce it, that's a real regression.

Evidence to Capture

  • The marker write, the post-clean-launch listing, and the post---no-clean listing.
  • The exact path under which the marker was placed and the path basecamp scrubbed (verify they match the profile root).
  • Any unexpected paths touched by launch outside .scaffold/basecamp/profiles/<profile>/.

Execution Notes

  • Use a marker filename and timestamp you can search for after the fact; do not rely on visual inspection alone.
  • Clean-slate state is project-local; never test scrub behavior against the user's global Logos directories.

B5. Build-Portable Artefacts for AppImage Hand-Loading

Goal

Validate that project sources captured under [modules] with role = "project" can be built against their #lgx-portable flake output for hand-loading into a basecamp AppImage, and that runtime role = "dependency" entries are skipped.

Preconditions

  • B2 completed (project sources are captured and basecamp install has succeeded against .#lgx).
  • The same flakes also expose packages.<system>.lgx-portable.

Commands / Actions

From the module project root:

"$SCAFFOLD_BIN" basecamp build-portable
ls .scaffold/basecamp/portable 2>/dev/null || find .scaffold -maxdepth 4 -name '*lgx-portable*' -o -name '*.tgz' | sort

Expected Success Signals

  • build-portable builds .#lgx-portable for each role = "project" entry in [modules], in dependency order, and writes / symlinks the resulting artefacts under .scaffold/.
  • role = "dependency" entries are skipped — the target AppImage provides its own copies.
  • A flake that does not expose .#lgx-portable fails with a targeted error naming the missing attribute, not a raw nix trace.

Failure Signals / Common Pitfalls

  • A build-portable that silently falls back from .#lgx-portable to .#lgx is a contract violation — the variant choice is the user's.
  • Building dependency entries (those with role = "dependency") is wasted work and a behavior regression.
  • Out-of-order builds that ignore the dependency graph between project sources are a regression introduced by changes to ordering logic.

Evidence to Capture

  • basecamp build-portable output excerpt including the per-source build lines.
  • The directory listing of the produced artefacts under .scaffold/.
  • For any failure, the exact missing flake attribute and the offending project source.

Execution Notes

  • This scenario does not exercise the AppImage itself — it stops at producing artefacts. Hand-loading into a basecamp AppImage is owned by the AppImage release, not by scaffold.

Minimum Rerun Guidance for Future Changes

  • Changes to onboarding, project creation, setup, localnet, or build flows: rerun D1, D2, and D6.
  • Changes to deploy behavior or deploy output formatting: rerun D3 and D6.
  • Changes to wallet flows or wallet-related defaults: rerun D4.
  • Changes to diagnostics, report contents, or redaction logic: rerun D5.
  • Changes to example runner binaries or template src/bin/* code: rerun D6.
  • Changes to run step ordering, post-deploy env vars, post-deploy CLI override flag handling, or [run] config parsing: rerun D7.
  • Changes to LEZ template scaffolding or generated outputs: rerun L1, L2, L3, and L4.
  • Changes to CLI argument parsing, help text, or error messages: rerun E1.
  • Changes to create/new flags or template selection logic: rerun E2.
  • Changes to AI skill materialization (apply_skills, the canonical skills/ source, frontmatter rewrite, AGENTS.md template, or init re-run semantics): rerun E3.
  • Changes to basecamp setup (pin sync, lgpm build, profile seeding, idempotency) or basecamp doctor: rerun B1.
  • Changes to [modules] derivation, dependency resolution, sibling --override-input handling, or basecamp install invocation of lgpm: rerun B2.
  • Changes to basecamp launch (kill-and-scrub semantics, XDG isolation, port-override env vars, p2p surface): rerun B3.
  • Changes to clean-slate / --no-clean semantics or the empty [modules] guard on launch: rerun B4.
  • Changes to basecamp build-portable (project/dependency role split, ordering, attr selection): rerun B5.

When in doubt, rerun more scenarios rather than fewer.