Files
member-console/status/milestones.md
T
cgalo5758 71818de0bd Add setup checklist and empty-state guidance
Implement the ux-first-run change: a state-derived setup checklist on
/operator/setup with a landing region that recedes once required steps
are done, and empty states that distinguish blocked from empty across
operator and member surfaces. Also add production deployment and
environment reference docs, plus a config-key completeness test.
2026-08-23 03:06:11 -05:00

30 KiB
Raw Blame History

Milestones

Mirrors Gitea milestones — title, description, status. Detailed phase breakdowns for completed work live in openspec/changes/archive/ (per-change artifacts) and archive/milestones-completed.md.

Completed

# Name Description
1 Governance spine Identity + organization. Person → org → workspace with auto-provisioning.
2 Grant-based FedWiki access Entitlements + minimal billing. Admin grants → numeric site limits.
3 UI/UX alignment Progressive disclosure, operator tools, validation, entitlement/product management.
4 Stripe Integration Stripe as billing execution engine: schema, webhook pipeline, customer/product/subscription/invoice sync, operator UI.
5 Public Tier Free FedWiki entry tier — signup → personal org → default-entitlement site (1 site, 16 MB); members see entitlements & usage; public products discoverable.
6 Plan Management Foundation First-class plan ladders + supersession (end-and-re-apply, no dormant), the generic Transition primitive, pool_provision_transitions audit, and operator ladder / auto-provisioning / enrollment UIs.
7 Operator UX Foundation Operator panel reworked SPA→MPA: information architecture + route hierarchy, design system (WCAG 2.1 AA), form/action conventions, and a member-console lint dead-reference / convention guard.
8 Standard Service Public→Standard (Stripe Checkout upgrade) and Standard→Public (cancel downgrade, clamp policy); member plan catalog, ladder-aware switch mechanics (paid→paid / paid→free), operator plan-topology view.
9 Integration Architecture Integrations as registered trees behind an explicit contract: registry-driven routes/workers/config/UI/migrations, per-stream schemas and goose ledgers, Discourse as the proving second integration (live commerce loop: checkout → entitlement → forum group), generic member-dashboard cards, runtime-managed integration settings.

Per-phase detail for M5M9 is in archive/milestones-completed.md.


Active

Milestone 10 — Open-Source / Public Launch Readiness

Serves: open-source launch (primary) + member-console features; 10d also enables the FedWiki Service Standard tier.

The "make it presentable" milestone: take the feature-complete engine (M1M9) and make it safe and unconfusing for a stranger's first contact, so the member console can be published as an open-source project with its own website. The substance is built; this milestone is the front door, the first-run experience, and the one or two feature gaps that make the operator happy-path actually completable. Most items are already enumerated in status/issues.md; this milestone organizes them into a launch goal.

