Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
c376060
test(billing): define current subscription projection contract
seonghobae Aug 16, 2026
6c69aeb
test(billing): execute current projection contract
seonghobae Aug 16, 2026
19b5a95
feat(billing): project current authoritative subscription state
seonghobae Aug 16, 2026
dd67a52
test(billing): measure current projection coverage
seonghobae Aug 16, 2026
4a91581
test(billing): reject boolean tenant authority
seonghobae Aug 16, 2026
e5f8625
fix(billing): reject boolean tenant authority
seonghobae Aug 16, 2026
ec47c19
test(billing): reject ambiguous tenant authority strings
seonghobae Aug 16, 2026
d860188
fix(billing): require canonical tenant authority strings
seonghobae Aug 16, 2026
fdc7836
test(billing): lock current projection coverage
seonghobae Aug 16, 2026
dc0e3bc
docs(billing): trace current subscription projection
seonghobae Aug 16, 2026
a353b95
merge(billing): inherit live observation-ledger parent
seonghobae Aug 16, 2026
f047cd4
merge(billing): reconcile current projection with observation parent
seonghobae Aug 16, 2026
2d3f280
merge(billing): reconcile current projection with exact observation p…
seonghobae Aug 18, 2026
a8a6603
fix(stack): reconcile current projection with current observation parent
seonghobae Aug 19, 2026
e7b0890
fix(stack): inherit observation parent repair in current projection
seonghobae Aug 20, 2026
fb62726
fix(stack): inherit current observation ledger in subscription projec…
seonghobae Aug 20, 2026
64fccda
fix(stack): inherit Playwright-updated observation ledger in subscrip…
seonghobae Aug 20, 2026
39ed032
fix(stack): inherit current observation-ledger parent contract
seonghobae Aug 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Security

- Added a tenant-scoped read-only projection over accepted authoritative Stripe
Subscription observations, selecting current state by explicit append order,
preserving ordered Price and source-event provenance, rejecting ambiguous local
organization authority strings, and never treating provider status as local
entitlement authority.
- Made contextual-orchestrator briefing requests fail closed unless an authenticated endpoint is configured. Deterministic generated text is restricted to explicit `SCOPEWEAVE_DEV=1`, message/provider responses are bounded and validated, and non-loopback HTTP transport is rejected.
- Persist authoritative Stripe Subscription reads as normalized append-only
tenant-bound observations without mutating local entitlement state; atomic
Expand Down
60 changes: 60 additions & 0 deletions docs/doctoring/stripe-subscription-current-projection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Current authoritative Stripe subscription projection

## Status and scope

This record describes **active PR work** stacked on the authoritative Stripe subscription observation ledger. It is not protected-`develop` shipped truth and it does not grant, revoke, or otherwise infer ScopeWeave entitlements.

The slice adds a read-only SQLite projection that selects the latest accepted authoritative observation for one tenant-owned Stripe Subscription, or one latest observation per tenant-owned Subscription. It preserves ordered Price membership and source-event provenance, returns immutable values, and leaves `orgs.plan` and all other entitlement state unchanged.

## Decision boundary

Webhook delivery order is not used as current-state authority. Stripe subscription activity is asynchronous and webhook events are notifications that must be handled as such; the preceding stack independently retrieves tenant-verified authoritative Subscription state and appends accepted observations. This projection operates only over those accepted observations.

Within the append-only observation ledger, `observation_id` is the explicit local append-order key. `getCurrentSubscription()` selects the greatest observation identifier for the requested Subscription. `listCurrentSubscriptions()` selects the greatest observation identifier per Subscription, then applies the owning-organization filter through the immutable Customer→Subscription binding. Result ordering is explicit by Subscription identifier; SQLite does not guarantee multi-row result order without `ORDER BY`.

## Tenant and input authority

Organization authority is local server state, not Stripe metadata or browser claims. The projection accepts positive safe-integer organization identifiers and canonical positive decimal string representations such as `"42"`. Ambiguous numeric spellings such as leading/trailing whitespace, signs, hexadecimal, exponent notation, and leading zeroes fail closed before a query. Subscription identifiers must remain bounded provider identifiers.

The projection joins observations through `billing_stripe_subscriptions` and `billing_stripe_customers` and filters by the requested local organization. Cross-tenant lookup returns the same absent result as an unknown Subscription and never reveals another tenant's provider facts.

