From f0d500a8d287de0d7725d04b725202917cd79a4a Mon Sep 17 00:00:00 2001 From: Jack D Date: Wed, 11 Mar 2026 14:23:01 +0000 Subject: [PATCH 1/2] Update docs --- AGENTS.md | 15 + CONTRIBUTING.md | 12 +- docs/convex-type-safety-playbook.md | 380 ++++++++++++++++++ docs/runtime-type-hardening-2026-03-05.md | 2 + ...refactor-assessment-evidence-2026-03-05.md | 0 .../refactor-assessment-roadmap-2026-03-05.md | 0 ...assessment-web-widget-convex-2026-03-05.md | 0 ...or-handoff-web-widget-convex-2026-03-06.md | 0 ...factor-opportunities-ranking-2026-03-03.md | 0 .../refactor-opportunity-audit-2026-03-06.md | 0 ...ence-rule-contract-alignment-2026-03-05.md | 0 ...e-outbound-trigger-contracts-2026-03-05.md | 0 ...e-outbound-trigger-contracts-2026-03-06.md | 0 ...convex-auth-wrapper-adoption-2026-03-05.md | 0 ...paign-delivery-decomposition-2026-03-05.md | 0 ...enger-settings-decomposition-2026-03-07.md | 0 ...nvex-reporting-decomposition-2026-03-05.md | 0 ...ress-convex-schema-fragments-2026-03-05.md | 0 ...ma-high-concentration-tables-2026-03-06.md | 0 ...-series-runtime-authoring-v2-2026-03-07.md | 0 ...-tour-progress-decomposition-2026-03-07.md | 0 ...onvex-visitors-decomposition-2026-03-05.md | 0 ...-surface-compatibility-gates-2026-03-05.md | 0 ...home-config-shared-contracts-2026-03-05.md | 0 ...er-home-contract-convergence-2026-03-07.md | 0 ...untime-shared-route-matching-2026-03-06.md | 0 ...s-visitor-readable-id-shared-2026-03-05.md | 0 ...articles-admin-decomposition-2026-03-05.md | 0 ...ogress-web-e2e-stabilization-2026-03-06.md | 0 ...ss-web-inbox-render-sections-2026-03-05.md | 0 ...enger-settings-decomposition-2026-03-07.md | 0 ...utbound-editor-decomposition-2026-03-05.md | 0 ...utbound-editor-decomposition-2026-03-06.md | 0 ...-series-editor-decomposition-2026-03-05.md | 0 ...ettings-domain-decomposition-2026-03-05.md | 0 ...-survey-editor-decomposition-2026-03-05.md | 0 ...eb-tour-editor-decomposition-2026-03-06.md | 0 ...versation-view-decomposition-2026-03-05.md | 0 ...ell-controller-decomposition-2026-03-05.md | 0 ...idget-shell-orchestration-v2-2026-03-06.md | 0 ...survey-overlay-decomposition-2026-03-05.md | 0 ...t-tour-overlay-decomposition-2026-03-05.md | 0 ...admin-security-onboarding-v1-2026-03-07.md | 0 .../refactor-remaining-map-2026-03-05.md | 0 ...actor-remaining-slices-pass2-2026-03-05.md | 0 45 files changed, 408 insertions(+), 1 deletion(-) create mode 100644 docs/convex-type-safety-playbook.md rename {docs => openspec/changes/archive}/refactor-assessment-evidence-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-assessment-roadmap-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-assessment-web-widget-convex-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-handoff-web-widget-convex-2026-03-06.md (100%) rename {docs => openspec/changes/archive}/refactor-opportunities-ranking-2026-03-03.md (100%) rename {docs => openspec/changes/archive}/refactor-opportunity-audit-2026-03-06.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-audience-rule-contract-alignment-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-centralize-outbound-trigger-contracts-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-centralize-outbound-trigger-contracts-2026-03-06.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-convex-auth-wrapper-adoption-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-convex-campaign-delivery-decomposition-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-convex-messenger-settings-decomposition-2026-03-07.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-convex-reporting-decomposition-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-convex-schema-fragments-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-convex-schema-high-concentration-tables-2026-03-06.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-convex-series-runtime-authoring-v2-2026-03-07.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-convex-tour-progress-decomposition-2026-03-07.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-convex-visitors-decomposition-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-cross-surface-compatibility-gates-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-home-config-shared-contracts-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-messenger-home-contract-convergence-2026-03-07.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-tour-runtime-shared-route-matching-2026-03-06.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-visitor-readable-id-shared-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-web-articles-admin-decomposition-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-web-e2e-stabilization-2026-03-06.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-web-inbox-render-sections-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-web-messenger-settings-decomposition-2026-03-07.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-web-outbound-editor-decomposition-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-web-outbound-editor-decomposition-2026-03-06.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-web-series-editor-decomposition-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-web-settings-domain-decomposition-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-web-survey-editor-decomposition-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-web-tour-editor-decomposition-2026-03-06.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-widget-conversation-view-decomposition-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-widget-shell-controller-decomposition-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-widget-shell-orchestration-v2-2026-03-06.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-widget-survey-overlay-decomposition-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-widget-tour-overlay-decomposition-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-progress-workspace-admin-security-onboarding-v1-2026-03-07.md (100%) rename {docs => openspec/changes/archive}/refactor-remaining-map-2026-03-05.md (100%) rename {docs => openspec/changes/archive}/refactor-remaining-slices-pass2-2026-03-05.md (100%) diff --git a/AGENTS.md b/AGENTS.md index 26426e9..b8811af 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,6 +26,7 @@ ### Convex TypeScript deep-instantiation workaround +- Canonical guide: `docs/convex-type-safety-playbook.md` - If Convex typecheck hits `TS2589` (`Type instantiation is excessively deep and possibly infinite`) at generated refs like `api.foo.bar` or `internal.foo.bar`, prefer a **local escape hatch** instead of broad weakening. - First keep call signatures shallow at the hot spot: - cast `ctx.scheduler.runAfter`, `ctx.runQuery`, or `ctx.runMutation` to a local shallow function type. @@ -33,6 +34,20 @@ - Keep this workaround **localized only to pathological sites**. Continue using generated `api` / `internal` refs normally elsewhere. - Expect hidden follow-on errors: rerun `pnpm --filter @opencom/convex typecheck` after each small batch of fixes, because resolving one deep-instantiation site can reveal additional ones. +## Convex Type Safety Standards + +- Read `docs/convex-type-safety-playbook.md` before adding new Convex boundaries. +- Frontend runtime/UI modules must not import `convex/react` directly. Use local adapters and wrapper hooks instead. +- Keep Convex refs at module scope. Never create `makeFunctionReference(...)` values inside React components or hooks. +- Do not add new `getQueryRef(name: string)`, `getMutationRef(name: string)`, or `getActionRef(name: string)` factories. +- Backend cross-function calls should use generated `api` / `internal` refs by default. Only move to fixed `makeFunctionReference("module:function")` refs after a real `TS2589` hotspot is confirmed. +- Keep unavoidable casts localized to adapters or named backend hotspot helpers. Do not spread `as unknown as`, `unsafeApi`, or `unsafeInternal` through runtime code. +- After changing a boundary, update the relevant hardening guard: + - `packages/convex/tests/runtimeTypeHardeningGuard.test.ts` + - `apps/web/src/app/typeHardeningGuard.test.ts` + - `apps/widget/src/test/refHardeningGuard.test.ts` + - `packages/react-native-sdk/tests/hookBoundaryGuard.test.ts` + ### Tests - Convex targeted file: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 83f1dfc..92ce27c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,6 +11,7 @@ Thanks for contributing to Opencom. This guide covers everything you need to get - [Security & Operations](docs/open-source/security-and-operations.md) - [Data Model Reference](docs/data-model.md) - [Backend API Reference](docs/api-reference.md) +- [Convex Type Safety Playbook](docs/convex-type-safety-playbook.md) - [Scripts Reference](docs/scripts-reference.md) ## Development Setup @@ -87,24 +88,33 @@ opencom/ - Visitor endpoints require `sessionToken` validated via `resolveVisitorFromSession()`. - System/bot actions use `internalMutation` to bypass external auth. - Use `v.any()` sparingly and document in `security/convex-v-any-arg-exceptions.json`. +- Follow `docs/convex-type-safety-playbook.md` for all new Convex boundaries. +- Default backend-to-backend calls to generated `api` / `internal` refs. +- If a call site hits `TS2589`, keep the workaround local: + - shallow `ctx.runQuery` / `ctx.runMutation` / `ctx.runAction` / `runAfter` helper first + - fixed typed `makeFunctionReference("module:function")` only if the generated ref still fails +- Do not add new generic `get*Ref(name: string)` factories or broad `unsafeApi` / `unsafeInternal` aliases. ### Frontend (Web / Landing) - Next.js App Router. - Tailwind CSS + Shadcn UI components from `@opencom/ui`. - React context for auth (`AuthContext`) and backend connection (`BackendContext`). -- Convex React hooks for data fetching (real-time subscriptions). +- Use local Convex wrapper hooks and adapters for data fetching. +- Do not import `convex/react` directly into feature/runtime UI modules. ### Widget - Vite-built IIFE bundle. Target: <50KB gzipped. - No external dependencies beyond Convex client. - All visitor calls thread `sessionToken`. +- Use `apps/widget/src/lib/convex/hooks.ts` plus feature-local wrappers instead of direct `convex/react` imports in runtime files. ### Mobile - Expo / React Native. - Same auth patterns as web (Convex Auth + BackendContext). +- New mobile Convex usage should follow the same local-wrapper pattern as web/widget instead of adding new direct screen/context-level hook usage. ## Verification Workflow diff --git a/docs/convex-type-safety-playbook.md b/docs/convex-type-safety-playbook.md new file mode 100644 index 0000000..3b4198d --- /dev/null +++ b/docs/convex-type-safety-playbook.md @@ -0,0 +1,380 @@ +# Convex Type Safety Playbook + +This is the canonical guide for adding or changing Convex-backed code in this repo. + +Use it when you are: + +- adding a new Convex `query`, `mutation`, or `action` +- calling one Convex function from another +- wiring a new frontend feature to Convex +- hitting `TS2589` (`Type instantiation is excessively deep and possibly infinite`) +- deciding where a cast or `makeFunctionReference(...)` is acceptable + +Historical hardening notes still exist in `docs/refactor-*` and `docs/runtime-type-hardening-2026-03-05.md`, but this file is the current source of truth for new code. + +## Goals + +- Keep runtime and UI code on explicit, local types. +- Keep unavoidable Convex typing escape hatches small and centralized. +- Prevent new generic string-ref factories, broad casts, and component-local ref creation. +- Make it obvious which pattern to use for each call site. + +## Decision Table + +| Situation | Preferred approach | Where | Why | +| --- | --- | --- | --- | +| Define a new public Convex query or mutation | Export a normal Convex function with narrow `v.*` args and a narrow return shape | `packages/convex/convex/**` | Keeps the source contract explicit and reusable | +| Call Convex from web or widget UI/runtime code | Use the local surface adapter plus a feature-local wrapper hook or fixed ref constant | `apps/web/src/**`, `apps/widget/src/**` | Keeps `convex/react` and ref typing out of runtime/UI modules | +| Call one Convex function from another and generated refs typecheck normally | Use generated `api.*` / `internal.*` refs | `packages/convex/convex/**` | This is the default, simplest path | +| Call one Convex function from another and generated refs hit `TS2589` | Add a local shallow `runQuery` / `runMutation` / `runAction` / `runAfter` helper | the hotspot file only | Shrinks type instantiation at the call boundary | +| The generated ref itself still triggers `TS2589` | Replace only that hot ref with a fixed, typed `makeFunctionReference("module:function")` constant | the hotspot file only | Avoids broad weakening of the entire module | +| Convex React hook tuple typing still needs help | Keep a tiny adapter-local helper/cast in the surface adapter | adapter file only | Localizes the last unavoidable boundary | + +## Non-Negotiable Rules + +### 1. Do not import `convex/react` in runtime or feature UI files + +Use the surface adapter layer instead: + +- web: `apps/web/src/lib/convex/hooks.ts` +- widget: `apps/widget/src/lib/convex/hooks.ts` +- mobile: follow the same local-wrapper pattern; do not add new direct screen/context-level `convex/react` usage + +Direct `convex/react` imports are only acceptable in: + +- explicit adapter files +- bootstrap/provider wiring +- targeted tests that intentionally mock the adapter boundary + +The current hardening guards freeze these boundaries: + +- `apps/web/src/app/typeHardeningGuard.test.ts` +- `apps/widget/src/test/refHardeningGuard.test.ts` +- `packages/react-native-sdk/tests/hookBoundaryGuard.test.ts` + +### 2. Do not create refs inside React components or hooks + +Bad: + +```ts +function WidgetPane() { + const listRef = makeFunctionReference<"query", Args, Result>("messages:list"); + const data = useQuery(listRef, args); +} +``` + +Good: + +```ts +const LIST_REF = widgetQueryRef("messages:list"); + +function WidgetPane() { + const data = useWidgetQuery(LIST_REF, args); +} +``` + +All refs must be module-scope constants. + +### 3. Do not add new generic string-ref factories + +Do not introduce helpers like: + +- `getQueryRef(name: string)` +- `getMutationRef(name: string)` +- `getActionRef(name: string)` + +Those patterns weaken the type boundary and make review harder. Some older code still has them, but they are legacy, not the standard for new work. + +Use named fixed refs instead: + +```ts +const LIST_MESSAGES_REF = webQueryRef("messages:list"); +const SEND_MESSAGE_REF = webMutationRef>("messages:send"); +``` + +### 4. Keep casts local, named, and justified + +Allowed: + +- a tiny adapter-local cast needed to satisfy a Convex hook tuple type +- a hotspot-local shallow helper for `ctx.runQuery`, `ctx.runMutation`, `ctx.runAction`, or `ctx.scheduler.runAfter` +- a hotspot-local typed `makeFunctionReference("module:function")` when generated refs trigger `TS2589` + +Not allowed for new code: + +- `as any` +- broad `unsafeApi` / `unsafeInternal` object aliases in runtime code +- repeated `as unknown as` across multiple call sites +- hiding transport typing inside UI/controller modules + +### 5. Update guard tests when you add or move a boundary + +If you intentionally add a new approved boundary, document it in the relevant guard test at the same time. + +## Standard Patterns + +## A. Defining a new Convex query or mutation + +Default backend pattern: + +```ts +import { query, mutation } from "./_generated/server"; +import { v } from "convex/values"; +import type { Id } from "./_generated/dataModel"; + +type VisitorSummary = { + _id: Id<"visitors">; + name?: string; + email?: string; +}; + +export const listByWorkspace = query({ + args: { + workspaceId: v.id("workspaces"), + }, + handler: async (ctx, args): Promise => { + const visitors = await ctx.db + .query("visitors") + .withIndex("by_workspace", (q) => q.eq("workspaceId", args.workspaceId)) + .collect(); + + return visitors.map((visitor) => ({ + _id: visitor._id, + name: visitor.name, + email: visitor.email, + })); + }, +}); +``` + +Rules: + +- Use narrow `v.*` validators. +- Prefer explicit local return types for shared, frontend-facing, or cross-function contracts. +- Convert untyped or broad data to a narrow shape before returning it. +- If you need `v.any()`, document it in `security/convex-v-any-arg-exceptions.json`. + +## B. Consuming Convex from web or widget code + +Runtime/UI files should consume feature-local wrappers or fixed refs through the local adapter. + +Widget example: + +```ts +import type { Id } from "@opencom/convex/dataModel"; +import { useWidgetQuery, widgetQueryRef } from "../lib/convex/hooks"; + +type TicketRecord = { + _id: Id<"tickets">; + subject: string; +}; + +const VISITOR_TICKETS_REF = widgetQueryRef< + { workspaceId: Id<"workspaces">; visitorId: Id<"visitors">; sessionToken: string }, + TicketRecord[] +>("tickets:listByVisitor"); + +export function useVisitorTickets( + workspaceId: Id<"workspaces"> | undefined, + visitorId: Id<"visitors"> | null, + sessionToken: string | null +) { + return useWidgetQuery( + VISITOR_TICKETS_REF, + workspaceId && visitorId && sessionToken + ? { workspaceId, visitorId, sessionToken } + : "skip" + ); +} +``` + +Use the same structure in web with `webQueryRef`, `webMutationRef`, `webActionRef`, `useWebQuery`, `useWebMutation`, and `useWebAction`. + +Rules: + +- Define refs once at module scope. +- Keep `skip` / gating logic in the wrapper where practical. +- Export narrow feature-local result types instead of leaking giant inferred shapes. +- Do not import `convex/react` directly in feature components, screens, contexts, or controller hooks. + +## C. Calling one Convex function from another + +### Preferred default: generated refs + +Start here when the types are normal: + +```ts +import { internal } from "./_generated/api"; + +await ctx.runMutation(internal.notifications.deliver, { + conversationId, +}); +``` + +This is the standard path until it hits a real `TS2589` problem. + +### TS2589 fallback: shallow helper first + +If `ctx.runQuery(...)`, `ctx.runMutation(...)`, `ctx.runAction(...)`, or `ctx.scheduler.runAfter(...)` causes deep-instantiation errors, add a local helper: + +```ts +import { type FunctionReference } from "convex/server"; + +type ConvexRef< + Type extends "query" | "mutation" | "action", + Visibility extends "internal" | "public", + Args extends Record, + Return = unknown, +> = FunctionReference; + +function getShallowRunMutation(ctx: { runMutation: unknown }) { + return ctx.runMutation as unknown as < + Visibility extends "internal" | "public", + Args extends Record, + Return, + >( + mutationRef: ConvexRef<"mutation", Visibility, Args, Return>, + mutationArgs: Args + ) => Promise; +} +``` + +Then call through the helper: + +```ts +const runMutation = getShallowRunMutation(ctx); +await runMutation(internal.notifications.deliver, { conversationId }); +``` + +### TS2589 fallback: fixed typed ref when the generated ref itself is the problem + +If simply referencing `api.foo.bar` or `internal.foo.bar` still triggers `TS2589`, switch only that hot call site to a fixed typed ref: + +```ts +import { makeFunctionReference, type FunctionReference } from "convex/server"; + +type DeliverArgs = { conversationId: Id<"conversations"> }; +type DeliverResult = null; + +type ConvexRef< + Type extends "query" | "mutation" | "action", + Visibility extends "internal" | "public", + Args extends Record, + Return = unknown, +> = FunctionReference; + +const DELIVER_NOTIFICATION_REF = makeFunctionReference< + "mutation", + DeliverArgs, + DeliverResult +>("notifications:deliver") as unknown as ConvexRef< + "mutation", + "internal", + DeliverArgs, + DeliverResult +>; +``` + +Use this only after the generated ref path proved pathological. + +## Which approach to choose + +### Use generated `api` / `internal` refs when + +- the call is backend-to-backend +- the generated ref typechecks normally +- you are not in a known `TS2589` hotspot + +### Use fixed typed `makeFunctionReference(...)` constants when + +- you are in a surface adapter or feature-local wrapper file +- you need a stable local ref for a frontend wrapper +- a backend hotspot still blows up after trying generated refs + +### Use local wrapper hooks when + +- the consumer is React UI, runtime, controller, screen, or context code +- the feature needs gating or `skip` behavior +- you want to normalize the result shape once for multiple consumers + +### Use a shallow backend helper when + +- the problem is `ctx.runQuery` / `ctx.runMutation` / `ctx.runAction` / `runAfter` +- the ref type is okay, but the invocation is too deep + +### Use an adapter-local cast only when + +- Convex’s React hook typing still needs an exact tuple or helper shape +- the cast can stay in the adapter file and nowhere else + +## Current Surface Standards + +### Backend (`packages/convex`) + +- Default to generated refs. +- Localize `TS2589` workarounds with named `getShallowRun*` helpers. +- If needed, use fixed typed refs at the hotspot only. +- Keep guard coverage in `packages/convex/tests/runtimeTypeHardeningGuard.test.ts`. + +### Web (`apps/web`) + +- Feature/runtime code should not import `convex/react` directly. +- Use feature-local wrapper hooks and the web adapter in `apps/web/src/lib/convex/hooks.ts`. +- Keep refs at module scope. +- Guard coverage lives in `apps/web/src/app/typeHardeningGuard.test.ts`. + +### Widget (`apps/widget`) + +- Feature/runtime code should not import `convex/react` directly. +- Use the widget adapter in `apps/widget/src/lib/convex/hooks.ts`. +- The only remaining adapter escape hatch is the query-args tuple helper required by Convex’s hook typing. +- Guard coverage lives in `apps/widget/src/test/refHardeningGuard.test.ts`. + +### Mobile (`apps/mobile`) + +- Target the same pattern as web/widget: local wrapper hooks plus module-scope typed refs. +- Do not add new direct `convex/react` usage to screens, contexts, or controller-style hooks. +- If a local adapter/wrapper does not exist for the feature yet, create one instead of importing hooks directly into runtime UI. + +## Anti-Patterns To Avoid + +- `function getQueryRef(name: string) { ... }` +- `function getMutationRef(name: string) { ... }` +- `function getActionRef(name: string) { ... }` +- component-local `makeFunctionReference(...)` +- `as any` +- broad `unsafeApi` / `unsafeInternal` aliases +- scattering the same `as unknown as` across many call sites +- returning `unknown` or `string` when a branded `Id<"...">` or explicit object type is the real contract + +## Verification Checklist + +After changing Convex typing boundaries: + +1. Run the touched package typecheck first. +2. Run the touched package tests. +3. Run the relevant hardening guard tests. +4. If this work is OpenSpec-driven, run strict OpenSpec validation. + +Useful commands: + +```bash +pnpm --filter @opencom/convex typecheck +pnpm --filter @opencom/web typecheck +pnpm --filter @opencom/widget typecheck + +pnpm --filter @opencom/convex test -- --run tests/runtimeTypeHardeningGuard.test.ts +pnpm --filter @opencom/web test -- --run src/app/typeHardeningGuard.test.ts +pnpm --filter @opencom/widget test -- --run src/test/refHardeningGuard.test.ts +``` + +## Review Rule of Thumb + +If you are about to: + +- add a new direct `convex/react` import in feature code +- add a new `get*Ref(name: string)` factory +- add `unsafeApi`, `unsafeInternal`, `as any`, or repeated `as unknown as` +- create a ref inside a React component + +stop and use one of the standard patterns above instead. diff --git a/docs/runtime-type-hardening-2026-03-05.md b/docs/runtime-type-hardening-2026-03-05.md index f34dab7..76e5f01 100644 --- a/docs/runtime-type-hardening-2026-03-05.md +++ b/docs/runtime-type-hardening-2026-03-05.md @@ -1,5 +1,7 @@ # Runtime Type Hardening Notes (2026-03-05) +For current standards and new work, use `docs/convex-type-safety-playbook.md`. This file is historical context from an earlier hardening pass. + ## Scope This pass hardens runtime-critical Convex paths in: diff --git a/docs/refactor-assessment-evidence-2026-03-05.md b/openspec/changes/archive/refactor-assessment-evidence-2026-03-05.md similarity index 100% rename from docs/refactor-assessment-evidence-2026-03-05.md rename to openspec/changes/archive/refactor-assessment-evidence-2026-03-05.md diff --git a/docs/refactor-assessment-roadmap-2026-03-05.md b/openspec/changes/archive/refactor-assessment-roadmap-2026-03-05.md similarity index 100% rename from docs/refactor-assessment-roadmap-2026-03-05.md rename to openspec/changes/archive/refactor-assessment-roadmap-2026-03-05.md diff --git a/docs/refactor-assessment-web-widget-convex-2026-03-05.md b/openspec/changes/archive/refactor-assessment-web-widget-convex-2026-03-05.md similarity index 100% rename from docs/refactor-assessment-web-widget-convex-2026-03-05.md rename to openspec/changes/archive/refactor-assessment-web-widget-convex-2026-03-05.md diff --git a/docs/refactor-handoff-web-widget-convex-2026-03-06.md b/openspec/changes/archive/refactor-handoff-web-widget-convex-2026-03-06.md similarity index 100% rename from docs/refactor-handoff-web-widget-convex-2026-03-06.md rename to openspec/changes/archive/refactor-handoff-web-widget-convex-2026-03-06.md diff --git a/docs/refactor-opportunities-ranking-2026-03-03.md b/openspec/changes/archive/refactor-opportunities-ranking-2026-03-03.md similarity index 100% rename from docs/refactor-opportunities-ranking-2026-03-03.md rename to openspec/changes/archive/refactor-opportunities-ranking-2026-03-03.md diff --git a/docs/refactor-opportunity-audit-2026-03-06.md b/openspec/changes/archive/refactor-opportunity-audit-2026-03-06.md similarity index 100% rename from docs/refactor-opportunity-audit-2026-03-06.md rename to openspec/changes/archive/refactor-opportunity-audit-2026-03-06.md diff --git a/docs/refactor-progress-audience-rule-contract-alignment-2026-03-05.md b/openspec/changes/archive/refactor-progress-audience-rule-contract-alignment-2026-03-05.md similarity index 100% rename from docs/refactor-progress-audience-rule-contract-alignment-2026-03-05.md rename to openspec/changes/archive/refactor-progress-audience-rule-contract-alignment-2026-03-05.md diff --git a/docs/refactor-progress-centralize-outbound-trigger-contracts-2026-03-05.md b/openspec/changes/archive/refactor-progress-centralize-outbound-trigger-contracts-2026-03-05.md similarity index 100% rename from docs/refactor-progress-centralize-outbound-trigger-contracts-2026-03-05.md rename to openspec/changes/archive/refactor-progress-centralize-outbound-trigger-contracts-2026-03-05.md diff --git a/docs/refactor-progress-centralize-outbound-trigger-contracts-2026-03-06.md b/openspec/changes/archive/refactor-progress-centralize-outbound-trigger-contracts-2026-03-06.md similarity index 100% rename from docs/refactor-progress-centralize-outbound-trigger-contracts-2026-03-06.md rename to openspec/changes/archive/refactor-progress-centralize-outbound-trigger-contracts-2026-03-06.md diff --git a/docs/refactor-progress-convex-auth-wrapper-adoption-2026-03-05.md b/openspec/changes/archive/refactor-progress-convex-auth-wrapper-adoption-2026-03-05.md similarity index 100% rename from docs/refactor-progress-convex-auth-wrapper-adoption-2026-03-05.md rename to openspec/changes/archive/refactor-progress-convex-auth-wrapper-adoption-2026-03-05.md diff --git a/docs/refactor-progress-convex-campaign-delivery-decomposition-2026-03-05.md b/openspec/changes/archive/refactor-progress-convex-campaign-delivery-decomposition-2026-03-05.md similarity index 100% rename from docs/refactor-progress-convex-campaign-delivery-decomposition-2026-03-05.md rename to openspec/changes/archive/refactor-progress-convex-campaign-delivery-decomposition-2026-03-05.md diff --git a/docs/refactor-progress-convex-messenger-settings-decomposition-2026-03-07.md b/openspec/changes/archive/refactor-progress-convex-messenger-settings-decomposition-2026-03-07.md similarity index 100% rename from docs/refactor-progress-convex-messenger-settings-decomposition-2026-03-07.md rename to openspec/changes/archive/refactor-progress-convex-messenger-settings-decomposition-2026-03-07.md diff --git a/docs/refactor-progress-convex-reporting-decomposition-2026-03-05.md b/openspec/changes/archive/refactor-progress-convex-reporting-decomposition-2026-03-05.md similarity index 100% rename from docs/refactor-progress-convex-reporting-decomposition-2026-03-05.md rename to openspec/changes/archive/refactor-progress-convex-reporting-decomposition-2026-03-05.md diff --git a/docs/refactor-progress-convex-schema-fragments-2026-03-05.md b/openspec/changes/archive/refactor-progress-convex-schema-fragments-2026-03-05.md similarity index 100% rename from docs/refactor-progress-convex-schema-fragments-2026-03-05.md rename to openspec/changes/archive/refactor-progress-convex-schema-fragments-2026-03-05.md diff --git a/docs/refactor-progress-convex-schema-high-concentration-tables-2026-03-06.md b/openspec/changes/archive/refactor-progress-convex-schema-high-concentration-tables-2026-03-06.md similarity index 100% rename from docs/refactor-progress-convex-schema-high-concentration-tables-2026-03-06.md rename to openspec/changes/archive/refactor-progress-convex-schema-high-concentration-tables-2026-03-06.md diff --git a/docs/refactor-progress-convex-series-runtime-authoring-v2-2026-03-07.md b/openspec/changes/archive/refactor-progress-convex-series-runtime-authoring-v2-2026-03-07.md similarity index 100% rename from docs/refactor-progress-convex-series-runtime-authoring-v2-2026-03-07.md rename to openspec/changes/archive/refactor-progress-convex-series-runtime-authoring-v2-2026-03-07.md diff --git a/docs/refactor-progress-convex-tour-progress-decomposition-2026-03-07.md b/openspec/changes/archive/refactor-progress-convex-tour-progress-decomposition-2026-03-07.md similarity index 100% rename from docs/refactor-progress-convex-tour-progress-decomposition-2026-03-07.md rename to openspec/changes/archive/refactor-progress-convex-tour-progress-decomposition-2026-03-07.md diff --git a/docs/refactor-progress-convex-visitors-decomposition-2026-03-05.md b/openspec/changes/archive/refactor-progress-convex-visitors-decomposition-2026-03-05.md similarity index 100% rename from docs/refactor-progress-convex-visitors-decomposition-2026-03-05.md rename to openspec/changes/archive/refactor-progress-convex-visitors-decomposition-2026-03-05.md diff --git a/docs/refactor-progress-cross-surface-compatibility-gates-2026-03-05.md b/openspec/changes/archive/refactor-progress-cross-surface-compatibility-gates-2026-03-05.md similarity index 100% rename from docs/refactor-progress-cross-surface-compatibility-gates-2026-03-05.md rename to openspec/changes/archive/refactor-progress-cross-surface-compatibility-gates-2026-03-05.md diff --git a/docs/refactor-progress-home-config-shared-contracts-2026-03-05.md b/openspec/changes/archive/refactor-progress-home-config-shared-contracts-2026-03-05.md similarity index 100% rename from docs/refactor-progress-home-config-shared-contracts-2026-03-05.md rename to openspec/changes/archive/refactor-progress-home-config-shared-contracts-2026-03-05.md diff --git a/docs/refactor-progress-messenger-home-contract-convergence-2026-03-07.md b/openspec/changes/archive/refactor-progress-messenger-home-contract-convergence-2026-03-07.md similarity index 100% rename from docs/refactor-progress-messenger-home-contract-convergence-2026-03-07.md rename to openspec/changes/archive/refactor-progress-messenger-home-contract-convergence-2026-03-07.md diff --git a/docs/refactor-progress-tour-runtime-shared-route-matching-2026-03-06.md b/openspec/changes/archive/refactor-progress-tour-runtime-shared-route-matching-2026-03-06.md similarity index 100% rename from docs/refactor-progress-tour-runtime-shared-route-matching-2026-03-06.md rename to openspec/changes/archive/refactor-progress-tour-runtime-shared-route-matching-2026-03-06.md diff --git a/docs/refactor-progress-visitor-readable-id-shared-2026-03-05.md b/openspec/changes/archive/refactor-progress-visitor-readable-id-shared-2026-03-05.md similarity index 100% rename from docs/refactor-progress-visitor-readable-id-shared-2026-03-05.md rename to openspec/changes/archive/refactor-progress-visitor-readable-id-shared-2026-03-05.md diff --git a/docs/refactor-progress-web-articles-admin-decomposition-2026-03-05.md b/openspec/changes/archive/refactor-progress-web-articles-admin-decomposition-2026-03-05.md similarity index 100% rename from docs/refactor-progress-web-articles-admin-decomposition-2026-03-05.md rename to openspec/changes/archive/refactor-progress-web-articles-admin-decomposition-2026-03-05.md diff --git a/docs/refactor-progress-web-e2e-stabilization-2026-03-06.md b/openspec/changes/archive/refactor-progress-web-e2e-stabilization-2026-03-06.md similarity index 100% rename from docs/refactor-progress-web-e2e-stabilization-2026-03-06.md rename to openspec/changes/archive/refactor-progress-web-e2e-stabilization-2026-03-06.md diff --git a/docs/refactor-progress-web-inbox-render-sections-2026-03-05.md b/openspec/changes/archive/refactor-progress-web-inbox-render-sections-2026-03-05.md similarity index 100% rename from docs/refactor-progress-web-inbox-render-sections-2026-03-05.md rename to openspec/changes/archive/refactor-progress-web-inbox-render-sections-2026-03-05.md diff --git a/docs/refactor-progress-web-messenger-settings-decomposition-2026-03-07.md b/openspec/changes/archive/refactor-progress-web-messenger-settings-decomposition-2026-03-07.md similarity index 100% rename from docs/refactor-progress-web-messenger-settings-decomposition-2026-03-07.md rename to openspec/changes/archive/refactor-progress-web-messenger-settings-decomposition-2026-03-07.md diff --git a/docs/refactor-progress-web-outbound-editor-decomposition-2026-03-05.md b/openspec/changes/archive/refactor-progress-web-outbound-editor-decomposition-2026-03-05.md similarity index 100% rename from docs/refactor-progress-web-outbound-editor-decomposition-2026-03-05.md rename to openspec/changes/archive/refactor-progress-web-outbound-editor-decomposition-2026-03-05.md diff --git a/docs/refactor-progress-web-outbound-editor-decomposition-2026-03-06.md b/openspec/changes/archive/refactor-progress-web-outbound-editor-decomposition-2026-03-06.md similarity index 100% rename from docs/refactor-progress-web-outbound-editor-decomposition-2026-03-06.md rename to openspec/changes/archive/refactor-progress-web-outbound-editor-decomposition-2026-03-06.md diff --git a/docs/refactor-progress-web-series-editor-decomposition-2026-03-05.md b/openspec/changes/archive/refactor-progress-web-series-editor-decomposition-2026-03-05.md similarity index 100% rename from docs/refactor-progress-web-series-editor-decomposition-2026-03-05.md rename to openspec/changes/archive/refactor-progress-web-series-editor-decomposition-2026-03-05.md diff --git a/docs/refactor-progress-web-settings-domain-decomposition-2026-03-05.md b/openspec/changes/archive/refactor-progress-web-settings-domain-decomposition-2026-03-05.md similarity index 100% rename from docs/refactor-progress-web-settings-domain-decomposition-2026-03-05.md rename to openspec/changes/archive/refactor-progress-web-settings-domain-decomposition-2026-03-05.md diff --git a/docs/refactor-progress-web-survey-editor-decomposition-2026-03-05.md b/openspec/changes/archive/refactor-progress-web-survey-editor-decomposition-2026-03-05.md similarity index 100% rename from docs/refactor-progress-web-survey-editor-decomposition-2026-03-05.md rename to openspec/changes/archive/refactor-progress-web-survey-editor-decomposition-2026-03-05.md diff --git a/docs/refactor-progress-web-tour-editor-decomposition-2026-03-06.md b/openspec/changes/archive/refactor-progress-web-tour-editor-decomposition-2026-03-06.md similarity index 100% rename from docs/refactor-progress-web-tour-editor-decomposition-2026-03-06.md rename to openspec/changes/archive/refactor-progress-web-tour-editor-decomposition-2026-03-06.md diff --git a/docs/refactor-progress-widget-conversation-view-decomposition-2026-03-05.md b/openspec/changes/archive/refactor-progress-widget-conversation-view-decomposition-2026-03-05.md similarity index 100% rename from docs/refactor-progress-widget-conversation-view-decomposition-2026-03-05.md rename to openspec/changes/archive/refactor-progress-widget-conversation-view-decomposition-2026-03-05.md diff --git a/docs/refactor-progress-widget-shell-controller-decomposition-2026-03-05.md b/openspec/changes/archive/refactor-progress-widget-shell-controller-decomposition-2026-03-05.md similarity index 100% rename from docs/refactor-progress-widget-shell-controller-decomposition-2026-03-05.md rename to openspec/changes/archive/refactor-progress-widget-shell-controller-decomposition-2026-03-05.md diff --git a/docs/refactor-progress-widget-shell-orchestration-v2-2026-03-06.md b/openspec/changes/archive/refactor-progress-widget-shell-orchestration-v2-2026-03-06.md similarity index 100% rename from docs/refactor-progress-widget-shell-orchestration-v2-2026-03-06.md rename to openspec/changes/archive/refactor-progress-widget-shell-orchestration-v2-2026-03-06.md diff --git a/docs/refactor-progress-widget-survey-overlay-decomposition-2026-03-05.md b/openspec/changes/archive/refactor-progress-widget-survey-overlay-decomposition-2026-03-05.md similarity index 100% rename from docs/refactor-progress-widget-survey-overlay-decomposition-2026-03-05.md rename to openspec/changes/archive/refactor-progress-widget-survey-overlay-decomposition-2026-03-05.md diff --git a/docs/refactor-progress-widget-tour-overlay-decomposition-2026-03-05.md b/openspec/changes/archive/refactor-progress-widget-tour-overlay-decomposition-2026-03-05.md similarity index 100% rename from docs/refactor-progress-widget-tour-overlay-decomposition-2026-03-05.md rename to openspec/changes/archive/refactor-progress-widget-tour-overlay-decomposition-2026-03-05.md diff --git a/docs/refactor-progress-workspace-admin-security-onboarding-v1-2026-03-07.md b/openspec/changes/archive/refactor-progress-workspace-admin-security-onboarding-v1-2026-03-07.md similarity index 100% rename from docs/refactor-progress-workspace-admin-security-onboarding-v1-2026-03-07.md rename to openspec/changes/archive/refactor-progress-workspace-admin-security-onboarding-v1-2026-03-07.md diff --git a/docs/refactor-remaining-map-2026-03-05.md b/openspec/changes/archive/refactor-remaining-map-2026-03-05.md similarity index 100% rename from docs/refactor-remaining-map-2026-03-05.md rename to openspec/changes/archive/refactor-remaining-map-2026-03-05.md diff --git a/docs/refactor-remaining-slices-pass2-2026-03-05.md b/openspec/changes/archive/refactor-remaining-slices-pass2-2026-03-05.md similarity index 100% rename from docs/refactor-remaining-slices-pass2-2026-03-05.md rename to openspec/changes/archive/refactor-remaining-slices-pass2-2026-03-05.md From 3b26857ba1cfa0d38e3c7627fdfb0e8a7e75fd01 Mon Sep 17 00:00:00 2001 From: Jack D Date: Wed, 11 Mar 2026 14:28:58 +0000 Subject: [PATCH 2/2] move last doc --- docs/convex-type-safety-playbook.md | 2 +- .../changes/archive}/runtime-type-hardening-2026-03-05.md | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {docs => openspec/changes/archive}/runtime-type-hardening-2026-03-05.md (100%) diff --git a/docs/convex-type-safety-playbook.md b/docs/convex-type-safety-playbook.md index 3b4198d..a8f99f5 100644 --- a/docs/convex-type-safety-playbook.md +++ b/docs/convex-type-safety-playbook.md @@ -10,7 +10,7 @@ Use it when you are: - hitting `TS2589` (`Type instantiation is excessively deep and possibly infinite`) - deciding where a cast or `makeFunctionReference(...)` is acceptable -Historical hardening notes still exist in `docs/refactor-*` and `docs/runtime-type-hardening-2026-03-05.md`, but this file is the current source of truth for new code. +Historical hardening notes still exist in `openspec/archive/refactor-*` and `runtime-type-hardening-2026-03-05.md`, but this file is the current source of truth for new code. ## Goals diff --git a/docs/runtime-type-hardening-2026-03-05.md b/openspec/changes/archive/runtime-type-hardening-2026-03-05.md similarity index 100% rename from docs/runtime-type-hardening-2026-03-05.md rename to openspec/changes/archive/runtime-type-hardening-2026-03-05.md