Phase Status Scope
10a Project front door Done README (what/why, quickstart, architecture, honest "pre-production" framing), CONTRIBUTING.md, SECURITY.md, LICENSE, and a docs reorg-by-audience all shipped. Operator-dashboard screenshot captured 2026-07-27 (docs/images/operator-overview.png, wired into the README) from mechanically-honest generated sample data — reversible seed/nuke pair at test/mockshot-seed.sql / test/mockshot-nuke.sql.
10b First-run friction Done Shipped + archived via boot-config-validation (fail-fast config validation), temporal-first-boot-retry (compose health-ordering + client-side connect retry — the race was Temporal vs. its own DB, not just JWKS), keycloak-dedicated-test-realm (app OIDC moved off Keycloak's master admin realm onto a dedicated app realm (now named test) — account-console error fixed, live-verified), test-stack-hygiene (test-only secret labeling; untracked the unused realm export), and member-console-init-scaffold (init writes a usable starter config). The "looks broken on first run" cluster.
10c Purchasability completion Done Shipped + archived 2026-07-03 (four changes, specs folded): purchasability-sync-completion (truthful Stripe-configured gate, failed/dead-letter state + retry, live-poll readiness card, "Payment processing" label), multi-price-support (default-price model, per-row sync/make-default/deactivate, price-aware product-idempotent sync), ladder-tier-append, and db-error-translation. Residual: runtime browser verification of the multi-price make-default→sync-without-duplicate path and the ladder-append flows is left for maintainer confirmation (stack running); behavior is unit/render-tested and the schema is confirmed live.
10d Custom domain support Code done, ops pending Shipped 2026-07-24/25 in three slices (exploration): Slice 1 custom-domain-verification (entitlement gate, TXT challenge, verification UX — 4924784); Slice 2 domains-registry (8d05934) — the core internal/domains module owning disjoint claims + generic placements, verify-once-place-freely, /domains/ask moved to core with the domains-ask-fallback-url strangler seam, reserve-then-bind create saga, boot reconciliation/adoption, domains.name_rules; Slice 2.5 lifecycle hardening (c85ac6a, c4bb1ba) — 24h pending window (was 7d), abandonment ledger with sticky evidence latch, per-workspace initiation budget, system-cancel exemption, expiry sweep on boot + 15m Temporal schedule, carve guards bound to every caller. Live-verified end-to-end against real DNS. Remaining: Slice 3 (ops, not code) — deploy to prod, seed the registry from the census, cut the ask label over via the fallback seam, retire mkdir-as-domain-management, settle grandfathering over quota. Two follow-ups filed in issues.md (no tombstone on released hosted names; subtree exclusivity ignores DNS delegation for BYO domains). Design notes: model-card-notes-domains.md. Original scope — custom domains as an entitlement-gated capability: a boolean "allow custom domains" rule on a plan's entitlement set gates the affordance, plus the DNS + SSL story that makes it demo-able end-to-end. Folded out of the metering milestone — credible co-op hosting needs members' own domains at launch, and the FedWiki Service Standard tier lists it (spec). Scoped 2026-07-03: the checkbox branch is already wired end-to-end (handler → Temporal → farmmanager → fedwiki.sites.is_custom_domain) but wholly ungated, and entitlement_set_rules already permits rule_type='boolean' — the gap is the materializer (skips non-limit rules), a materialized boolean store + read query, the rule-authoring form (hardcodes limit), and the create-site gate. Resolves audit finding #5 (the dead-end checkbox — hidden by 10h until this lands).
10e Hardening & polish Done Shipped 2026-07-31 (change hardening-polish): container runs as non-root (uid 65532, no runtime writes); styled 404/500 pages in the page chrome, wired at the router catch-all (which previously served the dashboard for any unknown path) and the panic-recovery middleware, with HTMX partials keeping text errors for the toast contract; operator/member heading hierarchy re-leveled across 29 templates (every page a real H1→H2→H3 outline, zero h5/h6 tags, visuals pinned via Bootstrap .hN classes, older pages gain the newer pages' visible H1 title); autocomplete on all forms + text inputs (Chrome flags cleared); cosmetics sweep (revoke-confirm empty-name fallback, truthful full-ladder tiers alert, catalog feature labels resolve resource_keys.display_name via the entitlement view's resolver); test-stack Keycloak client secrets re-valued to self-labeling TEST-ONLY- strings (extends 10b's test-stack-hygiene); /login made idempotent (unconsumed state reused <5 min — closes the recurring state-mismatch class structurally); extractRoles merges all four claim locations incl. client-scoped resource_access (union, deduped); CSRF-secret rotation procedure documented in docs/hosting.md. Also fixed en route: nil-logger SIGSEGV in logging.FromContext pre-initialization. Verified: build/vet, unit+render tests, full DB-backed suite, live-stack browser walkthrough (fresh-seeded Keycloak, operator login, heading outlines, styled 404, catalog labels). 13 issues closed to archive; trimmed scopes filed as follow-ups (IdP-agnostic role mapping, dual-key CSRF rotation).
10f Presentable operator dashboard Done Shipped 2026-07-25 (56a743f queries, 4cec386 landing; change operator-overview-landing): /operator rebuilt as a four-region overview — hero lookup, at-a-glance stat tiles (sqlc-backed, per-source em-dash degradation, provisions-not-ledger delivering count), unified activity timeline, registry-driven System rail with outbox pending/retrying/dead-letter. Shell brand-neutralized. Built via a two-model experiment (Opus vs Kimi K3 in parallel worktrees; Opus merged, 10f-kimi branch kept as reference). Post-merge polish shipped 2026-07-26 (change operator-overview-polish): domain-claims tile swapped for open invoices (receivables count — domains de-centered from the IA, headline row reserved for core capability state per the spec delta), shadow/hover card styling replaced with the app's plain bordered-card idiom, and the tile's dead plumbing removed (operator DomainsQ, CountClaimsByLifecycle). Metric refinement shipped 2026-07-27 (change overview-money-and-teams): Organizations → Team organizations (non-personal, non-reserved — personal orgs track the people count by convention and read as an error), Subscriptions → Monthly recurring (monthly-normalized money, largest currency bucket, subscription counts in the caption; FX conversion filed in issues.md), captions reworked em-dash-free (People 30-day trend, invoices outstanding balance), and the Delivering grants headline narrowed to deliberately issued grants with the signup-default mass in the caption (the actionable set — conferral mints a default per org). Live-verified on the test stack.
10g First-contact UX pass In progress Re-walk the whole app from a stranger's seat: progressive disclosure and learn-by-clicking IA so a newcomer can orient without a guide. Prior UX milestones (M3, M7) polished piecemeal; this one takes the newcomer's perspective as the explicit goal. The 2026-07-02 adversarial audit partly pre-empts this — 10h remediates its findings; 10g re-walks after. Evidence phase DONE 2026-08-22 per the new first-contact-ux-process.md (v1): 5 naive walkthroughs + 2 outside models (Kimi K3, DeepSeek) + 48-screen dual-state sweep + 8 model-card honesty audits + 3 independent heuristic reviews + code verification of the headline mysteries (3 of 4 were environment artifacts). Synthesis: ux-walks-2026-08/findings-2026-08.md, 19 findings UX-1…UX-19, four sev-4 (no first-boot door; phantom entitlement-set selection on product edit; grant delivery-state told three contradictory ways; operator lists unusable at scale). Maintainer triage 2026-08-22: fix everything inside M10; realm branding reframed to a configurable deployment name; slugs-eradication issue filed. Five changes planned, in order: ux-honest-surfaces DONE + archived 2026-08-23 (see findings' Change 1 status: 7 findings resolved in full, 5 partial, honest surfaces across both panels) → ux-first-run DONE 2026-08-23 (implemented + verified, uncommitted; see findings' Change 2 status: state-derived setup checklist at /operator/setup + landing region, blocked-vs-empty overhaul on the eight bare screens, point-of-blockage disclosures, topology nothing-to-validate state, billing Stripe-unconfigured branch, production-deployment guide + environment reference + README first-run order; full suite + e2e green, empty-DB browser walk end to end) → ux-ia-naming (design-first: fold Plan Ladders/Topology/Entitlement Sets into Products, Org Types into Organizations, drop the second sidebar group; Enrollment renamed; canonical vocabulary) → ux-operator-scaledrop-entity-slugs. Acceptance = fresh walkers re-run the missions; sev-3/4 friction must not recur.
10h Audit remediation Done Execute the triaged 2026-07-02 audit findings (triage): 43 quick-fixes, 4 de-advertisements (trial days, Replace stacking, both dead Active toggles — stop implying half-cooked affordances exist), the interim hide of the custom-domain checkbox (real remedy is 10d), and 7 interim guards for structural-defer findings. Blockers first (25). The 7 structural real fixes are NOT in scope — they gate on 10i model cards. Verified via build/vet/lint + full DB-backed suite + operator-walkthrough browser e2e against the live stack; two adjacent defects fixed en route (timeout-middleware process crash, test-harness migration numbering — see issues.md). Committed 2026-07-03 (8514df5 remediation, fa420ee org-default fix); same-day follow-up: the ladder Active toggle went from a "not yet enforced" badge to fully hidden, and the 11 postponed items are inventoried as deferred-remediation debt in issues.md.
10i Model cards Done Write the model catalog (inventory): one canonical card per domain model in docs/models/, indexed by a docs/models/README.md catalog page linked from the docs front door, in the recommended order — product-kind, payments/billing, plan-transitions, entitlements, resource-pools, provider/integration, identity/workspace. Each card pins purpose/invariants/dimensions/edge-cases/open-questions, citing design/ + membcons-db decision docs. Cards are descriptive (code-as-built vs design) and are written in this repo; when a card surfaces an open question that needs a new normative decision (e.g. the addon structural signal, payments dimensions), that decision is design work done in membcons-db — the card then updates to cite the resulting decision doc (the doc-35 pattern). Unblocks the 7 deferred structural fixes and de-risks M13. Done 2026-08-21 (model-cards change): eight cards (domains-registry added; "product-kind" renamed product-catalog after Doc 41) + index in docs/models/, each code-verified by adversarial fact-check (~5389 claims/card) and cold-read newcomer tests; per-card drift ledgered under "Model documentation" in issues.md; the run also produced the Doc-39 conformance check (doc39-conformance-2026-08-21.md) that ratified upstream Decisions 129133, closing the design record through Decision 141 (data-model v16 synced). Verification surfaced new launch-relevant bugs, triaged into M10 — see the M10 addendum below and the ledgers.
10j Verification-found hardening Done All three changes shipped 2026-08-22; phase closed. Fix what the 10i verification passes surfaced, sequenced before 10g so the first-contact re-walk isn't spent rediscovering known defects (the same ordering that put 10h before 10g). Triaged 2026-08-22 after a per-issue deliberate-vs-oversight audit: nine of the eleven Tier 2 gaps are confirmed oversights, two (rank-0 signup fragility, env enum validation) are recorded deferrals whose follow-on was already named — none is a blessed design position. All detail citations live in the issues.md model-documentation ledgers. Three small changes matching the triage tiers: (1) purchase-path-blockers — DONE 2026-08-22 (implemented + verified, uncommitted; archive with the landing commit): member catalog queries filter lifecycle_status = 'published' and checkout re-checks the shared member gate before any Stripe call, with a current-rung carve-out so an org enrolled on a now-unpublished tier keeps seeing its plan; the promised core.webhook_events partition creator now exists (internal/db/partitions.go, boot pass + 24h webhook-partition-ensure Temporal schedule, current + 3 months ahead); the Stripe receiver answers 500 when the event insert fails so Stripe redelivers (Discourse's receiver already did). Verified: full DB suite green, live walkthrough (draft hidden from /products, direct checkout POST 400s pre-Stripe, publish flip appears; boot created the month-ahead partition and registered the schedule). Cards updated (product-catalog inv 11; payments-billing inv 12/14 + map); three ledger entries resolved. (2) schema-hardening — DONE 2026-08-22 (implemented + verified, uncommitted; archive with the landing commit; maintainer arbitrations 2026-08-22 folded in): migration 00010 carries the schema seatbelts — one-default-pool-per-org and one-primary-per-workspace partial unique indexes (with a dedup pre-flight), pool_type CHECK, core.providers status CHECK + suspended_at/retired_at (doc-44 convergence), core.subscriptions.status CHECK over Stripe's full eight-value vocabulary (design-feedback note filed: the designed list lacks incomplete_expired). App guards: workspace creation via a shared transactional provisioning routine (no more false success); ExtendGrant multi-pool refusal + org/default pool ownership check; rank-0 both guards (last-tier delete refused on live org-type defaults; plan-less signup fallback with an error alarm); Slug()/manifest boot assertion; ValidateStart enum enforcement for env-sourced config shared with the override validator; checked int64→int32 conversions in the invoice projection (plus an executor-found live bug fixed: ProcessWebhookEvent's error wrapping silently made every non-retryable Temporal error retryable); domains cancel runs a strict fresh evidence probe with retries before stamping abandonment; stacking authoring restricted to additive-only (selector removed; homogeneity/tie-breaker work replaced by the "Stacking policies need a design exploration" issue). Verified: full DB suite + e2e walkthroughs green, migration applied live, all three constraints psql-rejected live, rule form checked in the browser; 12 ledger entries resolved/updated, 2 filed, 7 model cards updated. (3) model-doc-rot — DONE 2026-08-22 (uncommitted; archive with the landing commit): plan-architecture.md and grant-plan-safety.md retired rather than rewritten — both were model description now owned by the plan-ladders card, and the retire-don't-duplicate rule is written into the model-catalog spec; the 2026-04 incident narrative preserved at status/archive/incident-2026-04-grant-provision-sync.md; plan-management.md corrected in place (conferral boundary, real audit vocabulary, restore-records-initiate); identity-provider-setup.md role extraction now documents the four-location union; stripe.md sync-status claim fixed; dead code deleted (EnsureStripeCustomer function with its payload struct kept for the outbox consumer; two unused placement queries); the reconcileSite and 00012 comments corrected; the Makefile sqlc-generate target now loops the nine real configs — and its first honest run surfaced + folded in the internal/integration regeneration that migration 00010 had left behind. Nine ledger entries resolved. Explicitly deferred beyond M10, staying in the ledgers: rule-change re-materialization fan-out (design work), domains schema constraints and identity status vocabularies (upstream decisions), the ask-200 ambiguity (retires with 10d Slice 3).

