Requirements Matrix — AI Software Factory
Traceability. Rows = every
PR-xxxfrom product-requirements.md (read and mirrored there first — that file is the canonical ID source; this matrix must stay 1:1 with it: 167 requirements, PR-001 … PR-167). Columns: requirement (short title), priority (P0 MVP / P1 / P2 enterprise), phase (0–15 per ROADMAP.md; a comma list means the requirement lands across those phases), task file (indocs/tasks/), status.Status legend: ⬜ not started · 🟨 in progress · ✅ done (task file's Definition of Done met). At scaffold time everything is ⬜ — this matrix is updated as tasks complete (the phase owner ticks rows when their task's DoD is verified).
Coverage sections below: NFRs → verifying tests and API endpoints → requirements (the two other traceability directions).
1. Requirement registry (167 rows)
| ID | Requirement | Pri | Phase | Task file | Status |
|---|---|---|---|---|---|
| PR-001 | Register (email + password, server validation) | P0 | 1 | phase-01-authentication.md |
⬜ |
| PR-002 | Email verification (24 h single-use token) | P0 | 1 | phase-01-authentication.md |
⬜ |
| PR-003 | Login → access JWT 15 m + rotating refresh 30 d | P0 | 1 | phase-01-authentication.md |
⬜ |
| PR-004 | Refresh rotation + replay → family revocation | P0 | 1 | phase-01-authentication.md |
⬜ |
| PR-005 | Logout; session listing & revocation | P0 | 1 | phase-01-authentication.md |
⬜ |
| PR-006 | Password reset (1 h, single-use, session revoke) | P0 | 1 | phase-01-authentication.md |
⬜ |
| PR-007 | bcrypt(12) hashing, timing-safe, no logging | P0 | 1 | phase-01-authentication.md |
⬜ |
| PR-008 | Profile view/update (name, avatar, locale, tz, theme) | P0 | 1 | phase-01-authentication.md |
⬜ |
| PR-009 | Auth-endpoint rate limiting | P0 | 1 | phase-01-authentication.md |
⬜ |
| PR-010 | Mandatory auth audit events | P0 | 1 | phase-01-authentication.md |
⬜ |
| PR-011 | SSO: OIDC + SAML, enforce mode | P2 | 14 | phase-14-enterprise.md |
⬜ |
| PR-012 | SSO identity linking + SCIM deactivation | P2 | 14 | phase-14-enterprise.md |
⬜ |
| PR-013 | Auto personal workspace at registration | P0 | 2 | phase-02-workspace.md |
⬜ |
| PR-014 | Create workspaces (unique slug) | P0 | 2 | phase-02-workspace.md |
⬜ |
| PR-015 | Rename/re-icon/delete workspace (cascade preview) | P0 | 2 | phase-02-workspace.md |
⬜ |
| PR-016 | Four workspace roles (RBAC) | P0 | 2 | phase-02-workspace.md |
⬜ |
| PR-017 | Role-change rules + last-owner protection + audit | P0 | 2 | phase-02-workspace.md |
⬜ |
| PR-018 | Email invitations (accept/decline/revoke/expiry) | P0 | 2 | phase-02-workspace.md |
⬜ |
| PR-019 | Teams: CRUD, membership, ordering, privacy | P0 | 2 | phase-02-workspace.md |
⬜ |
| PR-020 | Sidebar (Home/Inbox/Search/Favorites/Teams/Private/Shared/Trash) | P0 | 2 | phase-02-workspace.md |
⬜ |
| PR-021 | Workspace settings sections | P0 | 2 | phase-02-workspace.md |
⬜ |
| PR-022 | Organizations + org policies | P2 | 14 | phase-14-enterprise.md |
⬜ |
| PR-023 | SCIM user & group provisioning | P2 | 14 | phase-14-enterprise.md |
⬜ |
| PR-024 | Per-workspace usage limits enforcement | P2 | 14 | phase-14-enterprise.md |
⬜ |
| PR-025 | Page CRUD | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-026 | Nesting + fractional tree ordering | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-027 | Full page surface (icon/cover/title/desc/blocks/comments/…) | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-028 | Trash: soft delete / restore / purge | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-029 | Archives (separate from trash) | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-030 | Favorites (ordered, sidebar) | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-031 | Page version history + restore-as-new-version | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-032 | Private pages | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-033 | Virtualized tree (100k < 2 s) | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-034 | Block-tree model (stable ids, fractional position) | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-035 | Text blocks (paragraph, h1–h3, quote, callout) | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-036 | List blocks (bulleted, numbered, todo, toggle) | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-037 | Media blocks (image, file = P0; video/audio/bookmark/embed = P1) | P0/P1 | 4, 9 | phase-04-editor.md, phase-09-files.md |
⬜ |
| PR-038 | Code block (P0) + equation block (P1) | P0/P1 | 4 | phase-04-editor.md |
⬜ |
| PR-039 | Layout blocks (divider P0; spacer, columns P1) | P0/P1 | 4 | phase-04-editor.md |
⬜ |
| PR-040 | Advanced blocks (table, synced_block P0; child rows, linked_database P1) | P0/P1 | 4, 5 | phase-04-editor.md, phase-05-databases.md |
⬜ |
| PR-041 | Full keyboard model (Enter/Tab/arrows/⌘A ladder/…) | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-042 | Mouse interactions (hover controls, multi-select, resize) | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-043 | Undo/redo + copy/paste (internal + markdown/HTML) | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-044 | Keyboard/ARIA equivalent for every mouse op | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-045 | Slash command menu (18 commands) | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-046 | Slash UX (groups, fuzzy, keyboard) | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-047 | /database dynamic creation (property picker) |
P0 | 4, 5 | phase-04-editor.md, phase-05-databases.md |
⬜ |
| PR-048 | Block context menu (full item list) | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-049 | Page context menu (full item list) | P0 | 3, 4 | phase-03-pages.md, phase-04-editor.md |
⬜ |
| PR-050 | Block drag: reorder/nest/indicators/auto-scroll | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-051 | Multi-block drag | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-052 | Cross-page drag + drag between columns | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-053 | Database row/card dragging (board = status update) | P0 | 5 | phase-05-databases.md |
⬜ |
| PR-054 | Sidebar page drag (reorder + reparent) | P0 | 3, 4 | phase-04-editor.md |
⬜ |
| PR-055 | OS file/image drop into editor & cells | P0 | 9 | phase-09-files.md |
⬜ |
| PR-056 | dnd-kit + non-drag keyboard DnD alternative | P0 | 4 | phase-04-editor.md |
⬜ |
| PR-057 | Database container (properties/records/views) | P0 | 5 | phase-05-databases.md |
⬜ |
| PR-058 | 24 property types (core 15 in P0; rel/rollup/formula/button P1) | P0/P1 | 5, 6 | phase-05-databases.md, phase-06-relations.md |
⬜ |
| PR-059 | Single TITLE property invariant | P0 | 5 | phase-05-databases.md |
⬜ |
| PR-060 | Record = page (1:1 join, properties bar) | P0 | 5 | phase-05-databases.md |
⬜ |
| PR-061 | Record create/update/archive/duplicate | P0 | 5 | phase-05-databases.md |
⬜ |
| PR-062 | Table view (virtualized 100k, inline edit, bulk bar) | P0 | 5 | phase-05-databases.md |
⬜ |
| PR-063 | List view | P1 | 5 | phase-05-databases.md |
⬜ |
| PR-064 | Board view (kanban, drag = property update) | P0 | 5 | phase-05-databases.md |
⬜ |
| PR-065 | Calendar view (day/week/month, ranges) | P1 | 5 | phase-05-databases.md |
⬜ |
| PR-066 | Gallery view (card grid + layout config) | P1 | 5 | phase-05-databases.md |
⬜ |
| PR-067 | Timeline view (Gantt, drag/resize bars) | P1 | 5 | phase-05-databases.md |
⬜ |
| PR-068 | Chart + Dashboard as view types | P1 | 5, 12 | phase-05-databases.md |
⬜ |
| PR-069 | Form view (published form over database) | P1 | 10 | phase-10-forms.md |
⬜ |
| PR-070 | Per-view settings (filter/sort/group/props/display) | P0 | 5 | phase-05-databases.md |
⬜ |
| PR-071 | Nested filters (AND/OR/NOT, 20+ operators) | P0 | 5 | phase-05-databases.md |
⬜ |
| PR-072 | Multi-key sort (nulls first/last) | P0 | 5 | phase-05-databases.md |
⬜ |
| PR-073 | Multi-level group (collapsible, counts) | P0 | 5 | phase-05-databases.md |
⬜ |
| PR-074 | Property management (add/rename/reorder/hide/delete) | P0 | 5 | phase-05-databases.md |
⬜ |
| PR-075 | Relations (1:1, 1:N, M:N, bidirectional) | P0 | 6 | phase-06-relations.md |
⬜ |
| PR-076 | Relation cells (search popover, sync both sides) | P0 | 6 | phase-06-relations.md |
⬜ |
| PR-077 | Rollups (COUNT/SUM/AVG/MIN/MAX/PERCENT/UNIQUE + filter) | P1 | 6 | phase-06-relations.md |
⬜ |
| PR-078 | Event-driven rollup recompute | P1 | 6 | phase-06-relations.md |
⬜ |
| PR-079 | Formula engine (safe parser/AST, zero eval) | P1 | 6 | phase-06-relations.md |
⬜ |
| PR-080 | Formula function set (16 fns + prop()) |
P1 | 6 | phase-06-relations.md |
⬜ |
| PR-081 | Formula dependencies + recompute + return type | P1 | 6 | phase-06-relations.md |
⬜ |
| PR-082 | Database buttons (7 action types) | P1 | 6 | phase-06-relations.md |
⬜ |
| PR-083 | Button validation, caller-permission execution, logging | P1 | 6 | phase-06-relations.md |
⬜ |
| PR-084 | Realtime WS server (rooms, auth, heartbeat, pruning) | P1 | 7 | phase-07-collaboration.md |
⬜ |
| PR-085 | Yjs CRDT sync (server-authoritative, debounced persist) | P1 | 7 | phase-07-collaboration.md |
⬜ |
| PR-086 | Presence (avatars, cursors, awareness) | P1 | 7 | phase-07-collaboration.md |
⬜ |
| PR-087 | Realtime latency < 200 ms p95 | P1 | 7 | phase-07-collaboration.md |
⬜ |
| PR-088 | Realtime comments over the socket | P1 | 7 | phase-07-collaboration.md |
⬜ |
| PR-089 | Reconnect + Yjs resume (Redis update log) | P1 | 7 | phase-07-collaboration.md |
⬜ |
| PR-090 | Offline mode (IndexedDB queue, CRDT merge) | P1 | 7 | phase-07-collaboration.md |
⬜ |
| PR-091 | Comments on page/block/selection/record | P0 | 3, 5, 7 | phase-03-pages.md, phase-05-databases.md, phase-07-collaboration.md |
⬜ |
| PR-092 | Thread ops (reply, resolve, reopen, edit, delete) | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-093 | Reactions (one per user/emoji/target) | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-094 | Mentions @user/@team/@page/@database + notify | P0 | 7 | phase-07-collaboration.md |
⬜ |
| PR-095 | Notifications (8 types; in-app P0, email P1) | P0/P1 | 7 | phase-07-collaboration.md |
⬜ |
| PR-096 | Inbox (unread, deep links, preferences) | P0 | 7 | phase-07-collaboration.md |
⬜ |
| PR-097 | Independent mention read-state | P0 | 7 | phase-07-collaboration.md |
⬜ |
| PR-098 | Search everything (7 entity types) | P0 | 8 | phase-08-search.md |
⬜ |
| PR-099 | Full-text + prefix + fuzzy | P0 | 8 | phase-08-search.md |
⬜ |
| PR-100 | Search filters + highlighted snippets | P0 | 8 | phase-08-search.md |
⬜ |
| PR-101 | Recent searches + suggestions | P0 | 8 | phase-08-search.md |
⬜ |
| PR-102 | Index pipeline (events → OpenSearch, lag < 30 s) | P0 | 8 | phase-08-search.md |
⬜ |
| PR-103 | Query-time permission filtering | P0 | 8 | phase-08-search.md |
⬜ |
| PR-104 | Search < 500 ms p95 | P0 | 8 | phase-08-search.md |
⬜ |
| PR-105 | S3 storage + presigned upload/download | P0 | 9 | phase-09-files.md |
⬜ |
| PR-106 | File pipeline (scan → metadata → thumbnail → READY) | P0 | 9 | phase-09-files.md |
⬜ |
| PR-107 | Supported kinds + previews | P0/P1 | 9 | phase-09-files.md |
⬜ |
| PR-108 | Virus quarantine (never served) | P0 | 9 | phase-09-files.md |
⬜ |
| PR-109 | File metadata, versions, permissions, trash | P0 | 9 | phase-09-files.md |
⬜ |
| PR-110 | Files in editor / database / forms | P0 | 9 | phase-09-files.md |
⬜ |
| PR-111 | 500 MB multipart + storage quotas | P0 | 9 | phase-09-files.md |
⬜ |
| PR-112 | RBAC base matrix (4 roles) | P0 | 2 | phase-02-workspace.md |
⬜ |
| PR-113 | Resource levels (None/View/Comment/Edit/Full) | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-114 | effective = max(role, grants) + tree walk-up + deny override | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-115 | Server-side re-validation on every op | P0 | 2 | phase-02-workspace.md |
⬜ |
| PR-116 | Grant management UI (share dialog, inherit) | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-117 | Share scopes (5) | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-118 | Public pages (options: comments/duplicate/index/password/expiry) | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-119 | Permission/share audit events | P0 | 3 | phase-03-pages.md |
⬜ |
| PR-120 | Enterprise policy expressions + guest restrictions | P2 | 14 | phase-14-enterprise.md |
⬜ |
| PR-121 | Template types + frozen snapshots + builtins | P1 | 10 | phase-10-forms.md (adjacent) |
⬜ |
| PR-122 | Instantiate from template (fresh ids) | P1 | 10 | phase-05-databases.md |
⬜ |
| PR-123 | Template browser in creation flows | P1 | 10 | phase-05-databases.md |
⬜ |
| PR-124 | Import (md/html/csv/json/notion zip) | P1 | 10 | phase-10-forms.md (adjacent) |
⬜ |
| PR-125 | Export (md/html/pdf/csv/json) | P1 | 10 | phase-10-forms.md (adjacent) |
⬜ |
| PR-126 | Import/export: permission-checked, audited, job progress | P1 | 10 | phase-10-forms.md (adjacent) |
⬜ |
| PR-127 | Form builder (10 question types, validation, mapping) | P1 | 10 | phase-10-forms.md |
⬜ |
| PR-128 | Public forms (unauthenticated, branding, closed) | P1 | 10 | phase-10-forms.md |
⬜ |
| PR-129 | Submission → record + form.submitted event |
P1 | 10 | phase-10-forms.md |
⬜ |
| PR-130 | Form responses view | P1 | 10 | phase-10-forms.md |
⬜ |
| PR-131 | Charts (6 types + full config) | P1 | 10 | phase-10-forms.md (adjacent) |
⬜ |
| PR-132 | Dashboards (8 widget types, grid, filters) | P1 | 10 | phase-10-forms.md (adjacent) |
⬜ |
| PR-133 | Public read-only dashboards | P1 | 10 | phase-10-forms.md (adjacent) |
⬜ |
| PR-134 | Automation model (trigger→conditions→actions, BullMQ) | P1 | 11 | phase-11-automation.md |
⬜ |
| PR-135 | 8 trigger types | P1 | 11 | phase-11-automation.md |
⬜ |
| PR-136 | 9 action types | P1 | 11 | phase-11-automation.md |
⬜ |
| PR-137 | Retries (×8 backoff), dead-letter, timeouts | P1 | 11 | phase-11-automation.md |
⬜ |
| PR-138 | Run history + logs UI | P1 | 11 | phase-11-automation.md |
⬜ |
| PR-139 | Pure matcher/evaluator + actor permissions on runs | P1 | 11 | phase-11-automation.md |
⬜ |
| PR-140 | automation.executed audit |
P1 | 11 | phase-11-automation.md |
⬜ |
| PR-141 | AI gateway (5 provider families, zero-code swap) | P1 | 12 | phase-12-ai.md |
⬜ |
| PR-142 | Ask AI chat (workspace context, history) | P1 | 12 | phase-12-ai.md |
⬜ |
| PR-143 | Page AI (summarize/rewrite/improve/translate, diff preview) | P1 | 12 | phase-12-ai.md |
⬜ |
| PR-144 | Database AI (generate/extract/formula) | P1 | 12 | phase-12-ai.md |
⬜ |
| PR-145 | RAG search (hybrid + citations) | P1 | 12 | phase-12-ai.md |
⬜ |
| PR-146 | Generation via structured tool calls (never raw SQL) | P1 | 12 | phase-12-ai.md |
⬜ |
| PR-147 | AI logging + per-workspace token budgets | P1 | 12 | phase-12-ai.md |
⬜ |
| PR-148 | REST API v1 surface (pages/dbs/query/search/files/…) | P1 | 13 | phase-13-api.md |
⬜ |
| PR-149 | API keys (scoped, hashed, per-key limit) | P1 | 13 | phase-13-api.md |
⬜ |
| PR-150 | OAuth2 (auth code) | P1 | 13 | phase-13-api.md |
⬜ |
| PR-151 | Webhooks (6 event families, HMAC, retry, log) | P1 | 13 | phase-13-api.md |
⬜ |
| PR-152 | OpenAPI spec + CI contract check + docs | P1 | 13 | phase-13-api.md |
⬜ |
| PR-153 | API error envelope + key-auth e2e | P1 | 13 | phase-13-api.md |
⬜ |
| PR-154 | Audit viewer (filters + export) | P2 | 14 | phase-14-enterprise.md |
⬜ |
| PR-155 | Organization policies (enforced) | P2 | 14 | phase-14-enterprise.md |
⬜ |
| PR-156 | Retention policies (PURGE/HOLD, nightly job) | P2 | 14 | phase-14-enterprise.md |
⬜ |
| PR-157 | Admin console | P2 | 14 | phase-14-enterprise.md |
⬜ |
| PR-158 | Security baseline (headers, cookies, CSRF, CORS, rate limits) | P0 | 1, 15 | phase-01-authentication.md, phase-15-production.md |
⬜ |
| PR-159 | Performance targets (6 metrics + 100k dbs) | P0 | 3, 4, 5, 15 | phase-15-production.md |
⬜ |
| PR-160 | Scalability (10k WS, 1k rps, 100k queries) | P1 | 15 | phase-15-production.md |
⬜ |
| PR-161 | Reliability (99.9 % SLO, graceful degradation) | P1 | 15 | phase-15-production.md |
⬜ |
| PR-162 | Observability (OTel, Prometheus, Grafana, Loki, alerts) | P2 | 15 | phase-15-production.md |
⬜ |
| PR-163 | Backup/DR (PITR, RPO 5 m, RTO 1 h, drills) | P2 | 15 | phase-15-production.md |
⬜ |
| PR-164 | Theming (light/dark/system, tokens only) | P0 | 0 | phase-00-foundation.md |
⬜ |
| PR-165 | Accessibility (WCAG 2.2 AA) | P0 | 4, ongoing | phase-04-editor.md |
⬜ |
| PR-166 | i18n readiness (keys, Intl, no hard-coded EN) | P2 | 15 | phase-15-production.md |
⬜ |
| PR-167 | Browser support matrix + responsive | P0 | 0 | phase-00-foundation.md |
⬜ |
Count check: 167 rows (PR-001…PR-167, contiguous). 92 rows are fully P0, 55 fully P1, 13 P2, and 7 rows (PR-037, PR-038, PR-039, PR-040, PR-058, PR-095, PR-107) split P0/P1 across sub-features — their P0 parts ship in the MVP. The MVP (all P0 content) spans Phases 0–9 exactly as product-requirements §5 states.
2. Coverage: NFRs → verifying tests
From non-functional-requirements.md; suite locations per testing.md.
| NFR | Target | Verifying test(s) | Gate |
|---|---|---|---|
| NFR-1 | page load < 2 s | e2e perf fixture (navigationStart→load, median of 3) + Lighthouse CI |
e2e job |
| NFR-2 | editor < 50 ms | in-app editor.interaction.duration otel metric (1 % sample) + 1 000-keystroke e2e |
nightly + phase-4 exit |
| NFR-3 | block move < 16 ms | e2e rAF-delta on 500-block page drag | phase-4 exit |
| NFR-4 | search < 500 ms | k6 search-latency.js (1M-doc fixture) + server-side query timing |
nightly |
| NFR-5 | API p95 < 300 ms | k6 api-load.js per-endpoint + otel histogram |
nightly + staging SLO |
| NFR-6 | realtime < 200 ms | realtime-latency.js (2-peer, 500 edits) |
phase-7 exit |
| NFR-7 | 100k-block page < 2 s | e2e seeded fixture + CDP memory snapshot | phase-3 exit |
| NFR-8 | 100k-record db | e2e fixture + db-query.js (100-row page < 300 ms) |
phase-5 exit |
| NFR-9 | 500 MB upload | e2e 500 MB + k6 upload.js (20 VUs × 50 MB) |
phase-9 exit |
| NFR-10 | concurrent edit no loss | e2e 2-browser opposite-ends ×20 | phase-7 exit |
| NFR-11 | batch 100 records < 1 s | k6 db-query.js write section |
nightly |
| NFR-12 | import 10k blocks < 60 s | e2e import timing | phase-10 |
| NFR-S1 | 100k records/db | fixture + EXPLAIN ANALYZE archive + insert soak |
phase-15 |
| NFR-S2 | 10k concurrent editors | ws-load.js (Artillery, 30 min) |
phase-15 |
| NFR-S3 | 1 000 rps | api-load.js 30-min hold |
phase-15 |
| NFR-S4 | 5k events/s, lag < 30 s | event-bus.js + bus.consumer.lag metric |
phase-15 |
| NFR-S5 | 10M docs, p95 < 500 ms | OpenSearch stats + search-latency.js |
phase-15 |
| NFR-S6 | 50 TB / 1k files/s | quota integration + pipeline depth under load | phase-15 |
| NFR-R1 | 99.9 % API SLO | 28-d SLO burn dashboard (staging→prod) | ongoing |
| NFR-R2 | realtime down ⇒ usable | chaos integration (kill realtime mid-edit) | phase-7 |
| NFR-R3 | search down ⇒ 503 + empty state | chaos (dead SEARCH_URL) + 13-flow run | phase-8 |
| NFR-R4 | redis down ⇒ 503 on enqueue-bound ops | chaos (redis failover drill) | phase-15 |
| NFR-R5 | zero loss on committed ops | outbox reconciliation integration test | every PR |
| NFR-R6 | pod kill < 30 s impact | k8s chaos (delete pod under load) | phase-15 |
| NFR-R7 | error budget → freeze | release checklist (SLO dashboard read) | release gate |
| NFR-SE1 | ASVS L2 | Phase-15 ASVS audit + SCA + pen test | phase-15 |
| NFR-SE2 | 0 authz bypasses | integration permission decision matrix (100 % cells) | every PR (CI gate) |
| NFR-SE3 | 0 injection vectors | e2e security/ payload suite + ZAP baseline |
every PR / staging |
| NFR-SE4 | 0 secrets in repo/history | gitleaks + trufflehog CI | every PR |
| NFR-SE5 | auth brute-force limits hold | credential-stuffing load test (5k attempts) | phase-15 |
| NFR-SE6 | EICAR blocked 100 %; no unsigned reads | files integration (EICAR + signed-URL expiry) | phase-9 |
| NFR-SE7 | 100 % mandatory audit events | audit scenario script integration suite | every PR (auth/perms) + phase-14 |
| NFR-SE8 | TLS 1.2+ / HSTS ≥ 1 y | TLS scanner + header assertions in e2e | staging CI |
| NFR-P1 | tenant isolation | cross-tenant matrix (50 pairs, 0 leaks) | every PR (CI gate) |
| NFR-P2 | no PII in logs | PII-scrubber test (seeded strings absent from logs) | every PR (logging) |
| NFR-P3 | platform retention defaults | retention integration tests (per scope) | phase-14 |
| NFR-P4 | enterprise retention (PURGE/HOLD) | policy→job→purge/hold integration (phase-14 exit) | phase-14 |
| NFR-P5 | export/delete user data | owner export + cascade-delete integration | phase-14 |
| NFR-P6 | cookie/tracking policy | cookie audit in e2e (no third-party cookies) | phase-15 |
| NFR-B1 | PG PITR | restore drill to T-10 min (quarterly) | phase-15 + quarterly |
| NFR-B2 | S3 versioning recovery | versioned-restore test | phase-15 |
| NFR-B3 | OpenSearch snapshot restore | snapshot restore + alias swap test | phase-15 |
| NFR-B4 | RPO ≤ 5 min | drill measurement (now − last WAL/snapshot) |
phase-15 + quarterly |
| NFR-B5 | RTO ≤ 1 h | full DR drill (timed, documented) | phase-15 + quarterly |
| NFR-B6 | backup verification | backup.verified jobs + staleness alert |
phase-15 |
| NFR-B7 | config reconstructable | clone-to-cluster drill (DR drill step) | phase-15 |
| NFR-O1–O6 | offline (edit, durability, sync, conflicts, scope) | offline.spec.ts (Playwright network emulation) + codec convergence tests |
phase-7 |
| NFR-M1/M2 | coverage 90 % engines / 70 % apps | Vitest/Jest coverageThreshold gates |
every PR |
| NFR-M3 | formula fuzz | nightly 10k fuzz job (0 crash/hang) + PR smoke 1k | nightly |
| NFR-M4 | strict TS, no any |
typecheck + eslint no-explicit-any (ratchet) |
every PR |
| NFR-M5 | dependency policy | PR template + SCA gate | every PR |
| NFR-M6 | import boundaries | eslint import/no-restricted-paths (0 exceptions) |
every PR |
| NFR-M7 | CI build < 15 min | turbo timing regression check | every merge |
| NFR-M8 | docs currency | PR checklist + docs-touch check (informational) | every PR |
| NFR-I1–I6 | i18n readiness | eslint i18n rule + Intl integration tests + RTL logical-property audit | every PR / phase-15 |
| NFR-H1 | one-command local | Phase-0 CI matrix (clean clone) | every merge |
| NFR-H2 | prod from git | clone-to-cluster drill | phase-15 |
| NFR-H3 | resource floor | staging sizing parity check | phase-15 |
| NFR-H4 | zero-downtime upgrade | rolling-deploy under load test | phase-15 |
| NFR-H5 | telemetry opt-out | env default check (OTEL_ENABLED=false) + no-egress test |
phase-15 |
3. Coverage: API endpoints → requirements
REST API v1 surface (full contract in docs/api/, generated from
packages/shared + Nest decorators — PR-152). Auth = session cookie or
API-key/OAuth bearer. ⚿ = permission required (level per
security-architecture §3).
| Endpoint (v1) | Purpose | Requirement(s) |
|---|---|---|
POST /auth/register · POST /auth/verify · POST /auth/login · POST /auth/logout · POST /auth/refresh |
auth lifecycle | PR-001…005 |
GET /auth/sessions · DELETE /auth/sessions/:id |
session management | PR-005 |
POST /auth/password/forgot · POST /auth/password/reset |
password reset | PR-006 |
GET /users/me · PATCH /users/me |
profile | PR-008 |
POST /workspaces · GET /workspaces · PATCH /workspaces/:id · DELETE /workspaces/:id |
workspace CRUD | PR-013…015 |
GET/PATCH /workspaces/:id/members · POST /workspaces/:id/invitations (+accept/revoke) |
members & invites | PR-016…018 |
GET/POST /workspaces/:id/teams · PATCH /teams/:id · members |
teams | PR-019 |
GET /workspaces/:id/sidebar (tree, depth-limited, cursor) |
sidebar data | PR-020 |
GET/PATCH /workspaces/:id/settings |
settings | PR-021 |
POST /organizations · members · POST /scim/v2/* · retention policies |
org + enterprise | PR-022…024, PR-155, PR-156 |
GET/POST /workspaces/:id/pages (+children, ?depth) · PATCH/DELETE /pages/:id · `POST /pages/:id/restore |
purge | duplicate |
GET /pages/:id/versions · POST /pages/:id/versions/:v/restore |
version history | PR-031 |
GET/POST /pages/:id/blocks · PATCH /blocks/:id · POST /blocks:batch (move/insert, fractional, 1 txn) · DELETE /blocks/:id |
block engine | PR-034…044 |
GET/POST /workspaces/:id/databases · PATCH /databases/:id (+properties, views) |
database schema | PR-057, PR-074, PR-070 |
GET /databases/:id/records (view query: filter/sort/group/pageSize/cursor) · POST /databases/:id/records (batch ≤ 100) · PATCH /records/:id · DELETE /records/:id |
records (record = page) | PR-060, PR-061, PR-062, PR-071…073, PR-148 |
POST /databases/:id/query |
the shared query engine (UI + API parity) | PR-070…073, PR-148 |
relation/rollup/formula/button endpoints (POST /databases/:id/relations, …/rollups, …/formulas (parse+validate), …/buttons, POST /buttons/:id/run) |
computed layer | PR-075…083 |
GET/POST /files:prepare · POST /files/:id:complete · GET /files/:id (signed URL) · GET /files/:id/download |
file pipeline | PR-105…111 |
GET/POST /pages/:id/comments (+replies, reactions, resolve) |
comments | PR-091…093 |
GET /notifications (+mark-read) · preferences |
inbox | PR-095…097 |
POST /search · GET /search/suggest |
search | PR-098…104 |
GET/POST /workspaces/:id/templates · POST /templates/:id/instantiate |
templates | PR-121…123 |
POST /imports · POST /exports (jobs + progress) |
import/export | PR-124…126 |
GET/POST /databases/:id/forms (+questions) · POST /forms/:id/publish · public POST /public/forms/:slug/submissions |
forms | PR-127…130, PR-069 |
charts/dashboards (POST /databases/:id/charts, POST /workspaces/:id/dashboards, widgets) |
dashboards | PR-131…133, PR-068 |
GET/POST /workspaces/:id/automations (+triggers/conditions/actions) · POST /automations/:id/run · runs + logs |
automation | PR-134…140 |
GET/POST /ai/chat (SSE) · POST /ai/actions/* (summarize/rewrite/generate) · POST /ai/search (RAG) |
AI | PR-141…147 |
GET /api/v1/openapi.json · /docs |
contract + docs | PR-152 |
API keys (POST /workspaces/:id/api-keys, revoke, list) · OAuth (/oauth/authorize, /oauth/token) |
public API auth | PR-149, PR-150 |
webhooks (POST /workspaces/:id/webhooks, list/deliveries) · inbound POST /api/v1/webhooks/inbound/:path |
webhooks | PR-151, PR-135 (WEBHOOK trigger) |
permissions (GET/POST /pages/:id/permissions, DELETE …/:grantId, share dialog data) · POST /pages/:id/shares |
permissions & sharing | PR-113…119 |
public (unauthenticated) GET /share/:token (page render) · GET /forms/:slug |
public surfaces | PR-118, PR-128, PR-133 |
admin (POST /admin/search/reindex, queue reprocess, audit export) |
ops | PR-102, PR-137, PR-154 |
realtime (WS, not REST): ws?token=… join/update/presence frames |
collaboration | PR-084…090 |
Unmapped-by-design: /healthz/* (liveness/readiness — no PR, infra
contract), static assets. Every other route must appear in this table (the
OpenAPI contract test enforces the reverse direction too: no live endpoint
without a documented requirement).
4. Maintenance rules for this matrix
- 1:1 with the registry: a PR id exists in exactly one of
product-requirements.md+ this file; adding a requirement means adding it to both in the same PR (phase owner's job; CI can diff the id sets — the id list is grep-stable:PR-\d{3}). - Status discipline: a row turns 🟨 when its task file's work starts, ✅ only when the task's Definition of Done is verified (AGENTS.md rule 30) — the ✅ commit must include the test evidence (suite name / run link).
- Phase changes (a requirement slipping or landing early) are recorded here + in ROADMAP, with the task file as the arbiter.
- Coverage sections update when a test suite is added or an endpoint is introduced; a new endpoint without a row in §3 fails the PR review (coding-standards DOC1).