Repository navigation
handbook: narrow versioning design doc scope - #15684
pieterbeulque wants to merge 5 commits into
Conversation
These were auto-written by `next dev` and accidentally swept into the versioning commit; unrelated to this PR. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Full freeze for declarative-config orgs, migrations DSL and meter versioning out of scope, derived live membership instead of is_current, config-only drafts with read-only dashboard review, and new References and API surface sections. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
2 issues found across 1 file
Confidence score: 3/5
- In
handbook/engineering/design-documents/versioning.mdx, plainapplycan publish without member review, leaving a path for an agent to move customers. Clarify that agents are limited toapply --draftor document a separate safeguard. - The uniqueness rule in
handbook/engineering/design-documents/versioning.mdxmakes meters catalog-versioned, which conflicts with the stated v0 scope. Limit the rule to products and benefits, and clarify meter identity in v0.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="handbook/engineering/design-documents/versioning.mdx">
<violation number="1" location="handbook/engineering/design-documents/versioning.mdx:86">
P3: This uniqueness rule makes meters catalog-versioned, contradicting the stated v0 scope. Limit the catalog-version uniqueness rule to products and benefits, and clarify meter identity in v0.</violation>
<violation number="2" location="handbook/engineering/design-documents/versioning.mdx:287">
P2: This mitigation only holds for `apply --draft`. Plain `apply` publishes with no member review, so an agent can publish and then move customers. Either state that agents are limited to `apply --draft`, or name a separate control that stops an agent from publishing and moving customers in one go.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| - **Risks:** an agent moves many customers to higher prices. Mitigations: plan before apply, idempotent work, and cancel of migrations. | ||
| - **Who can access it:** the existing read and write scopes. Publishing a draft and moving customers check permissions again. Moving subscriptions needs `subscriptions:write`. Moving one-time orders needs its own write permission. | ||
| - **Data we store:** draft and published rows, and catalogs. No new personal data. | ||
| - **Risks:** an agent moves many customers to higher prices. Mitigations: agents propose drafts that a member publishes, publishing never moves customers, and moves are explicit and idempotent. |
There was a problem hiding this comment.
P2: This mitigation only holds for apply --draft. Plain apply publishes with no member review, so an agent can publish and then move customers. Either state that agents are limited to apply --draft, or name a separate control that stops an agent from publishing and moving customers in one go.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At handbook/engineering/design-documents/versioning.mdx, line 287:
<comment>This mitigation only holds for `apply --draft`. Plain `apply` publishes with no member review, so an agent can publish and then move customers. Either state that agents are limited to `apply --draft`, or name a separate control that stops an agent from publishing and moving customers in one go.</comment>
<file context>
@@ -144,125 +168,123 @@ export default defineConfig({
-- **Risks:** an agent moves many customers to higher prices. Mitigations: plan before apply, idempotent work, and cancel of migrations.
+- **Who can access it:** the existing read and write scopes. Publishing a draft and moving customers check permissions again. Moving subscriptions needs `subscriptions:write`. Moving one-time orders needs its own write permission.
+- **Data we store:** draft and published rows, and catalogs. No new personal data.
+- **Risks:** an agent moves many customers to higher prices. Mitigations: agents propose drafts that a member publishes, publishing never moves customers, and moves are explicit and idempotent.
## Appendix: Milestones
</file context>
| - **Risks:** an agent moves many customers to higher prices. Mitigations: agents propose drafts that a member publishes, publishing never moves customers, and moves are explicit and idempotent. | |
| - **Risks:** an agent moves many customers to higher prices. Mitigations: agents propose drafts with `apply --draft` that a member publishes, publishing never moves customers, and moves are explicit and idempotent. Plain `apply` (publish at once) must be restricted to members or to a permission that agents do not hold. |
| 4. **`is_current`.** It is `true` for rows in the live catalog. It is a fast way to filter for current entities and new sales. The list of rows in each catalog (catalog membership) is the source of truth. | ||
| 1. **Catalog.** It contains the full list of products and benefits at a point in time. It has a per-organization incremental `version` and a state: `draft`, `live`, or `archived`. Every organization has at most one draft and one live catalog. | ||
| 2. **Copy-on-write** for products and benefits. Every change to a product or benefit creates a new row in the draft catalog. Once the catalog is published, those rows are locked. | ||
| 3. **`external_id` is the stable name.** It is required for every product, benefit, and meter. A row is unique by `(organization_id, external_id, version)`, where `version` is the catalog where the row was created. |
There was a problem hiding this comment.
P3: This uniqueness rule makes meters catalog-versioned, contradicting the stated v0 scope. Limit the catalog-version uniqueness rule to products and benefits, and clarify meter identity in v0.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At handbook/engineering/design-documents/versioning.mdx, line 86:
<comment>This uniqueness rule makes meters catalog-versioned, contradicting the stated v0 scope. Limit the catalog-version uniqueness rule to products and benefits, and clarify meter identity in v0.</comment>
<file context>
@@ -12,58 +12,83 @@ description: "How meters, products, and benefits get versions."
-4. **`is_current`.** It is `true` for rows in the live catalog. It is a fast way to filter for current entities and new sales. The list of rows in each catalog (catalog membership) is the source of truth.
+1. **Catalog.** It contains the full list of products and benefits at a point in time. It has a per-organization incremental `version` and a state: `draft`, `live`, or `archived`. Every organization has at most one draft and one live catalog.
+2. **Copy-on-write** for products and benefits. Every change to a product or benefit creates a new row in the draft catalog. Once the catalog is published, those rows are locked.
+3. **`external_id` is the stable name.** It is required for every product, benefit, and meter. A row is unique by `(organization_id, external_id, version)`, where `version` is the catalog where the row was created.
+4. **Catalog membership is the source of truth.** `catalog_products` and `catalog_benefits` list the rows in each catalog. "Current" means "in the live catalog", and it is always derived from membership. We don't store an `is_current` flag. If performance requires one later, it is a cache enforced by a partial unique index on `(organization_id, external_id) WHERE is_current`.
</file context>
| 3. **`external_id` is the stable name.** It is required for every product, benefit, and meter. A row is unique by `(organization_id, external_id, version)`, where `version` is the catalog where the row was created. | |
| 3. **`external_id` is the stable name.** It is required for every product, benefit, and meter. Product and benefit rows are unique by `(organization_id, external_id, version)`, where `version` is the catalog where the row was created. Meters are not catalog-versioned in v0. |
Brings in the review from #15684 and the follow-ups: declarative config only, no migrations DSL (move endpoints instead), meters not versioned, grants re-pointed by benefit external_id, polar init for adoption, git-style catalog hashes, apply uploads a draft by default with --publish, and dashboard edits in the same draft. Registers the page in the handbook sidebar. Co-authored-by: Petru Rares Sincraian <psincraian@outlook.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
* feat: add versioning desing doc * feat: versionig v2 * Revert next-dev generated AGENTS.md/CLAUDE.md in apps/web These were auto-written by `next dev` and accidentally swept into the versioning commit; unrelated to this PR. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix: answered questions * docs: narrow versioning design doc scope Brings in the review from polarsource#15684 and the follow-ups: declarative config only, no migrations DSL (move endpoints instead), meters not versioned, grants re-pointed by benefit external_id, polar init for adoption, git-style catalog hashes, apply uploads a draft by default with --publish, and dashboard edits in the same draft. Registers the page in the handbook sidebar. Co-authored-by: Petru Rares Sincraian <psincraian@outlook.com> * docs: clarify versioning scope, draft iteration, and catalog hashes - versioning is only for orgs with declarative config; dashboard changes stay live right away for other orgs - apply replaces the draft in the same draft catalog, so merchants can iterate; it only needs --force when the dashboard changed the draft since the last apply or pull - the catalog hash includes the previous published hash, so going back to an older definition gets a new hash Co-authored-by: Petru Rares Sincraian <psincraian@outlook.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Review changes on top of #15332, based on
feat-versioningso the diff is the rewrite only.Decisions
external_id.applypublishes at once,apply --draftuploads for read-only dashboard review/publish/discard. No conflict/force/pull rules.catalogs/catalog_products. No storedis_current; a partial unique index is mentioned as a possible cache.last_billed_eventlock stays. Balance carry-over on move is the M3 direction; a balance shared across meter versions is ruled out.external_idthrough the strategy's existingupdate, so license keys, Discord roles, etc. aren't revoked and reissued on a move. Blocking stricter benefits was considered and rejected.product.createdfor the new row,product.updatedfor the row leaving the live catalog.The benefit → product cascade is kept and now explained: grants are rebuilt from
product.benefitson reactivation, seat claims, etc., so editing links in place would break the freeze for some customers.Also adds a
catalog_benefitsmembership table, since benefits are versioned too.For @psincraian
Petru's latest commit (
fix: answered questions) is merged in:🤖 Generated with Claude Code