Remaining for M10 (what's missing): Done → 10a, 10b, 10c, 10e, 10f, 10h, 10i (2026-08-21). Code done, ops pending → 10d (all three code slices shipped; only Slice 3, the adoption/ops cutover, is left). 10j Done (2026-08-22) — all three changes shipped and archived (purchase-path-blockers, schema-hardening, model-doc-rot; see the 10j row). In progress → 10g, now the sole code phase left: evidence + triage done, changes 1 (ux-honest-surfaces, archived) and 2 (ux-first-run) of five shipped 2026-08-23; ux-ia-naming is next and is design-first. Carried-forward debts, tracked in issues.md: the 10c residual (maintainer live-verify of multi-price/ladder flows) and the 10h deferred-remediation items (4 de-advertisements; the 7 interim guards' card gates settled by 10i). The headline 10j item, maintainer-triaged 2026-08-21: the draft-product checkout bug — the member path never checks lifecycle_status, so a draft that is active+public+priced+mapped can be bought, with only conferral rejecting it after payment (found by the 10i model-card verification pass; full citations under "Product-catalog card: drift & trap ledger" in issues.md).

MVP Checkpoint: A newcomer can read the README and understand what the member console is in under a minute, run the stack from a documented quickstart without hitting a boot failure that looks like a bug, land on an operator dashboard worth showing off and find their way around it without a guide, complete the operator create-product → price → sync → sellable path end-to-end, and (Standard tier) attach a custom domain. The repo carries the standard OSS hygiene files, the audit's 25 publish-blockers are resolved or mitigated (10h), and every visible affordance does what it says — half-cooked features are hidden, not hinted. At that point it is presentable to the open-source community.