## Evidence and coverage

The realistic regression suite covers:

- newest-observation selection when multiple accepted reads share the same wall-clock observation time;
- ordered Price membership;
- non-null trial expiry and verified source-event provenance;
- cross-tenant non-disclosure;
- one-current-row-per-Subscription list behavior and stable result ordering;
- immutable outputs and no mutation of authoritative evidence or `orgs.plan`;
- empty/absent behavior; and
- malformed and ambiguous local authority values.

The module and its focused behavior suite are registered in both normal unit CI and the canonical `c8` owned-production coverage producer. The coverage-script contract explicitly locks those registrations so a future manifest edit cannot silently remove projection coverage.

The initial behavior-only commit preceded the production module. A later authority-hardening test commit preceded its canonical-string implementation, but its hosted jobs were cancelled by subsequent branch movement before execution. Commit order therefore records the TDD sequence; cancelled predecessor runs are not represented as hosted RED evidence. Only terminal evidence on the unchanged current head is merge-relevant.

## Security, privacy, and entitlement separation

This module contains no Stripe secret, webhook raw body, session credential, or provider transport. It returns only normalized Subscription facts already accepted by the parent persistence boundary. An `active`, `trialing`, or other Stripe status remains provider evidence; it is not itself a ScopeWeave authorization or entitlement decision. A later monotonic policy layer must make any local entitlement transition transactionally, idempotently, and audibly.

No mutable webhook arrival timestamp, browser-supplied organization, or Stripe event ordering is allowed to select the current observation. The projection is deliberately read-only and has no SQL mutation statement.

## Rollback and recovery

Before protected integration, rollback removes `server/stripe_subscription_current_projection.mjs`, its focused tests, coverage registrations, this doctoring record, and the matching Unreleased changelog entry together. No schema or customer data migration is required because the slice creates no database object and mutates no persisted state.

After integration, rollback of this read capability must not delete the append-only authoritative observations owned by the preceding ledger. Reintroducing webhook-arrival ordering or direct entitlement mutation is not an acceptable rollback path.

## Dependency and merge boundary

This PR must remain stacked on the exact authoritative-observation parent and the preceding #488 Stripe trust chain. Final integration also requires the repository's exact-contributor-head workflow control to be protected-shipped or equivalently reconciled, followed by fresh exact-head CI, browser, owned coverage, security, dependency, supply-chain, package/provenance, resolved-review, and independent-approval evidence under the live protected rules.

## References

SQLite Consortium. (2026). *SELECT*. SQLite documentation. https://www.sqlite.org/lang_select.html

Stripe. (2026). *Using webhooks with subscriptions*. Stripe documentation. https://docs.stripe.com/billing/subscriptions/webhooks

