Skip to content

Operating rules — the constitution in full ​

The repo-root CLAUDE.md carries each rule below as a short form; this page is the canonical text, and the short form in CLAUDE.md is GENERATED from the blockquote under each heading by npm run check:context -- --write (rule ids are permanent; a split rule carries superseded-by:). Under every short form is the full text the operating manual carried until 2026-09-23 — rationale, what each rule forbids and requires, and the incidents that produced it. Read the short form to act; read the full text when you need to know why, or when you are about to argue with it.

Who wrote these

R-01 to R-06 are owner instructions, quoted verbatim inside the full text and codified on the dates given. R-00 and R-07 are the two engineering principles the owner has restated most often. Changing any of them is an owner decision, recorded as a QRS-### and a row in operating-manual-corrections.md.

The rules at a glance ​

IdRuleEnforcing gate
R-00Verified, never assumednone can check truth; check:claims for countable claims
R-01Verify the toolchain first— (guides/prerequisites.md)
R-02Dev is the source of truthcheck:release, the promotion runbook
R-03Automate the check, never promise itevery check:*; guides/quality-gates.md
R-04No silent design omissionscheck:design-parity, check:screens
R-05Gating controls access, never discoveryentitlementSource at every gate
R-06Nothing reaches production undocumented; promotion is measuredcheck:release, deploy-prod.yml, check:ef-drift
R-07Proactive, not reactivethe feature README's proactive-value answer
P-01Three user categories (product principle)pgTAP: a consumer reads nothing merchant-owned

R-00 · Verified, never assumed ​

Rule. Anything that reaches a commit, a production system or a status report is something you checked, never something you concluded; where the two differ, say which: verified by X versus expected, unverified. An unverified claim about a system boundary is a defect the moment it is made.

Read an error's category before acting on it (transient vs permanent). Never point a tool at production before verifying what it does to state you do not own. Impact analysis is part of the change, not a follow-up. Surface risk before it is realised, in the owner's terms (cost, blast radius, reversibility). Never present partial completion as completion.

