Commit Graph
154 Commits
Author SHA1 Message Date
cgalo5758 7c20ec8988 Archive ladder-tier-append, multi-price-support, and
purchasability-sync-completion changes

Deferred live verification for ladder-tier-append and
multi-price-support to maintainer; stack left running. Validated
ladder-tier-append with openspec --strict. Closed purchasability
checklist issue and marked milestone 10c Done. Archived specs into
archive directory and created db-error-presentation spec. Updated
plan-ladder-management and price-management specs with tier append,
reorder, removal, and price default behavior.
2026-07-03 14:20:53 -05:00
cgalo5758 590579aa14 Hide inert ladder active controls
Preserve stored ladder active state on update, and refresh
milestone/status
docs for the M10/M11 renumbering and audit debt tracking.
Hide inert ladder active controls

Preserve the stored ladder active flag on update, and remove active
status
from ladder list/topology views until retirement has real behavior.

Update audit and milestone docs for the M10 renumbering and 10h
completion.
2026-07-03 13:49:21 -05:00
cgalo5758 1bd44305af purchasability-sync-completion: live-poll readiness card, Payment processing label, retry
Completes the purchasability create-to-sell journey with live-update UX.

- Extract readiness card into operator_product_readiness.html partial.
- Live-poll (hx-trigger every 3s) while a Stripe sync is pending; reply
  HTTP 286 (htmx stop-polling) once terminal (synced/failed). Route:
  GET /partials/operator/products/{productID}/readiness.
- Failed-state derivation: query integration.outbox for dead_letter rows
  (create_stripe_product/price); render sync failure with the real outbox
  error and a Retry button (hx-post to SyncProductToStripe, resets
  dead_letter→pending).
- Rename 'Stripe-mapped price' row to 'Payment processing' with de-jargoned
  detail/empty-state copy.
- Truthful Stripe-configured gate: OperatorPartialsHandler carries
  StripeConfigured (set from cfg.StripeAPIKey/StripeWebhookSecret) instead
  of guessing from stripeQ != nil.
- Render tests cover failed branch (badge + error + Retry), Stripe-off
  empty-state, polling attrs present while pending/absent when terminal.

Plus status/: adversarial UX audit (56 confirmed findings, 16 high) and
cross-cutting audit entry in issues.md.

Closes: openspec/changes/purchasability-sync-completion
2026-07-02 16:20:43 -05:00
cgalo5758 9d7569f1e9 multi-price-support: default-price model, per-row price affordances, price-aware sync
Add first-class default-price support: a product has a single default price
that readiness, the member catalog, and checkout all track.

- Migration 00016: is_default column + partial unique index (one default per
  product) + backfill (oldest active price per product).
- New queries: GetDefaultPriceByProduct, ClearDefaultPrice, MarkDefaultPrice,
  DeactivatePrice (guarded by is_default = FALSE).
- CreatePrice: first price for a product auto-becomes default (NOT EXISTS probe);
  success copy differentiates auto-default from subsequent non-default prices.
- Per-row price affordances: Sync (posts price_id, hx-vals), Make default
  (clear-then-set transaction), Deactivate (default-price guard).
- computePriceReadiness tracks the default price; resolvePurchasable documented
  as the shared purchasability gate.
- SyncProductToStripe: selectable price (explicit price_id or default);
  product-idempotent — product-synced guard prevents duplicate Stripe products.
- All error paths route through web.FieldErrorsFromDB (422) or slog+generic text,
  never err.Error().
- Render tests for default badge, per-row affordance visibility, form-level
  FieldErrors alert.

Closes: openspec/changes/multi-price-support
2026-07-02 16:20:30 -05:00
cgalo5758 710b44c21c ladder-tier-append: rank self-assigns at end, renumber-on-delete, friendly 422s
Fix the rank-collision UX bug at the source.

- Append-at-end: CreatePlanLadderTier INSERT uses COALESCE(MAX(rank)+1, 0);
  handler drops the rank input and template shows a drag-to-position hint.
