Files
member-console/test/AGENTS.md
Christian Galo 9d11ee0328 Add member-console demo seeder with person seeding
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).
2026-05-11 10:55:14 -05:00

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/.env with COMPOSE_PROJECT_NAME, host port assignments, and MC_* env vars that override test/mc-config.yaml via Viper.
  • Prints the URL/port table the developer should use.
  • Idempotent: re-running with test/.env already 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/.env by hand. Re-run bootstrap (after deleting the file) if you need to change ports.
  • Do not remove the ${VAR:-default} fallbacks from compose.yaml. They preserve the single-stack default workflow when no .env is 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.