Proposed

Renumbered 2026-07-03 so milestone numbers match execution order: Launch Readiness 12→10 (phases 12a12i→10a10i), Audit Log 10→11, REST API 11→12; M13 unchanged. Documents under archive/ and log/, and M7-era walkthrough evidence under docs/, are historical records and keep the old numbers. The sections below are in expected take-up order: M11 (audit), M12 (API), then M13 (metering), as capacity and need dictate. Each milestone's Serves: line names its driving roadmap(s) — see the four-roadmaps section in strategy.md.

Milestone 11 — Audit Log & Observability

Serves: member-console features + design affordances (the audit module in design/).

"Who did what when, and how is the system behaving while they do it." Delivers the generic audit.log that M6a's pool_provision_transitions was designed as a view-shaped projection of; supersedes the Backlog "Audit trail" and absorbs the M7-7a action-telemetry deferral.

Phase Status Scope
11a Audit-log schema Proposed Generic table vs typed projections vs union view — the load-bearing decision; record shape, retention, PII checklist.
11b Auto-instrumentation middleware Proposed One audit row per mutating route (actor, target, status, request id).
11c Observability stack Proposed Metrics surface (latency, error rate, queue depth) + on-call dashboards; stack TBD.
11d Operator audit UI Proposed Per-resource history views, built on the M7 design system (generalizes M6e).
11e Retention, PII, alerting Proposed Per-category retention, payload PII scrub, signal alerting.