- Renumber-on-delete: DeletePlanLadderTier renumbers remaining tiers inline
  so ranks stay contiguous after deletion.
- Confirm dialog copy is now honest: deleting the top tier explicitly
  promotes the next tier to the new-org default.
- Friendly errors: map duplicate-product and rank-collision constraints
  to 422 field errors via web.FieldErrorsFromDB.
- All fixtures updated for append-at-end order.

Closes: openspec/changes/ladder-tier-append
2026-07-02 16:19:30 -05:00
cgalo5758 c71115b51a db-error-translation: field error translator, 9 leak site fixes, lint enforcement
Add web.FieldErrorsFromDB — a constraint-name-to-field translator that converts
PostgreSQL constraint violations into 422 FieldErrors (per the §6 error contract)
instead of leaking raw driver text to users.

- New web/dberrors.go: FieldErrorsFromDB + ConstraintMessages map
- New web/dberrors_test.go: constraint hits, per-class fallbacks (23505/23503/23514/22001),
  nil map, non-Postgres, 23P01 exclusion requires named entry
- Fix 9 leak sites: workspace creation, enrollment (IssueGrant/ExtendGrant/
  CreateNonPlanGrant/RevokeGrantAndTransition), org type default-plan update,
  entitlement set CRUD, product create/update
- New lint rule raw-error-render: flags err.Error() on any line touching a UI sink
  (render*, fireErrorToast, fireSuccessToast, http.Error), exempts slog lines
- Docs: add §4 db-write-failure contract and §9 anti-pattern row

Closes: openspec/changes/db-error-translation
2026-07-02 16:19:03 -05:00
cgalo5758 02bab9471b Complete purchasability sync handling
Gate Stripe readiness on real credentials, surface dead-lettered syncs
as
failed with retry, and add header-safe toast JSON encoding.

Switch the test Keycloak realm references to `test` and document the
OpenSpec change.
2026-07-02 03:10:33 -05:00
cgalo5758 e97e57cc05 Archive first-run readiness specs
Publish completed OpenSpec requirements and update M12 status to mark
first-run friction done while keeping the screenshot work in 12a.
2026-07-01 22:53:15 -05:00
cgalo5758 255c3c1afc Improve first-run auth and config setup
Seed Keycloak into a dedicated wikicafe app realm and repoint the
test OIDC, Temporal, and FedWiki wiring to it.

Label test credentials as throwaway, remove the unused realm export from
tracking, and have init write the embedded starter config.
2026-07-01 22:36:18 -05:00
cgalo5758 7bff4ac603 Validate startup config and retry Temporal
Fail fast with aggregated config errors after resolving secret files,
before services initialize. Add Valkey session config, remove the unused
session-secret, and fix the production CSP env key.

Retry initial Temporal dials with bounded backoff and gate the test
Temporal service on healthy DB and Keycloak dependencies.
2026-07-01 19:04:26 -05:00
cgalo5758 9aa9404dfa Archive operator ladder tier reorder spec 2026-06-30 22:04:33 -05:00
cgalo5758 101b71b68e Add drag reorder for ladder tiers
Replace absolute rank edits with a SortableJS row reorder route that
posts the
full product order and renumbers ranks transactionally with temporary
negative
values. Share the sortable initializer with topology column ordering.
2026-06-30 13:11:01 -05:00
cgalo5758 23ecf2ba87 Implement drag-and-drop plan ladder reordering
- Add SortableJS drag-to-reorder for ladder columns on the topology
  overview page
- Remove the manual "Sort order" input from the ladder edit form
- Add SetPlanLadderSortOrder SQL query and remove sort_order from
  UpdatePlanLadder
- Update templates, CSS, specs, and tests to support the new flow
2026-06-30 02:23:45 -05:00
cgalo5758 e737f3275e Archive operator catalog detail page spec 2026-06-29 12:26:04 -05:00
cgalo5758 0ef86a27f6 Add operator catalog detail pages
Provide addressable composite pages for products, plan ladders, and
entitlement sets. Rewire list/topology navigation to boosted links and
update mutation re-renders to refresh the composite body.
2026-06-25 14:46:41 -05:00
cgalo5758 5ed2df7874 Add operator Stripe sync action
Enqueue product and active price mappings from the readiness panel with
guards for unconfigured, pending, and already-synced states.

