Commit Graph
23 Commits
Author SHA1 Message Date
cgalo5758 e68493ea16 Read both grants pages from one listing query
ListGrantsWithDelivery now takes optional organization, search, product,
state and paging arguments and serves the grants page and the
organization page's ledger. The grouped SELECT's columns and grouping
moved unchanged into a CTE named listed, whose WHERE now holds the
organization filter and the search filter (with the organizations join
the search needs); the outer statement filters on the state computed
once, counts with count(*) OVER() after that filter, orders by
created_at DESC and pages (a NULL limit or offset reads every row).
ListGrantsWithDeliveryPage, toDeliveryRows and loadGrantsListData's
unused success and error-message arguments are deleted. The generated
row is field for field the old page row. Product names are still
matched in Go, so % and _ in a search keep matching themselves there.

The two pages keep the pool difference: with an organization the waits
are evaluated on its active default pool, without one on every pool.

How the commit was shown to change no output:
- The row golden, run first on PostgreSQL against the merged query,
  passed with every file under testdata/grants_listing/ and
  testdata/org_enrollment/ unchanged. LIMIT NULL OFFSET NULL returns all
  rows, and EXPLAIN ANALYZE of the organization-only call and of the
  search, state and page call shows no CTE scan on listed (it is inlined).
- internal/server (1398 results) and internal/entitlements (180) give
  the same test and subtest names and results at the parent and at this
  commit; no test was added, renamed or deleted.
- 13 breaks, each caught by the old and the new test, plus 3 breaks
  caught on the old side only: the adapter's copies of the waits, the
  default-duplicate and the restored-after columns, which are deleted
  with the adapter. No break was dropped because the old test missed it.
- Test calls were re-pointed to the merged query with their assertions
  unchanged (every row of the assertion map is same). The comments of the
  query, the grants template, docs/models/entitlements.md and
  docs/operator-ux-conventions.md now name the one query.

Unprobed: the order of grants that share created_at (a tier removal
writes one default grant per vacated pool in one transaction). The
merged query's plan may order such a tie differently from either old
query, no fixture holds a tie, and the order is accepted until the
tie-break by grant id, as for positions. The zero UUID pool of an
organization with no active default pool: no fixture organization lacks
one. total_count on the organization call, which no caller reads. The
adapter's other 22 copied columns on the old side: the goldens read each,
three are probed, and the adapter is gone so no copy remains to break.
2026-10-10 03:36:06 -05:00
cgalo5758 c77c9d7300 Correct the documents that name moved code
The storefront and consumers rows name the files that now hold the plan,
add-on and entitlement code; the integration guide drops member_products.go
from the files that stay in core and says the files of the split import no
Stripe package; the resource-pools
dimension states what the schema admits and what the console refuses; the plan
ladders card gains a Member position row; the kit table says the fixture
builder covers plan fixtures.

No output changes: only Markdown under docs/ is touched, no Go, SQL or
template file. git grep for member_products.go in docs and scripts is empty,
make lint and the notebook-citation check pass, and the lint snapshot differs
from the previous one by removed 0, added 0.
2026-10-09 04:50:39 -05:00
cgalo5758 f66a80b26a Restore the rules in the model cards and the Stripe guide 2026-10-07 06:28:59 -05:00
cgalo5758 8dcda61e37 Describe the console's database locks and the order paths take them 2026-10-07 01:35:25 -05:00
cgalo5758 afb18aae07 Correct the model cards that disagree with the code 2026-10-07 01:19:45 -05:00
cgalo5758 ab2bd4ada6 Delete the materializer's dry run and rule overlays 2026-10-07 00:36:11 -05:00
cgalo5758 15ffabbdd6 Delete the rule-change dry run 2026-10-07 00:36:11 -05:00
cgalo5758 23d5bad024 Add integration delivery core: provider connections, desired-state
records, outbox, lifecycle requests, relay, push, read-back, and FedWiki
lifecycle workflows

- Add core.provider_connections table and unique provider connection per
  provider
- Add core.target_mappings table linking connections to resource keys
  and targets
- Add core.desired_state_records table storing desired entitlements per
  connection and recipient with generation and content hash
- Add core.observed_projections table for application-reported state per
  connection and recipient
