Attribute pricing RFC, build plan, and catalog inventory

This PR creates the written contract and read-only inventory that gates the 16-PR attribute-pricing stack; it does not ship product behavior.

Author: @mileszim PR: dealops#7353 Stack: 1/16 Base: main Head: miles/attribute-pricing-01-rfc Files: 25 Diff: +5845 / -5 State: Open

What it adds

A complete RFC set for condition- and attribute-based pricing: roles, predicate datapoint resolution, conformance scenarios, and one workstream packet per implementation slice.

What it changes

Adds a read-only WS0 inventory script under apps/server/src/dealops4/__scripts__ plus Mocha tests for its pure analyzer.

What it proves

The checked-in V2 seed corpus has real selector-tag and round-trip constraints: 12/21 seed datasheets round-trip today; 9/21 are blocked by existing type or page-id behavior.

What it preserves

No migrations, no tRPC routes, no runtime pricing engine changes, and no product UI changes. The only shared code tweak is exporting an existing converter map for inventory classification.

RFC docs
Build plan
Inventory script
WS0 report
Architecture docs
Diff note: the supplied diff is truncated. This explainer uses the PR description, file list, and visible diff; it does not pretend to have reviewed every line of the RFC packet files.

1. Why this exists

This is the foundation PR for the attribute-pricing stack, not an implementation PR.

Problem
  • Attribute pricing spans model, publish, engine, lifecycle, approvals, totals, writeback, and UI.
  • Later PRs need stable names, decisions, fixtures, and workstream boundaries to avoid re-litigating core semantics.
  • WS3 depends on knowing which real datasheets can survive V2→V3→V2 parity checks today.
Fix in this PR
  • Commit the RFCs and workstream packets under rfcs/attribute-pricing/.
  • Commit the reviewed scenario catalog used by the future conformance corpus.
  • Commit a read-only inventory tool that surveys checked-in V2 seed datasheets and, optionally, one live org.
12/21 seed datasheets round-trip today
8 blocked by unknown datapoint type
1 blocked only by pageId
8,898 datapoints with extra selector keys
Stack position
#7353
RFC, build plan, inventory
#7354
Org setting
#7355
Shared types and roles
#7356
Conformance corpus
#7357–#7368
Engine, tables, UI, lifecycle, goldens

Each PR is intended to merge independently and in order; this one supplies the reference documents the rest cite.

2. What changes

The diff is mostly documentation, plus one pure analyzer and a thin CLI shell.

Area Files Reviewer read
RFC docs rfcs/2026-09-02-condition-attribute-based-pricing.md
rfcs/2026-09-08-predicate-datapoint-resolution.md
rfcs/2026-09-23-attribute-pricing-test-scenarios.md
Defines the architecture, predicate resolution rationale, and scenario catalog later PRs turn into tests.
Build plan rfcs/attribute-pricing/README.md
rfcs/attribute-pricing/01-model.md–04-conformance.md
rfcs/attribute-pricing/10-ws-model.md–18-ws-approvals.md
Handoff packets per workstream. The README is the intended first review stop; decisions D13–D18 remain the active debate surface.
Inventory script apps/server/src/dealops4/__scripts__/inventoryAttributePricing.lib.ts
apps/server/src/dealops4/__scripts__/inventoryAttributePricing.ts
apps/server/src/dealops4/__scripts__/__tests__/inventoryAttributePricing.test.ts
Pure analyzer, CLI wrapper, and Mocha tests. It reads seed files by default and writes only the report file path requested by the user.
WS0 report rfcs/2026-09-03-ws0-attribute-pricing-inventory.md Generated inventory of checked-in V2 seed datasheets: round-trip gates, extra selector keys, ambiguous groups, sibling counts, and grid equivalents.
Architecture docs knowledge/architecture/dealops-1-vs-2.md
knowledge/architecture/index.md
Clarifies that dealops3 and dealops4 are Dealops 2 layers, and that attribute-pricing feature code lives under apps/server/src/dealops4/.
Export-only code apps/server/src/dealops3/datasheet/converter.ts Exports VALUE_TYPE_MAP so the inventory can test type membership without invoking a throwing converter path.
Start
README decisions
Read rfcs/attribute-pricing/README.md first.
Then
Model / resolution / trace
Use 01–04 as the executable spec spine.
Check
WS0 inventory
Verify the corpus findings that gate converter and parity design.
Argue
D13–D18
These are the decisions the PR description calls out as still worth debating.

3. How it works

The only executable path added here is the inventory tool; it is intentionally pure-core / thin-shell.

Pure core
  • analyzeOrg(args) builds one org inventory row
  • collectProductInputSelects(flowSpec) finds pricing-role precedents
  • buildGateVerdict(inventories) aggregates corpus blockers
  • renderMarkdownReport(...) formats the WS0 report
Thin shell
  • --source=disk reads dealops2/onboarding/*
  • --source=db --org=<name> reads one live org
  • --out=<path> writes the markdown report
  • DB import is lazy, so default disk mode never opens Prisma
Classification rules pinned by tests
Question Rule in code Why it matters for later PRs
Which selector keys are “extra”? Everything except type, productId, and pricebookId. WS3 needs to know whether unknown selector keys can safely become predicate clauses.
Which datapoint types block conversion? Types absent from both VALUE_TYPE_MAP and VARIANT_TYPE_MAP. Those orgs cannot be in a byte-identity V2→V3→V2 parity corpus.
Which page IDs block byte identity? Anything other than listPrice, because the V3→V2 converter writes that page ID. Some failures predate attribute pricing; the stack should not treat them as regressions.
Where is lookup ambiguous? Groups with the same (type, productId, pricebookId) and multiple datapoints. Dimension-less lookups already resolve by array order in live seed data.
Which attributes already act like pricing dimensions? VARIANT_TYPE_MAP usage plus select product inputs in V2 flow specs. WS1 backfill can classify only the small set with real precedent.
Inventory result shape
12 convertible
8 blocked by unknown type
1 blocked by pageId only

The headline consequence: WS3 should scope byte-identity parity to the convertible orgs and separately assert the blocked orgs remain unchanged.

Visible code sample

The shared converter change is export-only:

// Exported so the WS0 inventory can test type membership.
export const VALUE_TYPE_MAP: Record<string, V3DataPointValueType> = {
  LIST_PRICE: 'list_price',
  MIN_PRICE: 'min_price',
  DISCOUNT_APPROVAL: 'discount_approval',
  ...
};
Script safety model
  • Default source is disk, not database.
  • DB mode requires --org and prints the sanitized host before querying.
  • The analyzer is pure; filesystem and Prisma stay in the shell.
  • The script writes only the requested markdown report.

4. What it doesn’t change

Explicit non-goals for this PR

5. Risks / rollback / open questions

Review risk: most of the diff is specification text. The highest-value review is consistency, not syntax: make sure the RFC, build plan, scenario catalog, and WS0 report all describe the same stack and the same gates.
Rollback

Rollback is straightforward: revert the docs, the read-only script, its tests, and the export of VALUE_TYPE_MAP. No persisted product behavior depends on this PR.

Open questions
  • Decisions D13–D18 in rfcs/attribute-pricing/README.md are still the intended debate surface.
  • The truncated diff prevents line-by-line validation of every workstream packet.
Follow-up dependency

Later implementation PRs should cite these docs by section. If reviewers change a core decision here, the stack should be updated before downstream PRs merge.

Reviewer checklist