Files
member-console/status/milestones.md
T
cgalo5758 257955c9d3 Add operator list-scale contract and People directory
Governed operator lists (organizations, grants, people, billing×4) gain
server-side search, status filters, and 50-row pages with true totals
from count(*) OVER(); state is URL-addressable, out-of-range pages
clamp,
and no-match is distinct from true-empty.

People is the eighth flat sidebar entry: /operator/persons lists persons
newest-joined first (excluding the reserved system person), rows linking
to the existing detail.

Billing gains an operator invoice detail at
/operator/billing/invoices/{invoiceID} reusing the member projection;
open invoices past due present as Overdue (derived, filterable, stored
status untouched); all four views lead with the linked organization and
mute object IDs.

Grants filter over the derived Live/Superseded/Inactive state, the SQL
HAVING predicate pinned to the Go derivation by test. Embedded lists
(org composite ledger, Tier changes) adopt the shared controls under
namespaced params with sibling-state-preserving URLs and scoped htmx
swaps that hold the viewport.

Review corrections: blocked ladder Delete renders disabled with tooltip
and mutations fire toasts; collapse triggers paint their open state;
sections use outside headings; plan topology drops the orphan-product
check; domains policy collapses behind a disclosure.
2026-08-24 03:58:18 -05:00

33 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 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) → drop-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), 2 (ux-first-run), and 3 (ux-ia-naming, including the maintainer's correction rounds) shipped and archived 2026-08-23, change 4 (ux-operator-scale) shipped 2026-08-23 and archived 2026-08-24 after its composite-redesign rounds; only change 5 drop-entity-slugs remains, then the acceptance re-walk. 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.