- Add core.lifecycle_requests table recording lifecycle requests by
  instance and generation
- Add core.person_cut_offs table and setter function to record persons
  cut off everywhere
- Add core.desired_state_outbox table with triggers on entitlement,
  membership, organization, connection, mapping, and cut-off tables to
  write change notices
- Enforce constraints on recipients, generations, and statuses across
  tables
- Implement desired_state_content SQL function computing record content
  per recipient rules
- Implement relay workflow to claim outbox rows, expand to records,
  recompute records, and dispatch pushes
- Implement push workflow to push records, handle transport answers, and
  record push results
- Implement read-back workflow to page through application state, update
  projections, and raise records as needed
- Add lifecycle request writers with idempotent recording, finishing,
  and acknowledgment
- Add FedWiki integration lifecycle workflows for create, set status,
  swap, delete, and reconcile
- Add FedWiki web handlers and forms for lifecycle requests, including
  create form with instance and generation
- Add tests for schema, triggers, relay, push, read-back, lifecycle
  requests, FedWiki workflows, and operator pages
- Add documentation updates for provider integration, entitlements,
  identity, organizations, lifecycle, and FedWiki integration
- Add benchmarks for delivery and push workflows
- Ensure schema-per-provider for in-tree integrations; connector state
  lives in generic core tables keyed by connection
- Secure functions and triggers with SECURITY DEFINER and restrict
  core_writer role privileges accordingly
- Handle Temporal client integration for workflow execution and
  scheduling
- Provide a recording transport for integration tests
- Close Issues 32 and 33 by implementing provider connections and
  desired-state records with lifecycle requests and relay mechanisms
2026-10-06 03:46:15 -05:00
cgalo5758 113c672e3b Resume a marked grant when the plan that replaced it ends, and archive the change
An operator can mark a grant to resume. When a plan replaces a marked
grant and that plan later ends (the subscription ends, or a granted plan
is revoked or expires), the grant delivers again from its own row before
the org-type floor runs (core migration 00023, core.grant_waits).

- Grants: a resumes_after_supersession column, off by default and never
  on a default grant; delivery states gain On hold, with a badge hover
  that names what the grant waits on.
- Operator: one Manage action per grant opens a card under its row
  (Note, Valid until, Resumes; Save, Cancel, Revoke). A changed Valid
  until extends the grant; otherwise the note and mark change in place.
  An extension now keeps its predecessor's valid_from.
- Member: a held rung reads Granted and is reached by cancelling; the
  cancel confirmation names each ladder's outcome; Cancel plan sits
  beside Keep plan while a downgrade is scheduled.
- Smaller fixes from the screens reviews: 24-hour times everywhere,
  search placeholders that name what they match, a checkbox label above
  its box, and the org-types walkthrough restoring the default it
  changes.
2026-10-01 18:16:40 -05:00
cgalo5758 56e65b669c Read invoices back from Stripe and archive the change
Stripe delivers webhook events in the endpoint's API version. Under a
version later than the 2025-02-24.acacia stripe-go v81 pins, the
payload-shaped invoice handlers projected every invoice with no
subscription link and no lines, and every payment with no payment
intent or method.

- Invoice events read only the invoice id and run ReconcileInvoice,
  which reads the invoice, its lines and its payment intent's charges
  back at the pinned version and converges the core rows under a
  per-invoice lock, in any event order. A payment row is one attempt,
  keyed by its Stripe charge (Stripe store migration 00005).
- Two clocks: created_at is when the console wrote a row; invoices gain
  issued_at and payments paid_at (core migration 00022, backfilled),
  and every invoice and payment list, the activity feed and the
  composite's latest invoice read those.
- An hourly and at-boot invoice sweep reads back invoices never read,
  open invoices once a day, and invoices of dead-lettered events; the
  provider page states when it last finished. The first run is the
  repair of invoices projected so far.
- The API version skew is logged once per version, and
  payment_method.attached links payments recorded before it.
- A restricted key now needs Invoices read and Charges read.
- scripts/db-reader-role.sql maintains console_reader, a login role
  that reads the console's tables without personal data, documented in
  docs/database-management.md and checked against a golden column list
  by TestConsoleReaderColumns. The demo snapshot keeps owners and
  privileges so the role's checks hold on it.