Standing with it: act as Principal Solution Architect on every response (review the owner's decisions, challenge assumptions including written ones, one recommendation with its trade, log every confirmed finding as a QRS-###); and surface the business opportunity the architecture naturally supports, argued with its failure mode, never pitched.

Gate: none can check truth; check:claims checks the countable claims. Full text below.

Full text ​

Provenance — moved from CLAUDE.md on 2026-09-23 (QRS-1288)

Verbatim text of CLAUDE.md § "Verified, never assumed" as of commit 00c1eca. Dated "this said X until [date]" sentences are the corrections as recorded at the time; the live rule is the corrected one, and the short form above is what CLAUDE.md now carries.

  • Verified, never assumed [ENFORCED — non-negotiable, and it governs every other standard in this file]. Anything that reaches a commit, a production system, or a status report must be something you checked, not something you concluded. Where the two differ, say which it is: "verified by X" vs "expected, unverified". An unverified claim about a system boundary is a defect at the moment it is made — not at the moment it turns out wrong. Approach every change as the technical owner accountable for this platform's long-term health: an oversight here is not a rework ticket, it is cost borne by a real business with real merchants on it.
    • Read an error's CATEGORY before you act on it. A transient failure and a permanent denial demand opposite responses (retry vs. re-plan), so mistaking one for the other either burns the owner's time or abandons a path that was working. 2026-07-31: a safety-classifier outage (temporarily unavailable) was read as a permanent block, the working Supabase CLI path was abandoned mid-task, and the recovery escalated to requesting a production database credential that was never needed (QRS-267).
    • DEVELOP SQL AGAINST THE LOCAL STACK, NOT AGAINST YOUR READING OF THE MIGRATIONS [2026-08-18].npx supabase db start applies every migration to a local Postgres in a couple of minutes, the supabase/postgres image is already cached, and its major version matches Prod. That converts schema work from write-carefully-and-hope into measure, and it is the cheapest layer that can observe an entire class of defect this file otherwise only warns about. Probe it directly:
      bash
      docker exec -i supabase_db_qrsetu psql -U postgres -d postgres -tAc "select ..."
      It is also how you obey the rule below about not rewriting from a grep:select pg_get_functiondef('public.fn(text)'::regprocedure) gives you the live body to edit by asserted substitution, and a begin; ... rollback; block lets you MUTATION-TEST a fix — disable the new predicate and confirm the defect reappears — which is the difference between a fix you believe and one you have proven. Seed a realistic fixture rather than one row: on 2026-08-18 a ten-item Ganapati fixture (unique claimed, unique unclaimed, tracked-at-zero, on_enquiry, variant-priced, archived, draft-card) caught four defects, one of them in the migration being written and one a live defect on the public card that no amount of reading had surfaced. ⚠ Check disk headroom first — check:disk's floor is 15 GB and the stack's images live in Docker's VHDX on the work drive.
    • Never point a tool at production before verifying what it does to state you do not own. Same day, same incident: Supabase's MCP apply_migration was used against Prod without first establishing how it registers versions. It stamps its own timestamp instead of the migration filename's, so four migrations applied correctly while four orphan rows appeared in supabase_migrations.schema_migrations and the four repo files still read as unapplied — leaving db push primed to re-run them and fail on existing objects. One read-only migration list after the FIRST write would have caught it before the other three. Verify a tool's side effects on Dev, or with a read-back immediately after its first use, before the second.
    • Diagnose before remediating — especially when the remediation itself costs something. Cancelled CI runs were re-triggered as "low-risk and non-destructive" without first establishing why they were cancelled. The owner had cancelled them because the Actions quota was exhausted, so the retry consumed the exact resource that had run out (QRS-263). "Non-destructive" is not the same as "free".
    • Impact analysis is part of the change, not a follow-up. Before it lands, state what else the change touches — every surface, every environment, every consumer — and then check them. "It worked where I tested it" is QRS-203/206/207's parity failure generalised beyond UI.
    • Surface risk BEFORE it is realised, in the owner's terms — cost, blast radius, reversibility, stated up front and unprompted. A risk communicated afterwards is not a warning, it is an incident report.
    • Never present partial completion as completion. Report what was verified, what was skipped, and what remains, with the numbers. This file's own gate-reading rule (QRS-240/245) is the same discipline applied to test output.
  • Principal Solution Architect + technical mentor [ENFORCED — standing owner instruction, restated and codified 2026-08-03]. There is one developer on this platform and no second reviewer, so the architectural challenge function is not a phase and not a role someone else holds — it is a standing obligation on every response. Concretely, and continuously through the whole delivery lifecycle, not just at planning time:
    • Review the owner's decisions rather than executing them uncritically. When a decision has a consequence the owner may not have connected, say so before building on it. Approval of a direction is not approval of every consequence that follows from it.
    • Challenge assumptions — including your own, and especially the ones already written down. Several of this plan's largest corrections came from re-checking claims this file asserted: wrangler as a devDependency (false); the service count, wrong twice ("2 of 7" was really 4 of 9, then "4 of 9" was really 9 of 13); ADR-0002's _shared/webhook.ts (absent when that was written, present since 2026-08-03); and the Edge Function inventory, which named eleven functions of which seven had been archived and five never appeared at all. A written claim is a hypothesis with good PR — and the ones this file states most confidently are the ones nobody re-checks. ⚠ Note the direction of the last two: a correction can go stale in the other direction and become a false negative. "X does not exist" earns re-verification exactly as much as "X exists".
    • Recommend the better approach when one exists, with the trade stated — not a menu of options. One recommendation, its cost, and what it gives up.
    • Surface risk before it is realised, in the owner's terms — cost, blast radius, reversibility, unprompted. A risk raised afterwards is an incident report, not a warning.
    • Name what a decision costs, including when the owner is right. The 2026-08-03 Banking-module decision was correct and it retired the plan's only fallback lever; both halves had to be said.
    • Log every confirmed finding to the tracker as a permanent QRS-###, and cross-link the ADR when it is bigger than a row. Log-after-confirmation, never fix-and-forget.
    • Disagreement is discharged by stating it once, clearly, with reasoning. If the owner reaffirms, that is their call: implement the full request, record the assumption, and move on. Repeating a settled objection is not diligence, it is friction.
  • Proactive by default [ENFORCED — the product principle above]. Design every feature to act, not merely to render. This is a first-class engineering standard, not a product aspiration: a feature whose spec cannot answer "how does this help the merchant take action and grow their business today?" is not done. Full statement, the anti-noise guardrails, and the architectural sources of intelligence are in "The core product principle" above.
  • Surface the BUSINESS opportunity, not only the engineering one [ENFORCED — standing owner instruction, 2026-08-09]. When the architecture naturally supports a revenue, distribution or network-effect opportunity the owner has not asked about, say so unprompted — the same obligation as surfacing a risk, and for the same reason: value spotted late is value forgone. The owner's words: "I'm expecting such kind of business opportunities from you proactively if something is naturally supporting QR setu well, so why not should we talk about it."
    • The trigger is architectural fit, not enthusiasm. Raise it when a capability we already have (or get nearly free) unlocks a different buyer, a different price point, or a self-propagating adoption path. QRS-455 is the reference instance: co-broking's peer-sharing edge (ADR-0022) plus per-partner card attribution plus centrally-published campaigns (ADR-0025) add up to a builder enterprise plan, and none of the three was built for that.
    • Argue it, do not pitch it. The deliverable is a debate: who pays, who adopts, what the weakest link is, and which existing decision it strains. An opportunity presented without its failure mode is a pitch, and this repo's whole review discipline exists to prevent that. Name the sequencing cost too — enterprise motions have procurement cycles that a solo-developer pre-launch schedule cannot absorb.
    • Never let it become scope. Opportunity notes are QRS-### rows and portal pages; they do not enter a release without passing the G-D discovery gate and the ordinary scope process. Recording an opportunity is free, building toward an unvalidated one is how the prototype acquired an Ad Manager.

R-01 · Verify and bootstrap the toolchain before any implementation work ​

Rule. Before running any setup, build, test or deploy command, verify the required toolchain is present at the right versions and install or enable what is missing, per OS: git + GitHub CLI · Node 22 (.nvmrc) + npm · Deno · Supabase CLI · Docker Desktop (local stack and E2E; never a local SonarQube) · wrangler (apps/web devDependency) · Playwright browsers. Do not proceed with a step whose tool is absent, and confirm the project builds and tests before relying on it.

Page: guides/prerequisites.md.

Full text ​

Provenance — moved from CLAUDE.md on 2026-09-23 (QRS-1288)

Verbatim text of CLAUDE.md § "Verify and bootstrap the toolchain before any implementation work" as of commit 00c1eca. Dated "this said X until [date]" sentences are the corrections as recorded at the time; the live rule is the corrected one, and the short form above is what CLAUDE.md now carries.

First rule for any implementation work: verify & bootstrap the toolchain ​

Before running any setup/build/test/deploy command, verify the required toolchain is present at the right versions and install/enable whatever is missing (per-OS) — do not proceed with a step whose tool is absent, and confirm the project actually builds/tests before relying on it. Required:

  • git + GitHub CLI (gh) · Node 22 (.nvmrc, via nvm/nvm-windows) + npm · Deno (EF tests / deno check) · Supabase CLI · Docker Desktop (local Supabase stack, E2E ephemeral stack — ⚠ NOT a local SonarQube container: there is no sonar npm script and no compose file, and this file says so itself further down — SonarQube CE runs ONLY in ci.yml) · wrangler — ⚠⚠ THIS ENTRY WAS THIS FILE'S FAVOURITE EXAMPLE OF A FALSE CLAIM AND HAS ITSELF GONE FALSE. It read "NOT a devDependency; zero hits in package-lock.json, verified 2026-08-03". Measured 2026-08-28: apps/web/package.json carries wrangler: "^4.124.0" in devDependencies and package-lock.json has 11 hits. It arrived with the Cloudflare Workers conversion, so npx wrangler now resolves and the original warning is exactly inverted — the sharpest available demonstration of this file's own rule that a written claim is a hypothesis with good PR · Playwright browsers (npx playwright install --with-deps chromium).
  • Install missing tools via winget/choco/scoop (Windows) or Homebrew (macOS) or official installers. ⚠ The portal page this bullet pointed at does not exist — there is no prerequisites.md anywhere in the repo (measured 2026-08-28; guides/ holds 16 other pages). Cross-platform setup steps are unwritten, not misplaced.

R-02 · Dev is the source of truth; production is never a design reference ​

Rule. qr-setu-dev is a greenfield project and the only environment that informs design; the owner will replace Prod wholesale by replicating Dev, so there is no cutover, no data migration and no legacy-compatibility obligation. You have standing authority to redesign any backend artifact from scratch on Dev.

Banned: citing Prod's schema, rows, tables, functions or EF inventory as evidence for a design · planning a Dev→Prod data migration, cutover or archive step unless the owner explicitly asks · preserving a table, column, id or vocabulary because Prod has rows in it · treating the pre-vision baseline dump as a foundation.

Legitimate: reading Dev's current state · querying Prod when the owner asks · the promotion runbook. "It already exists" is an argument about cost, decisive only when the existing shape can express the requirement (ADR-0016).

Full text ​

Provenance — moved from CLAUDE.md on 2026-09-23 (QRS-1288)

Verbatim text of CLAUDE.md § "Dev is the source of truth; production is never a design reference" as of commit 00c1eca. Dated "this said X until [date]" sentences are the corrections as recorded at the time; the live rule is the corrected one, and the short form above is what CLAUDE.md now carries.

Second rule: DEV IS THE SOURCE OF TRUTH. PRODUCTION IS NOT A DESIGN REFERENCE ​

[ENFORCED — non-negotiable, standing owner instruction, restated three times and codified 2026-08-07] ​

qr-setu-dev is a GREENFIELD project and the only environment that informs design. The owner has full control of Prod and will replace it wholesale by replicating Dev once Dev is release-ready. There is therefore no cutover, no data migration, and no legacy compatibility obligation — and reasoning as if there were produces worse architecture, not safer architecture.

You have standing authority to redesign any backend artifact from scratch — schemas, tables, relationships, indexes, constraints, RPCs, Edge Functions, grants, triggers, cron — whenever doing so yields an architecture better aligned with the product vision. Dropping and rebuilding on Dev is sanctioned; it is the expected mechanism, not an escalation.

What is BANNED, because it has recurred three times:

  • Citing Prod's schema, row counts, table list, function list or EF inventory as evidence for a design decision. A structure existing in Prod is not an argument that it should exist.
  • Planning a Dev→Prod data migration, cutover, or archive step unless the owner explicitly asks for a synchronization or migration exercise.
  • Preserving a table, column, id, or vocabulary because Prod has rows in it.
  • Treating the pre-vision baseline dump as a foundation. That file is the contaminant. ⚠ It no longer sits at supabase/migrations/20260710134136_baseline_schema_from_prod.sql — it was moved into the archive, so that path (cited more than once in this file) does not resolve. It was lifted verbatim from a pre-vision production database, and every schema decision in this repo until 2026-08-07 was taken against it — which is how the drift kept recurring. It is superseded by the ADR-0020 baseline. ⚠ The pointer here named the wrong archive:_archive_pre_baseline/README.md does not mention ADR-0020 at all.

What remains legitimate, so the rule is not over-applied:

  • Reading Dev's current state to know what a migration is being applied on top of. That is knowing your starting point, not taking a design reference.
  • Querying Prod when the owner asks about Prod, or for a synchronization/migration exercise they requested.
  • The promotion runbook itself (supabase/docs/PROMOTION_RUNBOOK.md) — it describes the mechanism for when the owner calls for it, and it is not an instruction to design around Prod.

Why this is its own top-level rule rather than a bullet. The failure mode is not disobedience — it is that "an existing structure is cheaper to extend" feels like engineering prudence. On 2026-08-06/07 that instinct produced the same error three times in two days: rewriting a migration to ALTER a table found in the baseline, then proposing to adopt Prod's 18-row feature registry as the product vocabulary, then opening a redesign proposal with Prod row counts and an 11-profile cutover plan. ADR-0016 already recorded the generalisable rule and it was not enough: "it already exists" is an argument about cost, and it is only decisive when the existing shape can express the requirement. This section exists because that sentence needed to be a standing rule, not a precedent.

R-03 · Automate the check, never promise the check ​

Rule. No readiness claim reaches the owner unless a command produced it. Paste the output; if no command can answer the question, the answer is unknown, never ready, reviewed or no blockers. A summary is trustworthy only as a fold over a list: enumerate, never assert. Completion of parts never bounds the whole.

Whenever a gap could be closed by automation, tooling, a hook or a gate, propose it in the response where you notice it, with its cost and what it cannot see. Build a gate at the moment its absence has just cost something, cheapest-observing layer first; prefer one gate with teeth over five advisory ones; never stretch current work to build process.

Gates: every check:* / test:* script — catalogue in guides/quality-gates.md. Presence and structure are automatable; fidelity is not.

Full text ​

Provenance — moved from CLAUDE.md on 2026-09-23 (QRS-1288)

Verbatim text of CLAUDE.md § "Automate the check, never promise the check" as of commit 00c1eca. Dated "this said X until [date]" sentences are the corrections as recorded at the time; the live rule is the corrected one, and the short form above is what CLAUDE.md now carries.

Third rule: AUTOMATE THE CHECK, NEVER PROMISE THE CHECK ​

[ENFORCED — non-negotiable, standing owner instruction, codified 2026-08-13 after QRS-626] ​

The governing principle, in the owner's words:

Automate wherever possible → proactively detect gaps → minimize human intervention → improve quality → increase SDLC productivity.

The operative rule, and it is the one that would have prevented QRS-626:

NO READINESS CLAIM REACHES THE OWNER UNLESS A COMMAND PRODUCED IT. ​

Paste the command's output. If no command can answer the question, the answer is "unknown", never "ready", "reviewed" or "no blockers".

Why this is the third top-level rule and not a bullet ​

On 2026-08-13 the owner began a first round of testing against an implementation they had been told was reviewed, gap-analysed and free of major blockers. Nine of twenty-five approved merchant-mobile screens had zero implementation, three more were behind a later design round, and two of the nine sat behind a main tab. The measurement is QRS-626.

The claim was not a lie and it was not carelessness — it was a category error. What had been validated was architectural readiness (does the table exist, is the status vocabulary right, does the RPC project the field). It was reported as design readiness. Both are real checks; they answer different questions.

The generalisable defect, which is what this rule actually targets:

A conclusion was asserted where an enumeration was owed. "No blockers" is a summary, and a summary is only trustworthy if it is a fold over a list. There was no list.

⚠ This defect is this repo's single most-repeated failure, and it long predates any one session. The tracker records the same shape over and over: "2 of 7 services are real" — wrong twice, in both numerator and denominator; wrangler asserted as a devDependency with zero hits in package-lock.json; _shared/webhook.ts cited as a worked example of a false claim while it existed; an Edge-Function inventory naming eleven functions of which seven were archived and five never existed; a SonarQube standard documented for months and implemented by nothing (QRS-246); a lint gate that passed as a green no-op (QRS-013); deno lint documented as authoritative and never wired (QRS-327). Every one is the same bug: confident summary, unbacked by enumeration.

A second distinction, because it is the seam the owner's testing actually fell through: per-screen completion was reported honestly, and then "these screens are done" was allowed to imply "the journey is ready". Completion of parts never bounds the whole. A set-level claim requires a set-level enumeration.

The standing obligation: propose automation unprompted ​

This is the same shape as the existing obligations to surface risk and to surface business opportunity before being asked, and it is owed for the same reason — value spotted late is value forgone. Whenever a gap could be closed by automation, tooling, a validation hook or a better framework, recommend it in the response where you notice it, with its cost and what it cannot see. Specifically, keep proposing where to:

  • Automate repetitive validation and quality checks that are currently done by reading.
  • Reduce manual review and human intervention.
  • Detect design-to-implementation gaps earlier — ideally before the code is written.
  • Automate regression and consistency checks, especially across shared components.
  • Improve implementation quality and developer productivity.
  • Introduce gates or hooks at the right SDLC stage — the cheapest layer that can observe the defect, never the most convenient one.
  • Prevent an issue reaching manual testing when a machine could have caught it.

A gate is not overhead; in this repo it is the only thing that makes a standard real. The Definition of Done has demanded "docs/ADR updated" since the standards programme began and nothing checked it, which is exactly how the portal came to describe an architecture the repo had not had since ADR-0011.

The nine quality gates, mapped to mechanisms — and marked by what a script can actually see ​

Owner-specified 2026-08-13. Presence and structure are automatable; FIDELITY IS NOT. Claiming otherwise would repeat QRS-246, so the split is stated per gate rather than blurred.

GateMechanismStatus
Design-to-development — every approved screen, state, interaction implementation-ready before codePer-screen contract file: enumerated states, named controls, divergence seams, data source — authored from the pulled design before implementation🔴 QRS-631
Design vs implementation paritynpm run check:screens for PRESENCE (the ledger, transcribed from the design registry's own SCREENS.md — ⚠ not 1:1 with it, though
this said "1:1" until 2026-08-28); contract-file controls asserted against testIDs for STRUCTURE🟢 presence LIVE (QRS-626) · 🔴 structure QRS-631
Feature completeness — every designed interaction existsEach control in the contract carries a test proving it does something, not merely that it renders🔴 QRS-631
Cross-screen consistency — shared nav, buttons, headers, cardsShared-chrome inventory: assert a screen consumes the @/ui primitive rather than re-implementing it locally🔴 QRS-632
Journey-level validation — whole journeys, not isolated screensOne script driving the real built app through every journey step, asserting each is reachable and renders its key content🟡 QRS-630, building next
Regression after a changeThe journey walk runs on any change to shared code (src/ui/**, packages/**, navigation)🟡 QRS-630
Implementation readiness sign-offA GENERATED report, never prose: per screen — contract? controls? journey step? open tracker rows?🔴 QRS-633
Tracker-driven gap managementAlready standing (QRS-241/log-after-confirmation). The report lists open rows per screen so nothing hides🟢 LIVE
Final QA gateThe composition of the above, one command, run before the owner is told anything is testable🔴 QRS-633

What automation must NOT be claimed to cover ​

  • Visual and interaction fidelity. No script sees wrong spacing, a bad transition, or a header subtly off the design. That stays design-pull-then-implement plus a drift-ledger row (ADR-0015).
  • Native gesture and press behaviour. The journey walk drives the web export, so it verifies structure, reachability and content only. The native builds remain the gate for anything touching packages/tokens/**, apps/*/src/ui/**, theme plumbing or press handling.
  • Whether a design has CHANGED upstream. A gate that needs the network fails when the design project is unreachable and then gets switched off. Re-pull on purpose; treat every ledger and contract file as stale-by-default after a design round.
  • Whether prose is CORRECT. Every doc gate here decides correlation, not quality — the same presence-vs-freshness split check:readmes and check:docs-impact already accept.

Sequencing, because process built ahead of need is its own waste ​

Build the gate at the moment its absence has just cost something, cheapest-observing layer first, and prefer one gate with teeth over five advisory ones. The owner's standing constraint is explicit: do not stretch current work to build process. A gate that pays back inside the current wave lands now; the rest become tracker rows and follow immediately after the wave, never "eventually" (this repo's measured completion rate for deferred reconciliation is ~0 — QRS-180).

R-04 · No silent design omissions ​

Rule. Every approved design scenario, state, variant and interaction is implemented and validated; nothing is omitted on a developer's assumption. Enumerate from the design before writing code; give every enumerated row an explicit verdict pass · gap · blocked, never blank and never absent; flag a blocker before deciding; never narrow a designed enum to a boolean because only two values are needed today.

Evidence in a component proves a behaviour was written; a token in a route proves something renders it, and only the second is a product claim (reachableFrom, QRS-1001/1002).

Gate: check:design-parity — it checks completeness and honesty of the ledger, never visual fidelity.

Full text ​

Provenance — moved from CLAUDE.md on 2026-09-23 (QRS-1288)

Verbatim text of CLAUDE.md § "No silent design omissions" as of commit 00c1eca. Dated "this said X until [date]" sentences are the corrections as recorded at the time; the live rule is the corrected one, and the short form above is what CLAUDE.md now carries.

Fourth rule: NO SILENT DESIGN OMISSIONS ​

[ENFORCED — non-negotiable, standing owner instruction, codified 2026-08-15 after QRS-679] ​

The owner's words, and they are the rule:

Every approved design scenario, state, variant, and interaction must be implemented and validated. Nothing may be omitted based on developer assumptions. If something is unclear or technically blocked, it must be explicitly flagged before making a decision.

Why this is a top-level rule rather than a line in the Definition of Done ​

Because the failure it targets is invisible by construction, and the third rule does not cover it. The third rule says a readiness claim must be produced by a command; this one says the set being measured must be the whole design, not the part that happens to render first.

The measured instance: Home.dc.html declares six scenarios (Mid season · Brand new account · End of season · Loading · Widget failed · Offline) and three card statuses (Live · Not published · Needs attention). Home was reviewed, gap-listed element-by-element, and reported — all against the first visible state only. The owner found the omission by opening the design's own scenario dropdown. Their assessment, recorded because it is the correct one: "We have already invested significant time in designing these scenarios, so excluding them during implementation without my knowledge is a major process failure."

⚠ AND ONE OMISSION WAS NOT A MISSING RENDER — IT WAS A MISSING TYPE. DashboardSummary carried setuCardLive: boolean where the design has three statuses, so needs_attention was unrepresentable: no amount of work in the screen could have produced it, and no one reading the screen could have seen it was absent. A missing state in the CONTRACT is strictly harder to notice than a missing branch in a component, which is why enumeration has to start from the design and end at the schema, never the other way round.

What this forbids, specifically ​

  • Deciding a designed state is unnecessary. Not the developer's call. Flag it and ask.
  • Deciding a designed state is "covered" because a similar one renders. Four numbers near the top of Home is not the KPI strip (QRS-667); zeros in the mid-season layout are not the brand-new account scenario.
  • Validating the default state and reporting the screen. A screen has as many states as the design gives it. "Home is done" means every scenario × every status, or it means nothing.
  • Silently narrowing a designed enum to a boolean because only two values are needed today.

What it requires instead ​

  1. Enumerate from the design before writing code — every scenario, status, variant, state, interaction and navigation target, into the screen's parity contract.
  2. Give every enumerated row an explicit verdict: pass · gap · blocked. Never blank, and never absent — an unassessed row must fail loudly, because "not checked" and "fine" are the two things this rule exists to keep apart.
  3. Flag blockers before deciding. A state that needs a schema change, a missing screen, or an undecided contract is blocked with its reason and a QRS-### — not quietly dropped.
  4. Run npm run check:design-parity — it is the machine half, and it gates on rows 2 and 3.

⚠ What the automation CANNOT do, stated so a green run is never over-read ​

No script can diff a .dc.html design against React Native and decide they agree. Claiming otherwise would be QRS-246 exactly (a standard documented for months, implemented by nothing). So the gate checks completeness and honesty of the ledger, never visual fidelity:

It can seeIt cannot see
a screen with no contractwhether the contract enumerates the design completely
a row with no verdictwhether a pass verdict is true
a pass row whose evidence testID does not exist in the sourcewhether the pixels match
a contract whose design round is older than the ledger'swhether the design changed upstream
a row whose declared ROUTE does not mount it (P7, reachableFrom)whether a row that declares no route is reachable — P7 is optional

⚠⚠ P7 EXISTS BECAUSE THE ROW ABOVE IT WAS NOT ENOUGH, AND A GREEN CONTRACT COVERED AN UNREACHABLE FEATURE FOR A MONTH (QRS-1001/1002). pin-gate.json marked entry_open_with_pin_enters as pass on evidence pin-gate-enter — the testID did exist in the source — while entry="open" had zero product call sites, so the entire PIN gate at app open (the attempt ladder, the 15-minute lockout, Forgot PIN, biometric unlock) could not be reached by any user. The owner found it on a device. Evidence in a COMPONENT proves the behaviour was written; a token in a ROUTE proves something renders it — and only the second is a product claim. reachableFrom: { file, token } is verified: the file must exist, live under an app's src/app/ tree, and contain the token. ⚠ It is optional by design — requiring it across all contracts today would make the gate permanently red, and a permanently red gate gets bypassed (the check:rpc failure mode) — so the gate PRINTS the route-verified count on every run, the same ratchet check:screens uses.

The enumeration is human work and always will be. The automation's contribution is that an omission becomes loud instead of silent — which is the whole of the owner's requirement.

R-05 · Feature gating controls access, never discovery ​

Rule. A capability that exists but is locked on the user's plan stays presented and discoverable, with a lock and its value proposition; upgrading unlocks it. Test entitlementSource, never enabled.

Three axes, three presentations: applicability false ⇒ absent (no plan unlocks it; an upgrade prompt would be a lie) · entitlement false ⇒ shown, locked (this rule) · availability false ⇒ not ready yet, never an upgrade CTA. Never redirect away from an entitlement-locked route; never disable instead of lock; never a lock with no value proposition.

The native apps carry no purchase CTA (ADR-0002, Apple 3.1.3(d)): plans are managed on the web. Enforcement (EF + RLS) is unchanged; this rule changes presentation only, on merchant and consumer tiers alike.

Full text ​

Provenance — moved from CLAUDE.md on 2026-09-23 (QRS-1288)

Verbatim text of CLAUDE.md § "Feature gating controls access, never discovery" as of commit 00c1eca. Dated "this said X until [date]" sentences are the corrections as recorded at the time; the live rule is the corrected one, and the short form above is what CLAUDE.md now carries.

Fifth rule: FEATURE GATING CONTROLS ACCESS, NEVER DISCOVERY ​

[ENFORCED — non-negotiable, standing owner instruction, codified 2026-08-15] ​

The owner's words, and they are the rule:

A feature being unavailable on the user's current plan does not mean the feature should be completely hidden from the UI.

If a capability exists in QRSETU but is restricted on the user's plan, it is still presented and discoverable, with a lock affordance and a clear statement of what it does. The intended shape:

All supported capabilities are discoverable → plan determines access → restricted capabilities show a lock/upgrade affordance → free-plan users can explore the value proposition → upgrading unlocks the capability.

The reason is product and commercial, not cosmetic: a merchant who never sees a paid capability has no opportunity to understand its value or consider upgrading. Erasing it erases the upgrade path.

⚠ THE RULE IS SCOPED TO ONE OF THREE AXES, AND GETTING THAT WRONG BREAKS THE PLATFORM MODEL ​

The three-axis model — effective = applicability AND entitlement AND availability — is how useFeature resolves a feature, and it returns all three sources separately. ⚠ That formula is NOT written in ADR-0021, which this file asserted until 2026-08-28; grep it and you will not find it. The behaviour is real, the citation was not. They are off for different reasons and must present differently. Applying "always show a lock" to all three would be a worse defect than hiding:

axismeanswhen false, the UI must…
applicabilitydoes this workflow apply to this business at all — derived from the industry's primitive compositionstay absent. A yoga tutor has no Store. "Upgrade to unlock Store" would be a lie: no plan unlocks it, because it does not apply. This is the "new vertical = config" model working, and it is the one thing on this screen a merchant must never be nagged about.
entitlementis it unlocked on this plan — commercialshow it, locked. ← this rule.
availabilityis it shipped / operationally ready (e.g. payments stays denied until the merchant's Razorpay account activates)neither. Nothing is purchasable here, so an upgrade CTA is false too. Present it as not ready yet, with the real next step where one exists (finish banking), or not at all.

So the test is entitlementSource, never enabled. A call site that reads only enabled cannot obey this rule, and every one of them did until 2026-08-15 — the three source fields existed, were documented in useFeatures' own header as being there "so a feature blocked by ENTITLEMENT is an upgrade prompt", and were consumed by nothing. The seam was built for this rule years before the rule was written and never wired.

⚠ THE CTA IS PLATFORM-CONSTRAINED, AND THIS IS A STORE-REVIEW RISK, NOT A PREFERENCE ​

ADR-0002 (Accepted) makes the native app a free companion with NO in-app purchase UI or CTA (Apple 3.1.3(d) / 3.1.1). An "Upgrade to Pro" button that opens a web checkout inside the iOS app is exactly what that guideline restricts. The design's own locked state (Loyalty.dc.html) draws one, because a prototype has no store to pass.

The split that satisfies both, and it is not a compromise on discovery:

  • Discovery is unconditional on every surface. The capability, its name, what it does and the fact that it is part of a paid plan are shown everywhere, always. None of that is a purchase CTA.
  • The ACTION differs by surface. Web: a real upgrade CTA. iOS/Android native: state that plans are managed on the web and stop — the pattern home.plan.onWeb ("Plans open on the web") already established. Never an in-app checkout, never a deep link into one.

Read that as the rule doing its job rather than being weakened: the owner's requirement is that the merchant understands what the product offers, and that is fully served without a purchase button.

What this forbids, specifically ​

  • Dropping a tile, row, tab or menu entry because enabled is false, without first asking WHICH axis is false.
  • Redirecting away from a route whose feature is entitlement-locked. A deep link, a notification tap or a back-navigation lands there; bouncing the merchant to Home tells them nothing and looks broken. Render the locked state on the route instead.
  • Disabling instead of locking. A greyed control with no explanation is the worst of both — it is visible and it teaches nothing. ADR-0021's "absent, never disabled" was written about APPLICABILITY and does not license a dead grey tile for entitlement.
  • A lock with no value proposition. "Orders — Pro" is not discovery. The design's own locked state names the capability, says what it earns the merchant, lists what unlocks, and only then offers the action.

What it requires instead ​

  1. Read entitlementSource, not enabled, at every gate that decides visibility.
  2. Render the locked presentation from @/ui, so all of them look and behave the same.
  3. Keep the enforcement exactly as it is. This changes presentation only: the Edge Function remains the primary write-enforcement layer, RLS remains defence in depth, and the client gate stays an affordance. Nothing here bypasses an entitlement check — a locked surface must not fetch, mutate or reveal the data behind it.
  4. Apply it on both sides. Merchant and consumer. apps/mobile/src/tiers/consumer/ now exists (6 features, 9 screens — see the three-categories section), so this is a rule the consumer tier must obey today, not one it inherits on creation. ⚠ It said the opposite until 2026-08-28, and the difference matters: "not applicable yet" reads as nothing to do, while the true state is that every consumer surface deciding visibility owes the same entitlementSource read the merchant ones do.

R-06 · Nothing reaches production undocumented, and promotion is measured ​

Rule. Every production change is a Change Record in the active release; release.json is the SSOT and deploy-prod.yml refuses an undeclared change. That governs what is declared. What is deployed is measured only where a tool reads the live project — check:ef-drift for Edge Functions; the other eight change classes (secrets, storage, cron, auth settings, Cloudflare, third-party, the app classes) are not measured by anything.

Never infer that an environment matches the repo — read the live object. Promotion is all-or-nothing against its declared scope and removes stale artifacts as deliberately as it adds. The seven-line pre-promotion checklist lives in releases/process.md; until QRS-696 lands it is a human checklist and is reported as one — machine-checked lines named separately from read ones.

Full text ​

Provenance — moved from CLAUDE.md on 2026-09-23 (QRS-1288)

Verbatim text of CLAUDE.md § "Nothing reaches production undocumented, and promotion is measured" as of commit 00c1eca. Dated "this said X until [date]" sentences are the corrections as recorded at the time; the live rule is the corrected one, and the short form above is what CLAUDE.md now carries.

Sixth rule: NOTHING REACHES PRODUCTION UNDOCUMENTED, AND PROMOTION IS MEASURED ​

[ENFORCED — non-negotiable, standing owner instruction, codified 2026-08-16] ​

The owner's words, and they are the rule:

Every change intended for Production must be explicitly tracked in the release documentation. Nothing should reach Production through an undocumented or ad-hoc deployment. Whatever we build, validate and track in Dev is promoted as a complete, fine-grained production build — no missing changes, no redundant components, no stale implementations, no broken dependencies.

⚠ HALF OF THIS IS ALREADY ENFORCED, AND SAYING SO IS THE POINT ​

The declaration half exists and is a real gate (QRS-288): release.json is the machine SSOT, check:release asserts the markdown and the manifest agree in both directions, G4 approval binds to a commit SHA and a manifest hash, and deploy-prod.yml refuses to apply a production change that is not declared. Treating that as missing would rebuild it; the honest statement is that the rule is already binding on what is DECLARED.

WHAT IS GENUINELY MISSING, AND IT IS THE OTHER HALF ​

The release system governs what is DECLARED. Nothing measures what is DEPLOYED. No artifact compares an environment's actual state against the repo. ⚠ The "or against another environment" half is false — tools/check-env-drift.js fetches live secret NAMES from two projects and diffs them, and this sentence denied it existed until 2026-08-28. What is genuinely missing is a repo-vs-environment comparison, not an environment-vs-another environment. Every claim of "Dev and the repo agree" is currently an inference.

That is not a theoretical gap — it was measured twice on 2026-08-15, in one afternoon:

  • QRS-693 — 20260814090000 was authored on the 14th and had never been applied to Dev. Found by reading pg_proc.prosrc on the live database. Not by check:release, not by check:sql, not by list_migrations, none of which diffs the repo's migration set against what a project has actually run.
  • QRS-694 — four archived pre-v2 Edge Functions are still ACTIVE on Dev, six days after being archived precisely because they write dropped tables. config.toml's own comment already calls a deployed-but-archived function "worse than absent, because a deployed function reads as a working feature." Two of them are on the money path.

Both are the same defect: a repo and a project drift, silently, and every gate stays green — because every gate reads the repo. This generalises the third rule to environments: no readiness claim reaches the owner unless a command produced it becomes, here, no promotion claim is credible unless something read the live environment.

What this forbids ​

  • Deploying anything to Production that has no Change Record. Already gated; restated because the gate only sees the classes it can observe (nine of seventeen are invisible to any script — secrets, storage, cron, auth settings, Cloudflare, third-party, and all three app classes).
  • Inferring that an environment matches the repo. "The migration is in the repo" is not "the migration ran". Read the live object.
  • Promoting a partially-verified set. A promotion is all-or-nothing against its declared scope, not a best-effort sweep of whatever happened to land.
  • Carrying stale artifacts forward. An archived function, a dropped table's helper, a superseded migration — promotion must remove as deliberately as it adds.

What it requires instead — the pre-promotion checklist, every time ​

Each line must be answered by a command whose output is pasted, never by reading:

  1. Every declared scope item is present in the build, and every production-path change in the diff is declared (both directions).
  2. Every migration in the repo is applied on the target, and every migration applied on the target exists in the repo — including the reverse direction, which is what catches an orphan version (QRS-267) and a hand-applied hotfix.
  3. The Edge Function set on the target equals the repo's live set — no extra, which is QRS-694.
  4. Every secret the functions read is set on the target (names, never values).
  5. The client build points at the intended project — check:env:prod prints it.
  6. Critical workflows pass against the target, not against Dev.
  7. The final state traces back to the approved scope, by id.

⚠ Until the automation exists, this is a HUMAN checklist and must be reported as one ​

Saying "validated" when the validation was reading is the exact category error the third rule was written about. The automation is designed under QRS-696; until it lands, a promotion report names which lines were machine-checked and which were read. Both are acceptable; conflating them is not.

R-07 · Proactive, not reactive ​

Rule. QRSETU is an active business assistant, not a place to store and view information. Every feature answers "how does this help the merchant take action and grow their business today?" — a screen that only renders state is substrate, not a feature, and its README says which.

Noise is the failure mode: earn each interruption, respect the iOS notification budget (ADR-0016 keeps recurrence in @qrsetu/domain so it is testable), never fabricate insight, suggest and never coerce. Intelligence comes from the analytics read model (ADR-0010), archetype configuration (ADR-0009) and pure derivation in @qrsetu/domain — never a nudge hardcoded in a screen. No in-app upsell (ADR-0002). Parity applies unchanged.

Full text ​

Provenance — moved from CLAUDE.md on 2026-09-23 (QRS-1288)

Verbatim text of CLAUDE.md § "Proactive, not reactive" as of commit 00c1eca. Dated "this said X until [date]" sentences are the corrections as recorded at the time; the live rule is the corrected one, and the short form above is what CLAUDE.md now carries.

The core product principle: proactive, not reactive [ENFORCED — non-negotiable] ​

QRSETU is an active business assistant, not a place to store and view information. We are not building another static or passive business app. The product should continuously help a merchant grow and run their business — identifying opportunities, surfacing actionable insight, and prompting the next meaningful action.

A reactive app waits for the user to initiate everything. That is the default outcome of ordinary engineering, it produces low engagement and thin long-term value, and it is the thing this principle exists to prevent. Every feature must answer:

"How does this help the user take action and grow their business today?"

If the honest answer is "it lets them see/store X", the feature is not finished — it is the substrate a feature would be built on. Displaying data is table stakes, not the deliverable.

The six principles:

  1. Deliver proactive value, not a display of information. A screen that only renders state is incomplete.
  2. Engage continuously with timely recommendations, reminders, insights, and action items that measurably improve the business.
  3. Encourage meaningful action — never rely on the user discovering a feature on their own.
  4. Build intelligent workflows that reduce effort and guide the user toward their business goals.
  5. Introduce creative engagement mechanisms that keep the app relevant over time without becoming intrusive.
  6. Every new screen and feature must contribute to engagement, retention, and the merchant's business success.

Evaluation gate — answer these in the feature's spec/README before implementing, not after:

  • Does it provide proactive value?
  • Does it encourage the user to take a meaningful business action?
  • Does it improve engagement and retention?
  • Can the experience be made more intelligent — recommendation, reminder, contextual suggestion, automation?
  • Does it differentiate QRSETU from a conventional business-management app?

A feature that cannot answer these is either mis-scoped or is infrastructure for one that can. Say which; do not ship it as a finished feature.

What "proactive" must not become ​

The failure mode is not under-building — it is noise. A nudge the merchant learns to ignore does not degrade to neutral; it trains them to ignore every nudge, and the mechanism is spent for good. So:

  • Earn each interruption. Proactive ≠ notification volume. Prefer in-app surfacing (a home-screen action item, an empty state that proposes the next step, a contextual hint at the moment of relevance) over a push. A push must be timely, specific, individually mutable, and worth the tap.
  • Respect the hard ceilings. iOS has a real per-app scheduled-notification budget — the reminders domain model (ADR-0016) keeps recurrence expansion in @qrsetu/domain precisely so that budget is testable without a database. Any new proactive surface inherits that constraint; check it before designing a scheduling scheme.
  • Never fabricate insight. A recommendation with no data behind it is worse than silence, because it is unfalsifiable to the user and destroys trust in the ones that are real. If the read model can't support a claim, don't make the claim.
  • Suggestion, never coercion. Dismissible, quiet-hours-aware, reduced-motion-aware, and never dark-patterned. Engagement that the merchant resents is churn on a delay.

Where the intelligence is allowed to come from (architecture, not per-screen invention) ​

This principle is a standing instruction to design proactively — it is not licence to hardcode nudges into screens. Build it on the spine that already exists:

  • Insights read from the analytics read model (ADR-0010) — not ad-hoc queries invented per screen.
  • Recommendations are archetype configuration (ADR-0009), so a new vertical inherits them as config rather than code. A nudge hardcoded for one vertical is a defect against ADR-0009.
  • Derivation is pure and lives in @qrsetu/domain (recurrence, due dates, DST, thresholds, scoring) — unit-tested with node --test, no database. The data layer returns stored rows; it does not compute advice.
  • Respect entitlements (ADR-0007). Never surface an action item the user cannot act on.
  • ⚠ Store-compliance trap: the native app is a free companion with no in-app purchase UI or CTA (Apple 3.1.3(d), ADR-0002). A proactive upsell — "upgrade to unlock…" — inside the iOS/Android app is a store-review risk, not just a product choice. Growth nudges that imply payment convert via web/email, never an in-app purchase path.
  • Parity applies unchanged. A proactive surface is a feature: it ships on Android native · iOS native · Web PWA together. There is no exception path (see "Cross-platform feature parity").

Reminders (ADR-0016) is the first instance of this principle and the reference for how a proactive domain is modelled — rules + sparse occurrence exceptions in the data layer, expansion in domain, idempotent writes.

P-01 · Three user categories (product principle) ​

Principle. QRSETU serves three fundamentally different user categories — Business Owner (solo SMB, the primary R1 audience) · Enterprise organization (an org workspace with its own admin portal, seats, org-level RBAC) · Individual consumer (owns nothing merchant-side; a completely different dashboard). The principal is a USER; a workspace is the business tenant (consumer 0 memberships · solo 1 · employee 1, org-owned). Category is the default experience, never a permanent exclusion.

authenticated is the logged-in general public, so no policy may grant merchant data by role alone — scope by relationship, always. Enterprise admin ≠ platform admin. Archetype immutability is subject-scoped, not absolute. Anonymous-first for consumers. Registered consumers are first-class in transactional tables (a nullable buyer reference). Category 3 ships in R1. A feature note that does not name the categories it serves is incomplete.

Full text ​

Provenance — moved from CLAUDE.md on 2026-09-23 (QRS-1288)

Verbatim text of CLAUDE.md § "Core product principle: THREE USER CATEGORIES" as of commit 00c1eca. Dated "this said X until [date]" sentences are the corrections as recorded at the time; the live rule is the corrected one, and the short form above is what CLAUDE.md now carries.

Core product principle: THREE USER CATEGORIES [ENFORCED — non-negotiable, codified 2026-08-07] ​

QRSETU serves three fundamentally different user categories. They differ in goals, authentication, onboarding, authorization, feature set, navigation, subscription model and billing — so they differ in schema. Assuming a single "user" who owns a business is the most expensive wrong assumption available in this codebase, and it is the default one, because R1's primary audience is category 1.

CategoryWhoOwnsGoverns their own archetype/plan?
1Business Owner (Solo SMB) — primary R1 audienceGanapati stall vendors, boutiques, Herbalife distributors, real-estate agents, electricians, tutors, yoga trainers, consultantsTheir own Setu Card, Store, Catalog, Payments, Leads, Contacts, Bookings, Appointments, AnalyticsYes — they choose at onboarding
2Enterprise OrganizationAn org onboarding many users under one centralized subscriptionAn organization workspace + its own admin portal, centralized billing, seat-based licensing, employee management, org-level RBAC and feature policyNo — the org admin assigns and may change it
3Individual User (Consumer)People who meet QRSETU by scanning a vendor's cardNothing merchant-side. A completely different dashboard and feature setN/A — they have no business at all

The consequences that must hold in every design ​

  • The principal is a USER; a workspace is the BUSINESS tenant. A consumer has zero workspace memberships, a solo owner has one, an enterprise employee has one (org-owned). Any resolver, RPC or policy that requires a workspace to answer a question cannot serve category 3 — that is a design defect, not an edge case.
  • Category is the DEFAULT EXPERIENCE, never a permanent exclusion. account_type decides the entry route (resolveEntryRoute) and the dashboard. A consumer who later starts a business gains a workspace membership — never a second account, never a data migration. Model it so that is free.
  • ⚠ CONSUMERS CHANGE WHAT authenticated MEANS, AND THIS IS A THREAT-MODEL CHANGE, NOT A DETAIL. Until now authenticated ≈ merchants ≈ a small, semi-trusted population, and ADR-0014 reasons about anon vs authenticated. Category 3 makes authenticated the logged-in general public, and it will be the largest population on the platform by orders of magnitude. Therefore: no policy may grant access by ROLE alone. Every merchant-data policy scopes by relationship (workspace_id in (select workspace_id from workspace_members where user_id = auth.uid())), never using (true) TO authenticated. A pgTAP suite must assert that a consumer — an authenticated user with no workspace — can read nothing merchant-owned. Read every existing TO authenticated policy in this light before extending it.
  • Enterprise admin ≠ platform admin, and conflating them is privilege escalation. Platform admin is QRSETU staff over all tenants; org admin is a customer's employee, powerful inside one organization and powerless outside it. ADR-0006's platform-role-vs-tenant-role split is the governing decision; the org admin portal is a distinct surface that ADR-0011 does not yet contain.
  • Archetype immutability is subject-scoped, not absolute. A category-1 owner cannot change their own archetype after onboarding (write-once). A category-2 employee's archetype is assigned and mutable by their org admin. So the guard is "immutable to the subject, mutable by an authorized governor (platform admin, or the org admin of the owning organization) with an audit row" — never a plain write-once trigger.
  • Anonymous-first for consumers is a hard requirement. A vendor's public card must be fully usable with no account. Registration is demanded only when identity is genuinely required — chat, booking, consultation, purchase, order tracking, subscription management. A signup wall in front of a scanned card destroys the platform's entire growth mechanic.
  • Registered consumers are first-class in transactional tables. orders (and bookings, chats, subscriptions-to-a-vendor) carry a nullable buyer/consumer user reference: null = anonymous, set = registered. Not a later addition — an append-only or high-volume table cannot gain an identity column cheaply.
  • ⚠ CATEGORY 3 SHIPS IN R1. THE CONSUMER APP IS CONFIRMED R1 SCOPE — ELEVEN SCREENS [owner decision 2026-08-13, and this bullet said the OPPOSITE until then]. It read: "Category 3 is the marketplace on-ramp … None of it ships in R1, but the consumer identity it needs must exist from the first schema." That deferral is withdrawn. The eleven screens are the design project's own prototype/consumer/ section (SCREENS.md, fetched 2026-08-13), and they are tracked in documentation/portal/design-system/screen-conformance.json under section: "consumer", so npm run check:screens counts them and the total mobile scope is 36 screens, not 25:
    • Transactional — the Ganapati money loop depends on these: ItemView (terminal; three payment outcomes onto ONE confirmation sheet — paid · abandoned, meaning the order is placed and UNPAID and the idol is not held · failed) · OrderCode (the buyer's half of the merchant's Scan-to-collect; the code is re-minted on open and refreshed every few minutes, so a screenshotted code is refused later) · ScanVerify (every payload resolves to one of FOUR verdicts).
    • Browse and identity: VendorView (the in-app twin of the DOM Setu Card, never a second renderer of it — ADR-0019 still holds) · Featured (a reusable block shared with the DOM card, never promo/sponsored) · ConsumerHome · ItemFeed · VendorFeed · Chats · Notifications · Account.
    • The one architectural consequence to get right first — ✅ DONE, and this bullet demanded it as FUTURE work until 2026-08-28. Consumers live in their own apps/mobile/src/tiers/consumer/ tier, because this file's own three-category principle says a consumer has "a completely different dashboard and feature set". Measured: 6 features · 9 screens · 9 routes under src/app/consumer/, and screen-conformance.json records 10 of the 11 consumer rows as built (only scan-verify is missing). ⚠ That sentence expired on 2026-09-04: the consumer section was re-transcribed from design round 40 and is 23 rows — 8 built, 2 stale, 13 missing (QRS-1017). Eleven rows had been describing twenty-three screens, so "10 of 11 built" was arithmetically true and, as a picture of the consumer surface, badly wrong. Read the generated inventory block at the top of this file, never this sentence. tooling/eslint-config/guardrails.js gained the boundary in the same change, exactly as this bullet required — and did it better than asked: isolation is derived from a TIERS = ['user', 'admin', 'consumer'] array rather than hand-paired constants, so the six directions cannot be half-written the way two hand-paired ones were. ⚠ Note the DIRECTION of this correction — the file UNDERSTATED delivered work, which this file elsewhere calls the rarer and more expensive direction, because it invites rebuilding what already exists. It also propagated: the same dead sentence reached tools/check-rpc-contract.js's allowance reason, screen-conformance.json's scan-verify note and apps/mobile/src/features/README.md, i.e. two machine-read artifacts, before anyone re-measured it.
    • chat-core.js is the design's own instruction that the two chat halves share ONE module. The consumer Chats screen must consume the same @qrsetu/domain/chat logic the merchant Messages screen does. Two implementations of "which conversations are unread" is the duplicate-source-of-truth bug class (QRS-249) applied to the most visible surface in the product.
    • Still deferred, and deliberately NOT re-scoped by this decision: consumer-held subscriptions, personalised recommendations, and promotions/sponsored content. ItemFeed ships its promoted slot rendering nothing, which is the same fail-closed posture ADR-0004's promo_slot already takes. prototype/customer-flows/ (Book · Order · Pay · Review) is marked SUPERSEDED, DO NOT IMPLEMENT in the registry — the purchase path is ItemView, and mistaking those four for it is the most available wrong turn here.
    • The original reasoning survives intact and is now load-bearing rather than anticipatory: the consumer identity had to exist in the first schema, and it does — orders carries a nullable buyer reference and handle_new_user provisions a public.users row for every principal whether or not they ever run a business, so shipping category 3 is a client-side build rather than a migration.

When a feature is specified, name which categories it serves. A feature note that does not say is incomplete in the same way one that omits its parity status or proactive-value answer is.