Files
member-console/status/milestones.md
T
cgalo5758 660485d9b8 Make status/explorations a local notebook git ignores
The exploration directory held 245 tracked files, and one week's
notebook had grown to 28M of captures, per-run CSS copies and model
logs, so every change ended with an argument about what to prune. It is
now a local lab notebook: git ignores it, its tracked files are
untracked, and no tracked file cites a path under it, because another
clone does not have it.

Every live citation is rewritten so the fact stands in the sentence:
the docs pages, AGENTS.md, four main specs, one Go comment, issues.md
and milestones.md. The operator IA page gains the landed IA revisions it
used to point at, and the UX rubric moves to docs/first-contact-rubric.md
because the process requires it. Archived changes, status/archive,
status/log and design/ keep their old paths as frozen history; the
design/ ones are logged as an upstream issue. MAINTAINING.md and the
status README describe the notebook and the harvest that replaces
pruning.
2026-09-19 19:46:52 -05:00

42 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, scoped by the 2026-07 domain-management 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. Slice 3 (ops) in flight: deployed to console.wiki.cafe 2026-09-11 as the Swarm stack member-console-next, legacy backfill run from the never-merge legacy-backfill branch (313c770, tools/legacy-backfill/); gates G0G8 each checked on the day. Open as of 2026-09-11: Stripe secrets (G2), backfill reconciliation on record (G3/G4), maintainer sign-off (G5, ten findings logged in issues.md), enable sync (G6), point the farm's Caddy ask at /domains/ask (G7), soak (G8). 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 Done Closed 2026-08-30. Six changes shipped and archived: ux-honest-surfaces, ux-first-run, ux-ia-naming (2026-08-23), ux-operator-scale (2026-08-24), entity-keys with invoice-numbers (2026-08-29), and unify-shell (2026-08-30: one shell for both surfaces, the account menu, the offcanvas rail, the mirrored surface switch; the shell half of UX-13). The page half of UX-13 (page anatomy drift across the operator pages) was never in scope here and carries into 10k. The "fresh walkers re-run the missions" acceptance was not run as a separate pass; the maintainer walked each change through its review rounds instead, and the outside walk in 10k.4 covers it. Original scope follows. 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: 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 DONE + archived 2026-08-23 (implemented + verified through two maintainer review rounds folded in before the final re-archive, specs synced, uncommitted; see findings' Change 3 status: sidebar flattened to seven entries with sections navigating themselves in-page — the children mechanism, landing order, and inline IdP copy were REVERSED/revised in that review — map-first Plan Ladders + topology URL retired (404, no redirect), Enrollment retired for the org's own name, ui-vocabulary/form-conventions/chrome-conventions contracts applied with zero banned-term residue (workspace itself now banned from reachable UI per maintainer reversal; holdings attribute to the organization), member catalog rebuilt as Plans + a conditional non-plan section ("More products"/"Products"), member domains anchored, operator domains ledger/placements; full suite + e2e green after real walkthrough-suite fixes, all test-side) → ux-operator-scale DONE 2026-08-23 (implemented + verified, uncommitted, awaiting archive; see findings' Change 4 status: the operator-list-scale contract — search/filter pills/50-row pages with true totals, URL-addressable, on organizations, grants, people, billing×4; People directory as the eighth sidebar entry reversing the lookup-only decision; billing rows lead with the linked organization, IDs muted, new per-invoice detail; derived Overdue with a filter answering "who is behind on payments?"; grants filter proves SQL-vs-Go derivation agreement; 5 executor lanes, suites + 3 new e2e walkthroughs + live pass green; evening review round same day folded in — system person excluded from the directory, off-ladder products no longer "structural issues", flex min-width overflow fix, ladder delete disabled+tooltip with toast feedback, domains policy + composite forms progressively disclosed, integrations header tooltips, composite lists capped at 16, and Billing populated via a REAL Stripe test-mode checkout end to end; ARCHIVED 2026-08-24 after four further maintainer rounds on the organization composite, specs synced (73 items validate): a real defect fixed where extension resolved the pool's "first grant-backed provision" instead of the tier the button named, then a deliberated redesign (options weighed with three outside models before any code, per the maintainer's request) — the pool card groups by delivery so a shared product renders once with one Extend control and its ladder placements as rung pills; one heading rule (every section titled outside a box, every section boxed, card titles only name instances, disclosure actions inside the box they open into); the grants ledger split into Active/History tabs with search and paging on both; "Position history" renamed "Tier changes" reading in verbs and tier names instead of integer ranks; embedded lists defaulting to 10 rows with a size picker; and embedded controls doing scoped HTMX panel swaps so using them never scrolls the page while the URL still carries every list's state) → entity-keys. 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: 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, inventoried 2026-07-02: 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 of 2026-08-21, which 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).
10k Page anatomy, quality gate, htmx 4 Done Closed 2026-09-03. ui-acceptance archived 2026-09-03 (openspec/changes/archive/2026-09-03-ui-acceptance/; the ui-quality-gate spec gained the accessibility baseline and the outside-walk close requirements). 10k.4 acceptance: acceptance-fixes archived 2026-09-03 after the outside walk's 40 findings and six maintainer review rounds (design D1 to D31; the trail on every page, the setup banner and the first instance setting, record creation on its own page, the rebuilt billing card, the session-person check and the CSRF cookie lifetime found by review, the Stripe provider page, the screens baseline accepted). Follow-on work recorded as its own changes and issues: the forms consistency audit, the purchasability Status column, the instance settings page (deferred past M10), lint in CI (withdrawn 2026-09-01; CI is its own exploration). 10k.1 page-anatomy shipped and archived 2026-08-30 (openspec/changes/archive/2026-08-30-page-anatomy/): the shared parts, Domains and Integrations rebuilt on them, the anatomy lint rules with a 51-template allowlist, make screens with the first baseline accepted, and, from the maintainer's review, pages arriving complete (server-side composition through server.Include, the load-fetch rule), the list scaffold's pills wrapping on phones, the capture utility reporting viewport overflow, and the title/back-link rule (page-without-header). Next: 10k.2, the sweep in rail order, burning down internal/lint/anatomy_allowlist.txt. Decided 2026-08-30. The third UI pass, shaped so that sameness becomes a build failure and a visible artifact instead of a finding: M7 measured reachability and mechanics, 10g measured honesty and first contact, neither measured "built from the same parts", and the operator surface has eight page-header variants across fourteen pages to show for it. Sequenced before 10d ops. Scope: the operator surface, the member surface, and the integration-owned pages. 10k.1 machinery: page-anatomy partials (page_header, section_header, status_badge with one state-to-style map in Go, empty_state, and the existing list scaffold on every list page); a short "Page anatomy" section in docs/design-system.md that also removes its stale claims; member-console lint rules (an <h1>, a .badge, or an empty block rendered outside the partials, hx-confirm, an em dash in UI copy, a heading-level skip); make screens, a contact sheet of every route at 1600 and 390 with a baseline kept outside git, and the rule in the verify skill and docs/first-contact-ux-process.md that a UI change is not done until its sheet is reviewed. 10k.2 sweep: every page rebuilt on the partials in rail order with neighbours compared as they land: the operator pages, then FedWiki sites, Discourse, and Stripe settings, then Dashboard, Plans, Billing and their partials. 10k.3 htmx 4: 4.0.0 (released 2026-08-28; we vendor 2.0.4) behind the harness and the browser walkthroughs: upgrade-check; explicit inheritance for the shell's hx-boost and the CSRF header; hx-disabled-elt to hx-disable; hx-disinherit and the four hx-confirm dropped; event renames; hx-status in place of the 422 beforeSwap workaround; Vary: HX-Request where handlers branch on it; a recorded progressive-enhancement decision per mutation form; no htmx-2-compat left loaded. 10k.4 acceptance: contact sheets at both widths reviewed by the maintainer page by page against neighbours; one outside walk with the rubric plus a new section H "Sameness"; Lighthouse re-baseline at both widths; make test and lint green with lint in CI. Each step is its own OpenSpec change; nothing is archived before the maintainer's go. Follow-on forms-library archived 2026-09-05 (openspec/changes/archive/2026-09-05-forms-library/): the 2026-09-03 forms audit (53 findings) and the declaration-driven forms library (internal/forms, four layout families, the outcome contract, the registry and its invariants, the capture's open and refused states), through five maintainer review rounds; new spec form-library, 16 specs amended. typed-config-keys archived 2026-09-05 (openspec/changes/archive/2026-09-05-typed-config-keys/): typed configuration keys with one parser for the save, the boot overlay and boot validation; the member-console config group (list, set, clear, validate) through one settings service the page also calls, so no user-facing text names a SQL statement; the settings row keeps to its subject; the fail-fast question settled by config-validation research in 2026-09; the settings-versus-configuration rule ("the word follows the door") in ui-vocabulary and docs/settings-and-configuration.md. dense-widths archived 2026-09-06 (openspec/changes/archive/2026-09-06-dense-widths/; the overview brought onto the anatomy the same day, overview-consistency archived 2026-09-06 with the readout part, the linked card, the boxing rule and the raw-section-title lint rule): a dense column is full, half, third, quarter or sixth; the control's natural width picks its rung and a declaration steps it one rung only where the field's values are unusual; below 1200px every field takes its own line; the grant's free text is Note and both grant ledgers show it. Follow-ups filed in status/issues.md: the Tier changes ledger's paired rows and machine reasons (tier-changes-ledger), scheduled grants. Next: the purchasability Status change, or tier-changes-ledger first if the maintainer wants the ledger fixed while it is fresh.
10l Security audit Proposed 2026-09-03; run 1 remediated and committed 2026-09-09 (0b28a9d, ten changes); run 2 verified 2026-09-09, five confirmed in issues.md awaiting one fix change The maintainer, on the round-4 review of acceptance-fixes ("I think we'll want a security audit before launch too as yet another phase"): a pre-launch pass after the UI acceptance rounds, before 10d ops and the Gitea migration or alongside them. The prompt was that the two session findings of round 4 (a session outliving its person, a CSRF cookie shorter than the session) were found by review, not by design. Candidate scope on issues.md ("Security audit before launch"): authentication and sessions, authorization on every operator route and partial, integration-declared CSRF exemptions and webhook signatures, secrets in configuration and logs, CSP and response headers, dependency and image scanning, a threat model for the identity-provider trust boundary. Method, outside reviewers, and acceptance are for its proposal.

