spec(§6.6–6.9): NFRs, key decisions table, testing strategy, failure modes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -808,19 +808,97 @@ sequenceDiagram
|
||||
|
||||
### 6.6 Non-functional requirements & cross-cutting concerns
|
||||
|
||||
<!-- §6.6 pending -->
|
||||
- **Security & privacy:**
|
||||
- Authn: email + one-time code per INV-3; sessions in signed, HTTP-only,
|
||||
`SameSite=Lax` cookies, `Secure` in deployed environments.
|
||||
- Authz: MVP has one rule — you act only on your own storefront — enforced
|
||||
in the domain layer (INV-6).
|
||||
- No account enumeration: `request-code` answers uniformly for new and
|
||||
known emails.
|
||||
- Data classification: emails are personal data — the only personal data
|
||||
held (§6.3); code hashes are short-lived secrets; both excluded from
|
||||
logs in deployed environments.
|
||||
- Secrets are references, never bytes (handbook §6.3): session secret and
|
||||
SMTP credentials live in Secret Manager, named by deployment config
|
||||
(INV-8).
|
||||
- **Performance & scale:** MVP traffic is near-zero; the bar is honesty, not
|
||||
throughput — interactive responses well under a second on the standard
|
||||
single-VM deployment. SQLite/WAL with per-request connections is far above
|
||||
this bar; revisit at real scale (§7.4 R-3).
|
||||
- **Availability & resilience:** one VM, one process (standard flotilla
|
||||
stack); deploys are fail-stop with `/healthz` gating; brief deploy-window
|
||||
unavailability is accepted pre-v1.
|
||||
- **Observability:** structured request logs (no emails/codes in deployed
|
||||
logs); `/healthz` reporting migration currency; auth events (code issued /
|
||||
verify ok / verify failed) logged with hashed identifiers — enough to see
|
||||
the bootstrap rehearsal happen and debug delivery.
|
||||
- **Accessibility:** the four screens are forms and text — semantic HTML,
|
||||
labelled fields, keyboard-completable flows, visible focus; the code input
|
||||
carries one-time-code semantics so password managers and OS autofill
|
||||
cooperate.
|
||||
|
||||
### 6.7 Key decisions & alternatives considered
|
||||
|
||||
<!-- §6.7 pending -->
|
||||
| Decision | Chosen | Alternatives considered | Why chosen |
|
||||
| --- | --- | --- | --- |
|
||||
| Overall shape | Carry forward the prototype's 4-layer modular monolith + React SPA | Heavier re-architecture (Postgres, services, SSR); resume prototype code; new stack | Proven by R01–R07 against this exact domain; smallest bootstrap surface (§1.6 outcome 3); rebuild wants clean *code*, not unproven *shape* (§2) |
|
||||
| Datastore | SQLite (WAL), no ORM, forward-only `.sql` migrations | Postgres (managed or on-VM) | Single-VM flotilla standard; zero standing service to provision per environment — bootstrap stays one gesture; prototype precedent. Revisit at scale (R-3) |
|
||||
| Tenancy | One shared DB; tenant rows carry `storefront_id` (INV-5); ownership via membership relation | DB-per-storefront; hard 1-1 FK on account | Membership keeps the many-per-account door open (Feature #1 constraint) and is the staff-model seam (13.15.\*); DB-per-tenant multiplies the bootstrap story for no MVP gain |
|
||||
| One-storefront rule | Service-layer guard + no UX affordance (INV-4) | UX-only; schema `UNIQUE(account_id)` | UX-only leaves the API dishonest (data could contradict the promise); schema-level closes the door Feature #1 says to keep open. The guard is one deletable check |
|
||||
| Auth mechanism | Email + one-time code, passwordless | Passwords (+ reset/verification machinery); OAuth (Google/Apple) first | Corpus's primary path (14.01.0003–0004); prototype-proven; collapses the account-lifecycle question — no passwords to reset, verification inherent. OAuth deferred, addable under INV-2 |
|
||||
| Admission | Open sign-up, no gate | Prototype's pending/granted invite gate | ecomm is a product, not an internal tool; a gate would hand-curate exactly what BUC-5 says must be flow-reachable. Explicit §1.7 non-goal |
|
||||
| Bootstrap | No seed; empty DB is a working state (INV-1) | Prototype's seeded store/owner; seed scripts per environment | Seeding *is* hand-placed data — it's what Feature #1's acceptance forbids; INV-1 makes the bootstrap story a property of the app, not of tooling |
|
||||
| API surface | REST BFF only; GraphQL deferred | Prototype's dual REST + GraphQL from day one | Four screens don't justify a second surface; INV-6's one-source-of-truth layering keeps GraphQL addable as a projection later |
|
||||
| Email delivery | `mailer` port: `LogMailer` (dev/tests) / `SmtpMailer` (deployed); relay is deployment config | Provider SDK baked in; building codes-only without real mail | Port keeps INV-8 (no deployment shape in code) and makes the provider an operator choice (§9 Q-1); real mail is required by Feature #1's "no stubbed auth" in PPE/Prod |
|
||||
| Sessions | Signed cookie, no session table | Server-side session rows | No revocation/multi-device requirement yet; cookie sessions are one less table and zero queries; revisit with staff (§6.2 note) |
|
||||
| Vocabulary | **Storefront** is ecomm's canonical noun (corpus "store" maps to it) | Adopt corpus "store" | The charter, Feature #1, and repo names all say storefront; glossary (§10) carries the mapping so corpus scenario IDs still read cleanly |
|
||||
|
||||
### 6.8 Testing strategy
|
||||
|
||||
<!-- §6.8 pending -->
|
||||
The prototype's discipline, inherited:
|
||||
|
||||
- **Scenario-bound end-to-end tests** drive the app through its HTTP surface
|
||||
(FastAPI TestClient on a fresh temp DB). Where a corpus scenario covers the
|
||||
behavior, the test name embeds its ID (`test_14_01_0003_email_code_sent`);
|
||||
PUCs without a corpus ID bind to the PUC (`test_puc_05_...`). One test per
|
||||
scenario/PUC, including every unhappy path in §4.
|
||||
- **The bootstrap test is INV-1's enforcement:** from an empty database, one
|
||||
test walks request-code → verify → create-storefront → `/me` → admin
|
||||
answer, asserting no step needed seeded state. A second asserts
|
||||
`migrate()` is idempotent (re-running applies nothing).
|
||||
- **Invariant tests** for the sharp edges: code expiry/attempt
|
||||
exhaustion/cooldown (INV-3), concurrent second-storefront refusal (INV-4),
|
||||
enumeration uniformity (§6.6), `LogMailer` capture in tests.
|
||||
- **Frontend:** typecheck + production build in CI; the entry-routing rule
|
||||
(§6.5) unit-tested; component/E2E browser tests deferred until there is UI
|
||||
beyond forms.
|
||||
- **Gate:** `scripts/check.sh` = `lint-imports` (layer contract, INV-6) +
|
||||
`pytest` + frontend typecheck/build — locally pre-merge and in CI. "Tested"
|
||||
for a slice (§7.2) means its scenarios are green in this gate.
|
||||
- **PPE tier:** the §7.2 SLICE-4 rehearsal *is* the deployed-tier test — the
|
||||
product flows walked against PPE with real mail before Prod exists.
|
||||
|
||||
### 6.9 Failure modes, rollback & flags
|
||||
|
||||
<!-- §6.9 pending -->
|
||||
- **Mail relay down / refuses** → `request-code` returns `502
|
||||
delivery_failed`; the user is told the code didn't go out (INV-9, §5.2) —
|
||||
never a fake 204. Operator sees it in logs; codes are re-requestable, no
|
||||
state is corrupted.
|
||||
- **Migration fails at startup** → process refuses to start (INV-7);
|
||||
flotilla's fail-stop deploy keeps the prior version serving; operator fixes
|
||||
forward (no down-migrations — MVP migrations are additive).
|
||||
- **DB file lost/corrupted** → restore from VM disk snapshot (the standard
|
||||
flotilla VM's recovery story); accepted pre-v1 bar — an explicit backup
|
||||
cadence is deferred and logged (§9 Q-2).
|
||||
- **Session-secret leak** → rotate the secret in Secret Manager + redeploy;
|
||||
all sessions invalidate at once (accepted blast radius at MVP scale,
|
||||
§6.2).
|
||||
- **Code-guessing attempts** → INV-3's attempt cap and cooldown bound the
|
||||
attack; events are logged.
|
||||
- **Rollback** = flotilla redeploy of the previous version; additive-only
|
||||
MVP migrations make old-code-on-new-schema safe.
|
||||
- **Feature flags / kill switch:** none — pre-v1, nothing to protect that a
|
||||
redeploy doesn't cover; first flags arrive when there are users to shield.
|
||||
|
||||
## 7. Delivery Plan
|
||||
|
||||
|
||||
Reference in New Issue
Block a user