Files
q-blog/docs/Q-Blog_1.0.0_ROADMAP_implementation.md

365 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
**TL;DR:** Below is a phase-by-phase implementation guide: what libraries well use, what kinds of tests well write, what contracts/functions well define, and how well validate each step. Still high-level—no filenames or code dropped—yet detailed enough to steer day-to-day work.
---
# Q-Blog → 1.0.0 Implementation Plan (Technical, High-Level)
## Phase 0 — Orientation & Quality Bar
**Libraries / tools**
- Issue labels & PR templates (tracker-native).
- Metrics: Web Vitals (`web-vitals`), synthetic a11y checks (`axe-core` via CI).
**Artifacts**
- **Quality charter**: SLOs (e.g., “save post success ≥ 99.9%”), accessibility bar (keyboard-only paths pass), perf budgets (LCP/INP).
- **Protected journeys** list: _read post, create post, edit post, manage blogs_.
**Validation**
- Signed-off charter; journeys ratified in a short doc.
---
## Phase 1 — Project Docs & Working Agreements
**Libraries / tools**
- Diagrams as code (Mermaid in docs renderer).
- Conventional commits (tooling optional).
**Artifacts**
- **Architecture map** (UI ↔ state ↔ API), **Testing pyramid**, **A11y standard**, **Contributing rules**, **Changelog categories**.
- **Decision Record template** (context → options → decision → impact).
**Validation**
- New contributor dry run: “can run + test + understand contracts in <30 min.”
---
## Phase 2 — Test Harness & Linting Baseline
**Libraries**
- Unit/Component: **Vitest**, **@testing-library/react**, **@testing-library/user-event**, **@testing-library/jest-dom**.
- A11y lint/checks: **eslint-plugin-jsx-a11y**, **axe-core** or **jest-axe**.
- Mocking: **MSW** (Mock Service Worker) for API.
- Types: TypeScript strict mode.
- Lint/format: **eslint** (flat config), **@typescript-eslint** plugin, **prettier**.
**Key setup**
- Global test setup (RTL matchers, MSW server, clock/timers).
- Coverage thresholds (e.g., 80/70/70 lines/branches/functions to start).
- CI lanes: install/cache → typecheck → lint → unit/component → (later) e2e smoke.
**Tests to seed**
- Smoke renders: home, post list, editor screen.
- Tiny interaction: typing in editor, enabling/disabling toolbar control, save button disabled until valid.
**Validation**
- Green **typecheck + lint + tests** on clean checkout; coverage reports generated.
---
## Phase 3 — Correctness Sweep
**Libraries**
- RTK ecosystem: **@reduxjs/toolkit** (already present), **RTK Query** for data-fetching (adds caching, retries, invalidation).
- Schema/runtime validation: **Zod** (form/io validation) **and** `zod-to-json-schema` for docs **or** **AJV** if we align with JSON Schema like Q-Chess. (Pick one; see “Contracts” below.)
**Contracts / functions**
- **Fetch layer** with cancellation & retries:
- `fetchJson<T>(req: Request, opts): Promise<Result<T>>`
- `withTimeout(controller, ms): AbortSignal`
- **Async state convention**: `{ status: 'idle'|'loading'|'success'|'error', data?, error? }`.
- **Error object**: `{ code: string; message: string; recoverable: boolean }`.
- **Validation**:
- For Zod: `const PostDto = z.object({ id: z.string(), ... })``parse(response)`.
- For AJV: compile JSON Schemas once; validate per response.
**Work items**
- Remove legacy UI imports (v4 → v5), delete `ts-nocheck` sections by adding minimal types.
- Replace `any` in reducers/selectors and editor props with discriminated unions & precise payloads.
- Normalize empty/loading/error states for all remote views.
**Tests**
- Contract tests: MSW returns both **valid** and **invalid** payloads → validation errors surface as user-friendly messages.
- Reducer tests: transitions for each thunk/query state.
**Validation**
- No suppressed type regions; “known issues” list empty; core routes never hard-crash on bad data.
---
## Phase 4 — Accessibility Baseline
**Libraries**
- A11y testing: **jest-axe** or **axe-core** integration; **testing-library** queries by role/name.
- Focus management: rely on MUIs **Dialog/Popover** focus traps; add **focus-trap** only where needed.
**Contracts / behaviors**
- **Landmarks**: header/nav/main/footer; **Skip link** available and visible on focus.
- **Focus policy**: focus moves to first interactive element on open; returns to invoker on close.
- **Names/roles/states**: toggles expose `aria-pressed`; inputs have programmatic labels; errors are linked with `aria-describedby`.
- **Live regions**: polite updates for “saving…/uploaded/failed”.
- **Motion/contrast**: respect `prefers-reduced-motion`; enforce contrast tokens.
**Tests**
- Keyboard paths for protected journeys (Tab/Shift+Tab order, Escape close).
- axe checks for key pages (no critical violations).
- Live region announces long-running operations (assertions via `toHaveAccessibleDescription` or role queries).
**Validation**
- Keyboard-only success across protected journeys; automated checks pass with zero criticals.
---
## Phase 5 — UX & IA Touch-up
**Libraries**
- Router (current choice): ensure route guards & prefetch hooks supported.
- Toasts/banners: lightweight (e.g., notistack or MUI Snackbar pattern).
**Contracts / functions**
- **Design tokens**: spacing, radius, typography, color roles (text, surface, brand) centralized.
- **Common UI patterns**:
- Empty state contract: `{ icon?, title, body, primaryAction?, secondaryAction? }`
- Error surface: banner + inline guidance; all errors have recovery action.
- **Editor ergonomics**:
- Toolbar state machine: `computeToolbarState({ selection, schema }) → { boldEnabled, … }`
- Draft lifecycle: `autosaveDraft(postId, content) → { savedAt }` with debounce and “last saved” indicator.
**Tests**
- Token snapshots for theme roles; editor toolbar enable/disable based on selection.
- Error/empty states render consistently across pages (table-driven tests).
**Validation**
- Navigation clarity (first-click discoverability); zero dead ends; consistent affordances.
---
## Phase 6 — Multiple Blogs per Name
**Libraries**
- State & data: continue **RTK Query** (cache per blog key).
- Validation: same as Phase 3 (Zod/AJV).
**Domain model**
- **Name** (account) 1..N **Blog**; **Post** 1..1 **Blog** (immutable relationship post-creation).
- **Identifiers**:
- Blog handle: lowercase, slug rules; unique per **Name**; stored canonical form.
- URL composition: `/{name}/{blogHandle}/…` (conceptual, router-agnostic).
**Functions / contracts**
- `listBlogs(name): Promise<Blog[]>`
- `createBlog(input: { handle; title; visibility }): Promise<Blog>`
- `switchBlog(handle): void` (updates app-scoped “current blog”)
- `listPosts(blogId, filters)`, `createPost(blogId, input)`
- **Migration**: `backfillDefaultBlogs(): MigrationResult` (idempotent: skip if blog exists)
**Tests**
- Migration test with legacy dataset → posts appear under default blog.
- Router/selector scoping: when switching blogs, lists & counts update without leaking data.
- Handle validation: rejects collisions & invalid slugs; normalizes input.
**Validation**
- Users can create/switch blogs; all lists/summaries are scoped; legacy users retained seamlessly.
---
## Phase 7 — Shared Blogs (Collaboration)
**Libraries**
- Role/permission helpers: in-house constants; optional **casl** if we want DSL-like permissions (not required).
**Role model**
- **Owner**, **Editor**, **Author**, **Viewer** (implicit).
- Operation matrix:
- Owner: all
- Editor: edit any post, manage drafts
- Author: CRUD own posts
**Functions / contracts**
- Membership:
- `inviteMember(blogId, name, role): InviteToken`
- `acceptInvite(token): Membership`
- `removeMember(blogId, name): void`
- `listMembers(blogId): Membership[]`
- Authorization guard:
- `can(op: 'create'|'edit'|'publish'|'manageMembers', ctx: { user, blog, post? }): boolean`
- Attribution:
- Post metadata: `{ createdBy, updatedBy, updatedAt, revision }`
- Concurrency:
- `savePost(postId, input, { ifMatchRevision }): { revision }` → 409 on stale; client shows resolve UI.
**Tests**
- Permission matrix table tests (unit): each role × operation result.
- API guard tests via MSW: simulate forbidden responses, assert UI disables and surfaces denial.
- Concurrency tests: stale write attempt returns conflict → user offered resolve path.
**Validation**
- Owner can manage access; editors/authors can work within role; no unauthorized writes pass.
---
## Milestone Updates
- v0.1.x — Multiblog foundations and quality baselines delivered.
- v0.2.0 — Wiki Mode canonical selection shipped (owner/whitelist/blacklist; canonical dedupe across Names in feed, favorites, subscriptions, and post view). Settings cache added for efficient owner/settings resolution.
## Phase 8 — Performance & Resilience
**Libraries**
- **react-virtuoso** or equivalent (already used) for long lists.
- Data layer retries/backoff: **RTK Query**s retry plugin or custom wrapper.
- RUM metrics: **web-vitals** + lightweight sender.
**Functions / policies**
- **Optimistic updates** where safe (draft save, title edits) with rollback on failure.
- **Retry policy**: GETs exponential backoff; POST/PUT guarded by idempotency keys where applicable.
- **Abortable fetch**: timeouts and user-initiated cancel for uploads.
- **Prefetching**: on-hover/visible prefetch of likely next routes and data keys.
**Tests**
- Latency injection with MSW to verify spinners/skeletons and cancel/abort behaviors.
- Optimistic update rollback test: forced server failure restores previous UI state.
**Validation**
- Subjective feel: interactions seem instant; objective: Web Vitals within budget; error paths recover without data loss.
---
## Phase 9 — Internationalization & Theming Consistency
**Libraries**
- **react-i18next** (already used in sister projects).
- Intl APIs for date/number/plural; fallback polyfills if needed.
**Functions / contracts**
- `t('namespace:key', { vars })` usage with explicit, descriptive keys.
- Currency/number/date formatters as wrapper utilities to standardize formatting.
- Theme tokens as a single source of truth; contrast checks (scripted) against WCAG AA.
**Tests**
- Snapshot tests for key screens in both light/dark with token diffs.
- Formatting tests for pluralization and number/date locales.
**Validation**
- Strings externalized; theme passes automated contrast check; RTL-safety spot check on core screens.
---
## Phase 10 — Observability, Security & Release Readiness
**Libraries**
- Error tracking: **Sentry** (or equivalent) with source maps.
- Sanitization: **DOMPurify** for any HTML serialization/render of editor output.
- CSP guidance (server-side; document the policy).
**Functions / policies**
- **Error boundaries**: route-level and editor-level with friendly fallback and “copy details”.
- **Client logging**: `logClientError({ code, message, context })` throttle; opt-in breadcrumbs (sanitized, no PII).
- **Security checks**:
- Validate all inputs client-side with Zod/AJV before sending.
- Sanitize rich text on ingest and display; allowlist for marks/nodes.
- Permission checks every write path; never trust client-side hide/disable alone.
- **Migrations**:
- Versioned, idempotent; dry-run flag; record migration status.
- **Release discipline**:
- Version bump + changelog; migration notes; upgrade guide; go/no-go checklist (green gates + budgets met).
**Tests**
- Error boundary renders fallback on thrown child component; telemetry is called with redacted payload.
- XSS tests: paste malicious payload → sanitized output (no script execution) both save and render paths.
- Migration dry-run/effects tests with sample datasets.
**Validation**
- Crashes captured with actionable context; no stored XSS vectors; release checklist green.
---
## Cross-Cutting Standards
**Contracts-first**
- Pick **Zod** _(form/input + client response validation, type inference)_ or **JSON Schema + AJV** _(cross-project parity with Q-Chess)_.
- If Zod: generate JSON Schema for docs via `zod-to-json-schema`.
- If AJV: generate TypeScript types via `json-schema-to-ts`.
- Maintain a single **roles/status/constants** module imported across UI, data, and tests.
**Data access layer**
- Prefer **RTK Query** for cache, memoized selectors, retries, and invalidation; treat queries/mutations as the one path to remote state.
**State shaping**
- Store UI state distinct from server cache; selectors derive view models (small pure functions, unit-tested).
**Security posture**
- Default-deny in permission checks; treat editor input as untrusted; output sanitize on every render path.
**Testing pyramid (target distributions)**
- Unit (\~60%): pure utils, selectors, reducers, permission guards.
- Component (\~30%): screens/components with RTL (+ axe).
- Integration/E2E (\~10%): happy-path create/edit/publish; blog switch; invite/accept.
**CI**
- Node LTS matrix (current + previous).
- Caching for package manager.
- Artifacts: coverage report, axe report, Web Vitals synthetic (if applicable), build output for preview.
---
## Example Function Signatures (illustrative, no filenames)
```ts
// Networking & validation
type Result<T> =
| { ok: true; data: T }
| { ok: false; error: { code: string; message: string; recoverable: boolean } };
function fetchJson<T>(
input: RequestInfo,
init?: RequestInit & { timeoutMs?: number },
): Promise<Result<T>>;
// Permissions
type Role = 'owner' | 'editor' | 'author';
function can(
op: 'create' | 'edit' | 'publish' | 'manageMembers',
ctx: { role: Role; userId: string; postOwnerId?: string },
): boolean;
// Blog management
function createBlog(input: {
handle: string;
title: string;
visibility: 'public' | 'private';
}): Promise<Result<Blog>>;
function inviteMember(blogId: string, name: string, role: Role): Promise<Result<{ token: string }>>;
function savePost(
postId: string,
input: PostInput,
opts: { ifMatchRevision: number },
): Promise<Result<{ revision: number }>>;
```
---
## Completion Bars (what “done” looks like per category)
- **Correctness:** zero suppressed type regions; known issues = empty; core flows never hard-crash.
- **Testability:** green typecheck/lint/tests on clean checkout; coverage trend up and stable; fast local watch.
- **Accessibility:** keyboard-only paths pass; axe shows no criticals; live region & focus rules verified.
- **Usability:** consistent patterns for empty/error; editor ergonomics predictable; no dead ends.
- **Security:** sanitized rich text; permission matrix enforced server- and client-side; no privilege escalation paths.
- **Performance:** web vitals within budget; optimistic updates/abortable fetches; graceful failure/retry.
---
**Assumptions & Risks:**
We can evolve API contracts to support multi-blog and roles; legacy data maps cleanly to default blogs; Slate output can be sanitized without losing required formatting; e2e scope remains minimal to avoid CI flake—expand only if signals require it.