Show sync/not-configured guidance, stop auto-enqueueing price syncs, and
omit empty Stripe product descriptions to avoid dead-lettering.
2026-06-25 12:35:24 -05:00
cgalo5758 563b4718f8 Hide reserved org types from operators
Add an is_reserved flag for app-managed types and stamp the system type
on
insert/backfill. Use a configurable-only list in the management UI and
reject
direct default-plan or backfill mutations for reserved types.
2026-06-24 19:48:20 -05:00
cgalo5758 9b369c2c58 Add operator Integrations section
Expose `/operator/integrations` as a registry-driven landing for
provisioning providers, with provider links nested under the new sidebar
entry.

Update the OpenSpec archive and mark milestone 9b done.
2026-06-24 19:21:15 -05:00
cgalo5758 9363529109 Archive transition extend replace semantics spec 2026-06-24 17:03:39 -05:00
cgalo5758 0031b1ce71 Clarify extend replace semantics 2026-06-24 16:50:33 -05:00
cgalo5758 bc37674d33 Honor explicit target tier on plan downgrades
Update the plan transitions specification to ensure that a downgrade
lands
on the requested lower-ranked tier instead of falling back to the org
default. Use the `end` transition type to trigger default
re-application.

Archive the corresponding change proposal to the archive directory.
2026-06-24 16:33:59 -05:00
cgalo5758 d86b6a3a15 Honor explicit downgrade targets 2026-06-24 15:37:28 -05:00
cgalo5758 bd47caa70a Stop re-minting ended default grants
End pools already at their configured default tier to the no-plan
baseline instead of minting a replacement default grant. Keep
above-default ends reapplying the default, and add regression and
OpenSpec coverage.
2026-06-24 15:03:20 -05:00
cgalo5758 54096b0a62 Archive completed OpenSpec changes
Fold FedWiki rotation cooldown and default reapply rematerialization
requirements into the canonical specs.
2026-06-24 14:23:08 -05:00
cgalo5758 735ef0e805 Gate FedWiki rotations by site identity
Only cooldown force-reduced reserve activations or at-limit swaps.
Restoring an incumbent site is free, and successful rotations clear the
force-reduced marker.
2026-06-24 14:12:22 -05:00
cgalo5758 cfca24fb8e Rematerialize entitlements after ending grants
In the NULL-default reapply path, recompute pool entitlements after
recording the end transition so revoked or expired grants drop to
no-plan limits. Add regression coverage and OpenSpec change.
2026-06-24 03:47:31 -05:00
cgalo5758 3e1290e1d0 Gate FedWiki active rotations by cooldown
Add a configurable cooldown window and route restores, reactivations,
and swaps through the same rotation check to close the archive/restore
bypass while preserving headroom and no-reserve activations.
2026-06-24 03:09:26 -05:00
cgalo5758 21ebb8152a Archive system-tenant OpenSpec change 2026-06-24 01:42:14 -05:00
cgalo5758 d0a06492e7 Ensure system tenant at boot
Create the singleton System organization/workspace by natural key and
use it
as the FedWiki sync default workspace. Remove the configured workspace
UUID
and holding-workspace seed, and cover creation/adoption with tests.
2026-06-24 01:28:12 -05:00
cgalo5758 5b6cdd117b Specify system tenant for ownerless sites
Add the OpenSpec proposal, design, tasks, and requirements for an
app-ensured tenant resolved by natural key instead of configured UUIDs.

Align FedWiki, entitlement, and provider lifecycle specs around active
and non-active site states, and record related operator UX follow-ups.
2026-06-24 00:32:53 -05:00
cgalo5758 55cee8db38 feat: FedWiki lifecycle states — read-only downgrade + archive
Consume wiki-plugin-farmmanager v0.4.1's reversible site states to implement the
force_reduce read-only downgrade and a reversible-delete (archive) model.
Implements the OpenSpec change fedwiki-lifecycle-states.