- The demo seed and test fixtures carry issue and paid times.
  status/design-feedback.md gains four upstream notes, one written when
  #60 was closed.

OpenSpec change stripe-invoice-reconcile is archived and its deltas
synced into openspec/specs/. Follow-ups are #150, #151 and #152.
2026-09-28 18:42:29 -05:00
cgalo5758 fba272fedb Charge upgrades now and defer downgrades to renewal
A paid-to-paid switch sent create_prorations, which charged nothing
now: the difference waited on the next renewal invoice, an immediate
cancellation lost it, and the confirmation's "charged now" was false.
A downgrade took effect at once, so moving to a cheaper paid tier cost
more access than cancelling (OpenSpec change plan-switch-timing,
unarchived: it awaits the maintainer's review, task 11.4).

- An upgrade is invoiced at once (always_invoice) and applies once paid
  (pending_if_incomplete). A declined card voids the invoice and leaves
  the plan as it was; a card that needs the bank's approval links to
  Stripe's invoice page. The preview pins its proration moment and the
  switch charges at it, so the amount charged is the amount shown.
- A downgrade takes effect at renewal through a subscription schedule
  and is recorded in the scheduled-change ledger.
- Every paid plan's card offers Cancel plan; a scheduled downgrade or
  cancellation shows on the card with Keep, which drops it at no
  charge. A paid rank-0 tier is a downgrade target, and a rung a grant
  holds moves up through Checkout with no move down.
- Reconcile keeps the ledger true to Stripe, and marks ended the
  subscription item a schedule's phase change replaced (core migration
  00021, subscription_items.ended_at). The sweeper reconciles a
  period-end move instead of firing it.
- The form library gains a commit that is a link out of the console.

The verify skill and the first-contact process now say the gap in
scripted screens is tracked as #118 and that a new issue waits for the
maintainer's approval.
2026-09-28 18:37:10 -05:00
cgalo5758 0c78d5f425 Move issue tracking to the forge tracker
Replace status/issues.md and status/milestones.md with the Gitea forge
tracker, add issue templates, and archive pre-forge issues as YAML.
2026-09-21 02:35:27 -05:00
cgalo5758 0caed6e905 Harvest the exploration notebooks into the docs
A survey of the twenty notebooks on disk found general facts that no
tracked page stated; the maintainer struck the weak rows and the rest
are written into their owning pages in each page's own words. The
design-system page gains the hidden-navigation figures that replaced a
dangling survey pointer, the reason the anatomy is parts and lint, the
record-table grouping and single-tint rules, and when a surface earns
row editing. The first-contact process gains a code-verification phase,
the transcript lane for models without vision, the three kinds of
honesty-audit drift and the click-path guardrail. The operator
conventions gain guards-before-writes, error state over empty state,
copy that states the real effect, toasts that report counts and the
PostForm rule. The Stripe, FedWiki, identifiers, IA, testing, model and
environment pages each gain their facts, the integration guide stops
telling authors to hand-write a page title, issues.md logs the
placeholder security contact, milestones.md records the Codeberg terms
clause and CONTRIBUTING.md notes that the module path is the forge URL.

One claimed fact was checked against the code and not written: the
mass-mutation preview does not run the commit loop in a rolled-back
transaction.
2026-09-19 19:47:33 -05:00
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
cgalo5758 f8a3478f2a Rebuild the entitlement set Rules surface as a staged batch
The Rules section is one record table grouped by kind, Limit then
Boolean, on fixed columns, edited in place: Edit opens a row's controls
in their columns, Add rule opens a dense row above the table, and every
change is staged into a tray that lists the deltas with Undo and applies
them as one rule-change act. The reduction policy is a column of the
rule beside its limit. History shows counts only. Group rows are a quiet
heading rather than a divider, the maintainer's pick from four rounds of
outside-model ideation.

Dense rows align to the top and render each error under its control in
every form family (design D16), replacing the below-row error block; the
forms library gains the batch form (rows plus one tray) and the RowField
dense and label-hidden options. Migration 00019 records the governing
reduction policy on effect rows.