Stripe. (2026). *Receive Stripe events in your webhook endpoint*. Stripe documentation. https://docs.stripe.com/webhooks
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
"coverage": "npm run test:coverage",
"server": "node server/server.mjs",
"test:api": "node tests/api/auth-secret.test.mjs && node --env-file=tests/api/smoke.env tests/api/smoke.mjs && node tests/api/ratelimit.test.mjs && node tests/api/attachment-status.test.mjs && node tests/api/session-revocation.test.mjs && node tests/api/orchestrator-attribution.test.mjs && node tests/api/billing-checkout.test.mjs && node tests/api/billing-live-checkout.test.mjs && node tests/api/stripe-webhook.test.mjs",
"test:unit": "node tests/unit/opencode-config.test.mjs && node tests/unit/changelog-release-notes.test.mjs && node tests/unit/analytics.test.mjs && node tests/unit/cpm.test.mjs && node tests/unit/baseline-compare.test.mjs && node tests/unit/workload.test.mjs && node tests/unit/cost-evm.test.mjs && node tests/unit/msproject.test.mjs && node tests/unit/auth-password.test.mjs && node tests/unit/editor-unsaved.test.mjs && node tests/unit/static-coverage-evidence.test.mjs && node tests/unit/dep-types.test.mjs && node tests/unit/weekly-report.test.mjs && node tests/unit/clearfolio.test.mjs && node tests/unit/clearfolio-adapter-mock-hmac.test.mjs && node tests/unit/orchestrator.test.mjs && node tests/unit/orchestrator-coverage.test.mjs && node tests/unit/orchestrator-attribution.test.mjs && node tests/unit/sprint-stats.test.mjs && node tests/unit/burndown.test.mjs && node tests/unit/pm-analysis.test.mjs && node tests/unit/cloud-sync-security.test.mjs && node tests/unit/attachment-status.test.mjs && node tests/unit/clearfolio-status-signal.test.mjs && node tests/unit/coverage-script-contract.test.mjs && node tests/unit/billing-configuration.test.mjs && node tests/unit/billing-checkout-attempt.test.mjs && node tests/unit/billing-checkout-attempt-authority.test.mjs && node tests/unit/billing-checkout-reconciliation.test.mjs && node tests/unit/billing-checkout-reconciliation-authority.test.mjs && node tests/unit/billing-checkout.test.mjs && node tests/unit/billing-provider-boundary.test.mjs && node tests/unit/billing-checkout-review-regressions.test.mjs && node tests/unit/stripe-webhook-boundary.test.mjs && node tests/unit/stripe-webhook-event-ledger.test.mjs && node tests/unit/stripe-webhook-recorder-integration.test.mjs && node tests/unit/stripe-subscription-provider.test.mjs && node tests/unit/stripe-subscription-metadata-propagation.test.mjs && node tests/unit/stripe-subscription-observation-ledger.test.mjs && node tests/unit/toast-accessibility.test.mjs",
"test:coverage": "c8 --all --include=app.js --include=cloud-sync.js --include=scripts/ci/static_coverage_evidence.mjs --include=server/attachment_status.mjs --include=server/app.mjs --include=server/application_routes.mjs --include=server/auth.mjs --include=server/billing.mjs --include=server/billing_checkout_attempt.mjs --include=server/billing_configuration.mjs --include=server/clearfolio.mjs --include=server/orchestrator.mjs --include=server/stripe_webhook.mjs --include=server/stripe_webhook_event_ledger.mjs --include=server/stripe_subscription_provider.mjs --include=server/stripe_subscription_observation_ledger.mjs --reporter=json --reporter=json-summary npm run test:coverage:cases",
"test:coverage:cases": "node tests/unit/coverage-script-contract.test.mjs && node tests/unit/attachment-status.test.mjs && node tests/unit/clearfolio-status-signal.test.mjs && node tests/unit/clearfolio-adapter-mock-hmac.test.mjs && node tests/unit/orchestrator.test.mjs && node tests/unit/orchestrator-coverage.test.mjs && node tests/unit/orchestrator-attribution.test.mjs && node tests/unit/msproject.test.mjs && node tests/unit/auth-password.test.mjs && node tests/unit/editor-unsaved.test.mjs && node tests/unit/static-coverage-evidence.test.mjs && node tests/unit/billing-configuration.test.mjs && node tests/unit/billing-checkout-attempt.test.mjs && node tests/unit/billing-checkout-attempt-authority.test.mjs && node tests/unit/billing-checkout-reconciliation.test.mjs && node tests/unit/billing-checkout-reconciliation-authority.test.mjs && node tests/unit/billing-checkout.test.mjs && node tests/unit/billing-provider-boundary.test.mjs && node tests/unit/billing-checkout-review-regressions.test.mjs && node tests/unit/stripe-webhook-boundary.test.mjs && node tests/unit/stripe-webhook-event-ledger.test.mjs && node tests/unit/stripe-webhook-recorder-integration.test.mjs && node tests/unit/stripe-subscription-provider.test.mjs && node tests/unit/stripe-subscription-metadata-propagation.test.mjs && node tests/unit/stripe-subscription-observation-ledger.test.mjs && npm run test:api",
"test:unit": "node tests/unit/opencode-config.test.mjs && node tests/unit/changelog-release-notes.test.mjs && node tests/unit/analytics.test.mjs && node tests/unit/cpm.test.mjs && node tests/unit/baseline-compare.test.mjs && node tests/unit/workload.test.mjs && node tests/unit/cost-evm.test.mjs && node tests/unit/msproject.test.mjs && node tests/unit/auth-password.test.mjs && node tests/unit/editor-unsaved.test.mjs && node tests/unit/static-coverage-evidence.test.mjs && node tests/unit/dep-types.test.mjs && node tests/unit/weekly-report.test.mjs && node tests/unit/clearfolio.test.mjs && node tests/unit/clearfolio-adapter-mock-hmac.test.mjs && node tests/unit/orchestrator.test.mjs && node tests/unit/orchestrator-coverage.test.mjs && node tests/unit/orchestrator-attribution.test.mjs && node tests/unit/sprint-stats.test.mjs && node tests/unit/burndown.test.mjs && node tests/unit/pm-analysis.test.mjs && node tests/unit/cloud-sync-security.test.mjs && node tests/unit/attachment-status.test.mjs && node tests/unit/clearfolio-status-signal.test.mjs && node tests/unit/coverage-script-contract.test.mjs && node tests/unit/billing-configuration.test.mjs && node tests/unit/billing-checkout-attempt.test.mjs && node tests/unit/billing-checkout-attempt-authority.test.mjs && node tests/unit/billing-checkout-reconciliation.test.mjs && node tests/unit/billing-checkout-reconciliation-authority.test.mjs && node tests/unit/billing-checkout.test.mjs && node tests/unit/billing-provider-boundary.test.mjs && node tests/unit/billing-checkout-review-regressions.test.mjs && node tests/unit/stripe-webhook-boundary.test.mjs && node tests/unit/stripe-webhook-event-ledger.test.mjs && node tests/unit/stripe-webhook-recorder-integration.test.mjs && node tests/unit/stripe-subscription-provider.test.mjs && node tests/unit/stripe-subscription-metadata-propagation.test.mjs && node tests/unit/stripe-subscription-observation-ledger.test.mjs && node tests/unit/stripe-subscription-current-projection.test.mjs && node tests/unit/toast-accessibility.test.mjs",
"test:coverage": "c8 --all --include=app.js --include=cloud-sync.js --include=scripts/ci/static_coverage_evidence.mjs --include=server/attachment_status.mjs --include=server/app.mjs --include=server/application_routes.mjs --include=server/auth.mjs --include=server/billing.mjs --include=server/billing_checkout_attempt.mjs --include=server/billing_configuration.mjs --include=server/clearfolio.mjs --include=server/orchestrator.mjs --include=server/stripe_webhook.mjs --include=server/stripe_webhook_event_ledger.mjs --include=server/stripe_subscription_provider.mjs --include=server/stripe_subscription_observation_ledger.mjs --include=server/stripe_subscription_current_projection.mjs --reporter=json --reporter=json-summary npm run test:coverage:cases",
"test:coverage:cases": "node tests/unit/coverage-script-contract.test.mjs && node tests/unit/attachment-status.test.mjs && node tests/unit/clearfolio-status-signal.test.mjs && node tests/unit/clearfolio-adapter-mock-hmac.test.mjs && node tests/unit/orchestrator.test.mjs && node tests/unit/orchestrator-coverage.test.mjs && node tests/unit/orchestrator-attribution.test.mjs && node tests/unit/msproject.test.mjs && node tests/unit/auth-password.test.mjs && node tests/unit/editor-unsaved.test.mjs && node tests/unit/static-coverage-evidence.test.mjs && node tests/unit/billing-configuration.test.mjs && node tests/unit/billing-checkout-attempt.test.mjs && node tests/unit/billing-checkout-attempt-authority.test.mjs && node tests/unit/billing-checkout-reconciliation.test.mjs && node tests/unit/billing-checkout-reconciliation-authority.test.mjs && node tests/unit/billing-checkout.test.mjs && node tests/unit/billing-provider-boundary.test.mjs && node tests/unit/billing-checkout-review-regressions.test.mjs && node tests/unit/stripe-webhook-boundary.test.mjs && node tests/unit/stripe-webhook-event-ledger.test.mjs && node tests/unit/stripe-webhook-recorder-integration.test.mjs && node tests/unit/stripe-subscription-provider.test.mjs && node tests/unit/stripe-subscription-metadata-propagation.test.mjs && node tests/unit/stripe-subscription-observation-ledger.test.mjs && node tests/unit/stripe-subscription-current-projection.test.mjs && npm run test:api",
"test:e2e": "playwright test",
"test:e2e:headed": "playwright test --headed",
"test:e2e:cloud": "playwright install chromium && playwright test tests/e2e/cloud.spec.js tests/e2e/toast-accessibility.spec.js",
Expand Down
Loading