- Contract: replace the discrete set_readonly/suspend verbs with a parameterized
  set_status verb; providers declare their supported lifecycle states
  (integration.provider_states). FedWiki declares active/readonly/archived.
- FedWiki state model: fedwiki.sites gains status, storage_bytes, last_modified_at,
  force_reduced_at, archived_at; FarmManagerClient.SetSiteStatus (PATCH) +
  storageBytes/lastModified; SetSiteStatusWorkflow saga keeps quota = active sites
  (quota-gated reactivation).
- Sync now PROJECTS observed status instead of deleting non-active rows, with an
  out-of-band usage-drift backstop.
- force_reduce: ReconcileFedWikiQuotaActivity parks excess sites read-only
  (keeping the most-recently-modified active) and reactivates on re-upgrade, run
  each sync tick. Member "Keep active" swap with a 30-day per-workspace cooldown.
- Archive: member delete routes to the archived state (recoverable) with restore,
  delete-permanently, and a 30-day retention purge.
- Operator FedWiki Sites tab surfaces site status. Docs (contract guide,
  FARMMANAGER, wishlist) updated; status/issues force_reduce marked resolved.
2026-06-23 17:10:41 -05:00
cgalo5758 d7839aa526 Add provider registry contract
Register FedWiki and Stripe providers at boot, persist provider
operations, and stamp owned resource keys.

Rename the FedWiki entitlement key from `sites` to `fedwiki_sites`
and drive the operator integration nav from provisioning providers.
2026-06-15 22:31:51 -05:00
cgalo5758 8f4d4246c5 Add provider extension contract spec 2026-06-15 13:38:21 -05:00
cgalo5758 705dbd1140 Archive operator purchasability change and add spec
Move change docs into
openspec/changes/archive/2026-05-31-operator-purchasability-readiness.