Archive staged-rule-changes with its spec updates (entitlement-set-
management, entitlement-set-history, entitlements, form-library,
form-conventions, ui-quality-gate). Screens accepted 2026-09-19.
2026-09-19 19:46:09 -05:00
cgalo5758 0b28a9dc29 Remediate security audit findings
- Replace gorilla/csrf with net/http CrossOriginProtection
- Require valkey-password and add TLS options for session store
- End session at /logout and revoke refresh tokens
- Re-derive identity and roles from provider every five minutes
- Process each Stripe webhook event in its own Temporal workflow
- Give each outbox entry its own workflow with Temporal retries
- Guard against stale Stripe events with provider timestamps
- Derive transport security from base-url scheme
2026-09-09 13:25:43 -05:00
cgalo5758 568383b55e Reorganize status docs and add identifier docs
Move settled investigation records into
`status/explorations/<topic>-<date>/` with README indexes,
promote durable conventions to `docs/`, and add
`docs/identifiers.md` plus upstream Doc 46 person-name
provenance documents. Update cross-references and archive
resolved issues.
2026-08-29 21:15:31 -05:00
cgalo5758 dd3962990b Adopt entity keys and add invoice numbers
Replace the entity slugs on organizations, workspaces, resource pools,
and
plan ladders with nullable `key` columns and add keys to products,
prices,
and entitlement sets. Rename `providers.slug` to `provider` and add
partial
unique indexes for system and org role names.

Assign invoice numbers per billing account from a gapless transactional
counter; Stripe's number moves to the invoice mapping as an external
reference.

Seeds, fixtures, and the operator lookup address rows by key, and the
returning-login resync no longer blanks a display name when the IdP
sends
no `name` claim.
2026-08-29 20:12:04 -05:00
cgalo5758 8e3c68c6be Make UI surfaces honestly reflect system state
- Add deployment-name branding to titles, mastheads, and OG tags
- Share one grant delivery-state query with lineage across grants
  surfaces
- Show pool status/usage, org owners, and config readiness
- Make billing views projection-aware with recency and sync vocabulary
- Guard FedWiki creation without domains and render route-aware 404s
2026-08-23 01:45:52 -05:00
cgalo5758 ad7a219adf Enforce schema and boot invariants
Enforce 10j's verified gaps (schema-hardening change):

- Migration 00010: partial unique indexes for one default pool and one
  primary assignment per workspace, plus CHECKs pinning
  pool/provider/subscription vocabularies and provider lifecycle
  timestamps.
- Workspace creation shares a transactional provisioning function;
  extension validates its target pool; last-tier deletion of a defaulted
  ladder is guarded; signup completes plan-less on a broken ladder.
- Boot asserts integration slug parity and validates declared config
  enums; Stripe invoice amounts are range-checked; domain cancellation
  runs a final evidence probe; rule authoring is additive-only.
2026-08-22 18:02:46 -05:00
cgalo5758 efe3f1528d Restrict member surfaces to published products
Add lifecycle_status = 'published' to the public-catalog queries
(plans and add-ons listings) and reject checkout before any Stripe
call unless the product behind the price clears the shared member
gate (published + active + public). The currently-enrolled ladder
rung stays renderable even if its product is later drafted or
retired, fetched directly so members keep seeing what they are on.

Introduce a single evaluateMemberGate definition shared by the
catalog paths and the operator readiness panel so the surfaces
cannot disagree about what is publishable for members.
2026-08-22 12:58:05 -05:00
cgalo5758 ac2760a0fe Add model catalog cards and ratify Doc 44
Write the eight domain-model cards under `docs/models/`, mark M10 10i
Done, and update the design docs to v16. Also records Doc 44, which
ratifies Decisions 129-133 as amended, and the Doc-39 conformance
check that satisfied its gate.
2026-08-22 03:11:19 -05:00
cgalo5758 706c177847 Add model cards catalog
Introduce `docs/models/` with the catalog index and its first card
(product-catalog), recording invariants, dimensions, and drift traps
per the Decision 141 documentation division. Add the model-cards
openspec change (proposal, design, spec, tasks) and sync the design
docs to v15: product kind taxonomy dissolved (Decisions 134–139),
Decisions 140–141 ratified, and product/pricing helpers updated. The
catalog is the single descriptive home for as-built models.
2026-08-21 18:47:49 -05:00