Remaining for M10 (what's missing): Done → 10a, 10b, 10c, 10e, 10f, 10g (closed 2026-08-30), 10h, 10i (2026-08-21), 10j (2026-08-22). Code done, ops pending → 10d (all three code slices shipped; Slice 3 deployed 2026-09-11, gates G2G8 still open; see the cutover ledger). 10k closed 2026-09-03, with seven follow-up changes archived after it (forms-library 2026-09-05, typed-config-keys 2026-09-05, dense-widths 2026-09-06, overview-consistency 2026-09-06, purchasability-status 2026-09-06, tier-changes-ledger 2026-09-06, ledger-demo-seed 2026-09-07: the demo seed plays a grant history on Carlos Member's organization through shared entitlements helpers, and the screens sheet shows it). Next → 10d ops Slice 3; then the Gitea migration (the forge: this file, issues.md and CI move to git.coopcloud.tech; Codeberg is ruled out as canonical home or mirror, because its Terms of Use forbid at § 2 (1) 7 projects that "mostly consist of code written by 'generative AI'-tools", and this one does); then 10l, the security audit, which is the last phase before launch. The mobile top bar rework that was queued behind 10l is done: mobile-shell-menu (2026-09-09) put the brand left-most below lg and made the rail's drawer the shell's one menu, in two named groups. Open from 10g, tracked in issues.md: the persons display-name model (upstream Issue 34, alternative 3 approved), personal-organization naming (Issue 35), terminal-state enforcement (Issue 36), operator editing of a key (a later slice). 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 checked lifecycle_status, so a draft that was active+public+priced+mapped could be bought, with only conferral rejecting it after payment; found by the 10i model-card verification pass), was fixed by purchase-path-blockers on 2026-08-22 (the catalog queries filter published products and checkout re-checks the member gate); the ledger entry under "Product-catalog card: drift & trap ledger" in issues.md records it resolved. Nothing on this list is a launch blocker; the gates before launch are 10d Slice 3, the forge migration, and 10l.

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.