Add a `seed-demo` subcommand (cmd/seed_demo.go + internal/demoseed/) that inserts a fixed reference catalog into the member-console DB so the operator panel has rows to walk through for UX research. Catalog: 6 demo-* products (4 by product_type + 2 extra plans), 1 plan ladder with 3 tiers, 1 entitlement set with 2 rules (limit + boolean), and 1 grant on Alice's personal org (once she's logged in). Person rows for bob/carlos/diana are seeded via provisioning.AutoProvision keyed on the pinned Keycloak UUIDs — no longer fragile now that seed-keycloak.sh's partialImport switch preserves the pinned id. Alice is intentionally NOT pre-seeded so the lazy-creation OIDC flow stays exercised on every fresh stack. Idempotency: list+filter by `name` for catalog rows, by `oidc_subject` for persons. Re-running is a no-op for created rows; warns + skips the grant if alice hasn't logged in yet. Host-side invocation only — run `./test/seed-demo.sh` after bootstrap-stack.sh. Mirrors how member-console itself runs on the host (config + secrets live under test/). Two OpenSpec changes folded in: 2026-05-10-member-console-demo-seeder (the seeder) and 2026-05-11-demo-seeder-persons (the persons follow-up unlocked by the Keycloak fix).
4.9 KiB
test/ — instructions for LLM agents
If you are an automated agent working in this repository, read this before starting Docker. The test stack uses fixed default host ports; multiple worktrees running it concurrently will collide unless each generates its own isolated configuration.
All commands in this doc assume your working directory is test/ —
that's where compose, the env file, and the binary's expected secret
paths all resolve from.
Required first step in any worktree
cd test
./bootstrap-stack.sh
The script:
- Derives a slug from this worktree's directory name (override with
./test/bootstrap-stack.sh my-slug). - Allocates a deterministic port slot via
FNV-32(slug) % 200. Probes for collisions and advances if any candidate port is already bound on the host. - Copies
test/secrets/from the main worktree if missing locally (this directory is gitignored, so fresh worktrees do not inherit it). - Writes
test/.envwithCOMPOSE_PROJECT_NAME, host port assignments, andMC_*env vars that overridetest/mc-config.yamlvia Viper. - Prints the URL/port table the developer should use.
- Idempotent: re-running with
test/.envalready in place is a no-op that reprints the summary.
After bootstrap
docker compose up -d
set -a; . .env; set +a
go run .. start --config mc-config.yaml
Both keycloak-seed (creates realm users) and fedwiki-render
(resolves real KC UUIDs and templates seed/fedwiki/*.tpl into
testdata/fedwiki/) are compose services that run automatically as
one-shots on up. fedwiki waits for both to complete before
starting. Keycloak silently regenerates the id field on POST /users,
so FedWiki identity cannot be pinned at commit time — it's rendered
after each realm seed.
The member-console binary must run with test/ as its working directory
because mc-config.yaml references secrets/stripe-* via relative paths.
Compose auto-loads .env from the current directory, so no
-f/--env-file flags are needed.
Use the URLs printed by bootstrap, not the defaults from
test/mc-config.yaml — the YAML still says port: 8081, but the running
process listens on whatever MC_PORT is in test/.env.
Walking the operator UI with data
The default workflow produces a clean, empty environment — good for fresh-user
flows. To populate the operator panel (products, plan ladder, entitlement set,
one active grant) for UX walkthroughs or operator-panel exploration, run the
opt-in demo seeder from the repo root after bootstrap-stack.sh:
./test/seed-demo.sh
Idempotent — re-running is a no-op. Person rows for bob/carlos/diana are not
seeded (Keycloak doesn't honor pinned sub IDs); log in once as each demo
user to populate them. See
seed/member-console-demo/README.md for
the catalog and rationale.
Tearing down
./teardown-stack.sh
Brings the compose stack down with volumes, removes test/.env,
test/secrets/, and test/testdata/. Returns the worktree to a clean
state for the next experiment.
Keycloak user seeding
seed/keycloak/seed-keycloak.sh creates users via POST /admin/realms/{realm}/partialImport, not POST /admin/realms/{realm}/users. Keycloak 26.x silently drops the id field on the latter (server-side ID assignment for least-privilege admin operations); partialImport preserves pinned IDs and is the supported escape hatch. See OpenSpec change 2026-05-11-keycloak-id-pinning-fix for the empirical confirmation and design rationale.
Temporal namespace
The compose stack sets SKIP_DEFAULT_NAMESPACE_CREATION=true on the
temporal service, so a one-shot temporal-seed service registers the
default namespace via the internal frontend on first up. If
member-console fails with Namespace default is not found, the seed
service didn't run yet — docker compose ... up -d temporal-seed and
re-check docker compose ... logs temporal-seed.
Constraints
- Do not run Stripe webhook scenarios in parallel across stacks. All
stacks share the same Stripe test account via
test/secrets/stripe-*. Webhook delivery to one stack is global to that account; running two stacks through webhook flows simultaneously gives undefined results. - Do not edit
test/.envby hand. Re-run bootstrap (after deleting the file) if you need to change ports. - Do not remove the
${VAR:-default}fallbacks fromcompose.yaml. They preserve the single-stack default workflow when no.envis present.
What bootstrap-stack.sh writes to test/.env
COMPOSE_PROJECT_NAME=<slug>
SLOT=<0..199>
POSTGRES_PORT, VALKEY_PORT, KEYCLOAK_PORT,
TEMPORAL_PORT, TEMPORAL_UI_PORT, FEDWIKI_PORT
KC_HOSTNAME, MC_BASE_URL, TEMPORAL_UI_URL
MC_PORT, MC_DB_DSN, MC_VALKEY_ADDR,
MC_OIDC_IDP_ISSUER_URL, MC_TEMPORAL_HOST,
MC_TEMPORAL_OAUTH_TOKEN_URL
Compose reads the first three blocks. Viper reads the MC_* block via the
existing SetEnvPrefix("MC") + AutomaticEnv() configuration in
cmd/root.go.