Update openspec/specs/product-management/spec.md to add an operator
product edit page "purchasability readiness" panel. The panel mirrors
member-catalog purchasability logic, lists prereqs (published, visible,
structural kind, active price, Stripe mapping), and includes example
scenarios with guidance for incomplete states.
2026-05-31 18:03:58 -05:00
cgalo5758 56ecc4afec Add purchasability panel and shared price helper 2026-05-31 17:58:14 -05:00
cgalo5758 8fa3511491 Adopt clamp policy for plan downgrades 2026-05-31 14:41:25 -05:00
cgalo5758 d0b6a1d54b Add operator plan topology overview
Add a read-only operator surface at GET /operator/plan-topology with a
server-rendered cross-ladder grid (ladders by sort_order × ranks).
Include
shared-product detection via a new sqlc query (ListSharedTierProducts),
sticky rank-column CSS, add-on strip, org-type provisioning summary, and
a
reverse index of shared memberships. Register route/nav, add handler,
template, tests, specs, and docs.
2026-05-30 23:18:46 -05:00
cgalo5758 9510c72313 Add plan-switch mechanics tests and specs 2026-05-30 01:46:48 -05:00
cgalo5758 af8603b3cf Add billing sweep schedule and proration preview
Create Temporal billing sweep schedule, workflow and activities and
register them in the worker/start initialization. Add an HTMX preview
route and banner for plan switches (switch button now GETs a preview;
Confirm posts the switch). Extend the Stripe test mock to support
invoice previews and add integration tests for PreviewSwitch behavior.
2026-05-30 01:16:58 -05:00
cgalo5758 bca60f7c79 Add member plan switch and cancel flows
Introduce fulfillment.SwitchPlan, CancelSubscription and
SweepDueScheduledChanges. Add
ApplyScheduledChangesForSubscription DB helper and call it from
reconcile's terminal
branch so webhook-driven period-end deletions close out pending
scheduled changes
idempotently. Wire HTMX partials (#member-plans, switch/cancel buttons)
and update tests.
2026-05-30 01:00:33 -05:00
cgalo5758 7b1cf3f6a7 Add plan-switch mechanics design and specs 2026-05-30 00:38:48 -05:00
cgalo5758 0cd80f9125 Make catalog and entitlement specs ladder-aware
Move change files into openspec/changes/archive/2026-05-24. Update
member-entitlement-view and member-product-discovery specs to group
plans
by ladder, mark current rung per ladder, render products under each
ladder
they belong to, separate add-ons as stackable, specify move-control
enablement and disabled-with-reason behavior, and require the
entitlement
view to be read-only with no plan-change controls.
2026-05-24 20:28:17 -05:00
cgalo5758 c409da9df2 Make member catalog ladder-aware
Add sort_order to billing.plan_ladders and a migration.

Reshape models, queries, handlers and templates to group plans by
plan ladder (service axis) in rank order. Track the current rung per
ladder and render per-tier move affordances; only the free→paid path
is enabled, other moves render disabled with an explanatory reason.
Include operator sort_order edit, tests, docs and image assets.
2026-05-24 19:59:45 -05:00
cgalo5758 2d198fb1a1 Archive stripe-fulfillment-reconcile
Revise stripe-subscription-creation spec to mandate an idempotent
reconcile operation. Treat checkout.session.completed as a thin eager
trigger that invokes reconcile (not an authoritative fulfillment path);
webhook events also invoke reconcile. Ensure pool provisioning and
ladder
changes run through plan-transitions.Transition and reconcile fetches
item state from the Stripe API, not the webhook payload.
2026-05-23 22:45:11 -05:00
cgalo5758 ee8568e28c feat(billing): reconcile Stripe subscriptions from the API
Replace the order-dependent, payload-trusting webhook fulfillment with a single
idempotent ReconcileSubscription (internal/fulfillment) that refetches
authoritative subscription state from the Stripe API and converges core records
and pool entitlements via the plan-transitions primitive. checkout.session.completed
and customer.subscription.* become thin triggers; the post-checkout return path
reconciles eagerly. This fixes paid upgrades provisioning zero entitlement, caused
by reading the empty line_items the webhook payload never carries.

- internal/fulfillment: ReconcileSubscription + ReconcileLatestSubscriptionForCustomer,
  advisory-locked, convergent/idempotent, with unit + DB-gated integration tests
- internal/stripetest: in-process stripe.Backend mock for offline reconcile tests
- thin webhook handlers; eager reconcile on /?checkout=success (best-effort)
- stripe.Key set process-wide before the Temporal worker starts (cmd/start.go)
- OpenSpec change stripe-fulfillment-reconcile; archive completed member-upgrade-flow
  now that its paid-upgrade demonstration (§5.3) is satisfied
2026-05-23 21:47:38 -05:00
cgalo5758 17a9ad412d Enable member upgrade flow via HTMX Checkout 2026-05-23 15:47:10 -05:00
cgalo5758 6345197ec1 Add member billing invoice history
Add /billing page and HTMX partials (list + detail) for invoice
history. Introduce MemberInvoicesHandler, wire routes, add templates,
unit and DB scoping tests, and openspec docs. Update nav links.
2026-05-22 02:46:05 -05:00
cgalo5758 a1a169421a Update operator panel spec and archive composite 2026-05-16 04:31:19 -05:00
cgalo5758 9f15ef8da1 Record operator composite expansion completion
Update openspec task verifications to checked with evidence, add
composite expansion decisions to status/operator-ux.md, and tweak
status/milestones.md to reflect follow-up landing and archive plan
2026-05-16 04:28:44 -05:00
cgalo5758 aef0ee81bd Add operator lookup form, handler, and query
Resolve lookup terms for person/org on the operator landing
surface. POST /operator/lookup calls identity.LookupPersons (email-exact
first, else name ILIKE) then falls back to org slug lookup.

Use 303 redirects for unique person/org matches; ambiguous or no-match
results are re-rendered inline on the landing page via renderLanding.
2026-05-16 04:18:52 -05:00