Milestone 12 — Public REST API

Serves: member-console features.

A stable, versioned /api/v1/* surface for every privileged mutation, independent of the operator HTMX UI (which stays as-is). For external automation, CLI tooling, the demo seeder, and future integrations. An API is a permanent contract — auth model, error envelope, versioning, and idempotency are picked deliberately in 12a before the surface expands.

Phase Status Scope
12a API design & auth model Proposed REST + OpenAPI; token vs OAuth client-credentials; error envelope (likely RFC 7807); pagination / idempotency; ship /api/v1/health only to validate.
12b Catalog endpoints Proposed Org types, products (+ Stripe mapping), entitlement sets (+ rules), plan ladders (+ tier-rank reorder).
12c Runtime endpoints Proposed Persons, organizations, grants (both paths), per-org composite.
12d Billing & integrations Proposed Read-only over Stripe mirror state; integration endpoints (FedWiki; shape informed by M9).
12e OpenAPI publication Proposed docs/api/openapi.yaml in CI; first generated Go client used by the demo seeder.
12f API audit, rate limits, retention Proposed Integrate M11 audit; per-principal rate limits; CSP/CORS lockdown.

Relationship to M9: M9 is the contract for an integration plug-in; M12 is the contract for a programmatic caller. Different audiences — not conflated.

Milestone 13 — Usage Metering & Time-Tracked Services

Serves: member-console features + design affordances. Explicitly NOT required for the FedWiki Service launch — the service thread's latest word (post #16, 2025-11-23) proposes dropping bandwidth limits entirely, since no metering mechanism exists and raw VPS capacity dwarfs the stated limits.

Usage ingest + enforcement + billing over the M9-namespaced resource keys, plus time-tracked/hourly services. Measurement itself (bytes on the wire, disk consumed) is provider/infra scope — member-console cannot observe it and only ingests reported usage. Absorbs M8c's storage-compliance split-out: storage becomes a metered resource alongside network egress (storage_bytes exists in the model but is unmetered/unenforced today). Down the road — not near-term. (Custom-domain support moved to M10.)

Phase Status Scope
13a Usage ingest & alerts Planned Ingest provider/infra-reported usage per site/org; expose usage; threshold alerts.
13b Limit enforcement Planned Block/throttle over-limit; configurable grace periods (per plan/policy).
13c Time-tracked / hourly service billing Planned Time-tracking; hourly billing; add-on management UI.
13d Usage-based billing Planned Metered subscriptions; pending charge → InvoiceItem overage sweep.

Open (deferred): whether a metered resource can be scoped to an arbitrary subset of providers (e.g. storage pooled across {FedWiki, NextCloud} but not Discourse) rather than one-or-all. Needs a grouping construct, not a column tweak — M13 either accommodates subset scoping or ships a flat scalar for v1. See issues → "Pooled metered resources across arbitrary provider sets."


Backlog

Capabilities we want but don't yet fit into a milestone. Items graduate to milestones when triggered by need or capacity.

Capability Why We Want It Current Thinking
FedWiki↔Discourse full parity M9 demoted FedWiki onto the integration registry; a 2026-07-31 audit inventoried the residue. Delivered 2026-08-01 in two slices (issue archived to archive/issues-resolved.md): integration-config-parity (core domains-connect-target key, typed ConfigSpec, generated init scaffold) and test-stack-integration-profiles (fedwiki chain + Caddy profile-gated, COMPOSE_PROFILES composition knob defaulting to core-only, presence guards across seed/teardown/walkthroughs). Still distinct and open: fedwiki's direct internal/domains import (parked interdependence exploration).
Forum integration Public tier has "community-only" support (read, no post); Standard tier unlocks posting rights. Discourse SSO + group sync based on membership status.
Backup self-service Members can restore from backup without operator intervention. Integration with backup system; point-in-time restore UI.
Site replication Standard tier can host replicas of external sites for higher availability. FedWiki farm configuration; origin→mirror sync workflow.
Voting rights management Cooperative governance — membership classes have voting rights per Bylaws. Link subscription status to voter registry; maybe separate from member-console.
Credit system Prepaid credits for Flexible services or usage-based billing. credit_grants + credit_transactions tables; apply to invoices.
Coupons & promotion codes Discounts for promotions, referrals, etc. coupon_mappings, promotion_code_mappings, sync core coupons to Stripe.
Refunds & disputes Handle customer refunds and dispute resolution. refund_mappings, dispute_mappings, bidirectional handling.
Credit application Apply credit grants to Stripe Customer Balance. Integration with Stripe Customer Balance API.
Invitations & team management Orgs can invite additional members to collaborate on sites. Organization invitations; role-based access within workspace.
Service accounts & API tokens Programmatic access to member-console APIs. Personal access tokens scoped to workspace.
Webhook integrations External systems can push/pull events from member-console. integration.webhook_events + integration_outbox.
Auth status endpoint External sites check session state to show dynamic CTAs ("Sign in" vs "Console"). GET /auth/status returning {authenticated}, CORS allowlist, parent-domain session cookie.
Hosted landing page Co-ops without a separate website get a configurable front door with tier info. Unauthenticated / renders configurable welcome page; links to /register and /login; optional pre-registration email capture.
Post-registration redirect Return users to the originating site after signup. ?return_to= param on /register, stashed in session, origin allowlist to prevent open redirect.
Custom theming (two-tier) Member console is open-source; deployments need to brand it without forking. Tier 1 (easy): operator admin UI overrides Bootstrap CSS variables (--bs-primary, --bs-secondary, --bs-body-color, a handful of others) — Bootstrap 5's CSS variable architecture makes this a small server-side feature (inject a <style> block with overrides). Tier 2 (power): unrestricted custom CSS injection for deeper changes. Reference model: Discourse admin appearance settings. Builds on the app.css scope contract established in M7c.
Minimum-term commitments (Decision 126 completion) Offer plans that bind a member for a minimum term with a clear, enforced early-exit story (block / fee / allow). Schema already landed (subscriptions.commitment_end / commitment_renewal / early_termination_policy + subscription_scheduled_changes), and block/allow enforcement shipped in plan-switch-mechanics. Three pieces remain, all latent until an operator can actually configure a commitment: (1) operator UI to set commitment terms on a subscription/product; (2) the fee branch — emit an early_termination_fee line item (needs the invoice_line_items.line_type column + credit-ledger, neither in code yet); (3) member-catalog per-tier labeling of block/fee moves (endpoints already enforce policy; only pre-click labeling is missing). See the three commitment-related entries in status/issues.md.