diff --git a/.claude/hooks/session-start.sh b/.claude/hooks/session-start.sh index eb01bb594f..53abbcb719 100755 --- a/.claude/hooks/session-start.sh +++ b/.claude/hooks/session-start.sh @@ -1,8 +1,8 @@ #!/bin/bash # SessionStart hook for Claude Code on the web. -# The app is engine-strict on Node >=24.15 <25 / npm 11.x, but web containers +# The app is engine-strict on Node >=26.0.0 <27 / npm 11.x, but web containers # ship an older Node on PATH, so nothing installs or runs until a Node meeting -# that floor is present. Installs one into $HOME/.node24 (cached with the +# that floor is present. Installs one into $HOME/.node26 (cached with the # container), exposes it via $CLAUDE_ENV_FILE, and installs npm dependencies. set -euo pipefail @@ -12,17 +12,16 @@ fi payload="$(cat 2>/dev/null || true)" -NODE_VERSION="24.19.0" +NODE_VERSION="26.8.1" # Keep in step with the floor in package.json engines.node. A matching major is -# not enough: dev dependencies (jsdom) carry a minor-level floor, so a 24.13 on -# PATH satisfied the old major-only check and then failed `npm ci` with -# EBADENGINE. That blocked PRs #1611, #1697, #1705 and #1740. -NODE_MINIMUM="24.15.0" -# Exclusive major ceiling, matching the "<25" half of engines.node. Checking only -# the floor would let a container shipping Node 25+ skip provisioning and then +# not enough: keep the complete range check so a future minor-level floor cannot +# let an older Node 26 runtime reach `npm ci` and fail with an opaque EBADENGINE. +NODE_MINIMUM="26.0.0" +# Exclusive major ceiling, matching the "<27" half of engines.node. Checking only +# the floor would let a container shipping Node 27+ skip provisioning and then # fail `npm ci`, which is the same blind-spot as the major-only check above. -NODE_MAJOR_CEILING="25" -NODE_HOME="$HOME/.node24" +NODE_MAJOR_CEILING="27" +NODE_HOME="$HOME/.node26" NODE_BIN="$NODE_HOME/node-v${NODE_VERSION}-linux-x64/bin" supported_runtime() { diff --git a/.claude/skills/issues/SKILL.md b/.claude/skills/issues/SKILL.md index f088ee03bc..53cb6548a0 100644 --- a/.claude/skills/issues/SKILL.md +++ b/.claude/skills/issues/SKILL.md @@ -36,7 +36,7 @@ the report; record that fetch separately because the report itself performs no p If a filter is given, filter the open items before rendering steps 2–3, then show only matching queued tasks and matching non-queued items: `/issues P1` (by priority), `/issues issues` / -`/issues recs` / `/issues tasks` (by type), `/issues ` (summary/detail substring match). +`/issues recs` / `/issues tasks` (by type), `/issues ward` (Ward Flow tasks only via `npm run issues:report -- --ward --json`), `/issues core` (repository/platform tasks excluding Ward Flow via `npm run issues:report -- --core --json`), `/issues ` (summary/detail substring match via `npm run issues:report -- --filter --json`). `/issues wins` or `/issues agent-safe` runs `npm run issues:report -- --agent-safe-wins --json`. The classifier includes only queued work diff --git a/.claude/skills/run-pr/SKILL.md b/.claude/skills/run-pr/SKILL.md index f2cd72a13d..bef7ab4e8c 100644 --- a/.claude/skills/run-pr/SKILL.md +++ b/.claude/skills/run-pr/SKILL.md @@ -44,7 +44,7 @@ Never, even during a sweep: 2. `mcp__github__get_me` to confirm identity and access, then `git fetch origin --prune`. 3. Record the current branch/ref so it can be restored at sweep end. Require a clean `git status`; if the worktree is dirty, do not stash or discard — either restrict the sweep to - PRs whose fixes do not need this checkout, or create a separate `git worktree add` (Node 24, + PRs whose fixes do not need this checkout, or create a separate `git worktree add` (Node 26, `npm ci`) and report which was chosen. 4. `mcp__github__list_pull_requests` with `state=open`, paginated in small batches. Build the work queue in ascending PR number. diff --git a/.design-sync/config.json b/.design-sync/config.json index 790764d543..6a905c997a 100644 --- a/.design-sync/config.json +++ b/.design-sync/config.json @@ -20,7 +20,7 @@ ], "dtsPropsFor": { "AccessibleTable": "actionsHeader?: string; caption: string; clinicalOnly?: boolean; columnAlign?: AccessibleTableColumnAlign[]; columns?: string[] | null; compact?: boolean; densePreview?: boolean; dialogTitle?: string | null; expandOnMobile?: boolean; hidePreviewCaption?: boolean; hidePreviewRowCount?: boolean; lowConfidenceFallback?: ReactNode; markdown?: string | null; normalizedTable?: NormalizedAccessibleTable | null; numericColumns?: number[]; previewRows?: number; rowActions?: ReactNode[]; rows?: string[][] | null;", - "AnswerCard": "actions?: AnswerCardAction[]; children: ReactNode; className?: string; frame?: \"raised\" | \"bare\"; onOpenSource?: ((sourceId: string, locator?: string) => void) | ((sourceId: string, locator?: string) => void); provenance?: AnswerFooterProps; query?: string; state: { kind: \"ready\"; sourceCount: number; } | DegradedAnswerState; support: AnswerSupportStrength; verification: VerificationNoticeProps;", + "AnswerCard": "actions?: AnswerCardAction[]; children: ReactNode; className?: string; frame?: \"raised\" | \"bare\"; metaChips?: ReactNode; metaDetail?: ReactNode; onOpenSource?: ((sourceId: string, locator?: string) => void) | ((sourceId: string, locator?: string) => void); provenance?: AnswerFooterProps; query?: string; retrievalStatePlacement?: \"header\" | \"content\"; state: { kind: \"ready\"; sourceCount: number; } | DegradedAnswerState; support: AnswerSupportStrength; verification: VerificationNoticeProps; verificationPlacement?: \"header\" | \"content\";", "AnswerFooter": "className?: string; generatedAt?: string | null; metadata?: SourceMetadataInput; provenance?: string | SourceMetadataInput; publisher?: string | null; reviewDate?: string | null; version?: string | null;", "AsyncButton": "about?: string; accessKey?: string; \"aria-activedescendant\"?: string; \"aria-atomic\"?: (boolean | \"true\" | \"false\"); \"aria-autocomplete\"?: \"none\" | \"list\" | \"inline\" | \"both\"; \"aria-braillelabel\"?: string; \"aria-brailleroledescription\"?: string; \"aria-busy\"?: (boolean | \"true\" | \"false\"); \"aria-checked\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-colcount\"?: number; \"aria-colindex\"?: number; \"aria-colindextext\"?: string; \"aria-colspan\"?: number; \"aria-controls\"?: string; \"aria-current\"?: boolean | \"true\" | \"false\" | \"page\" | \"step\" | \"location\" | \"date\" | \"time\"; \"aria-describedby\"?: string; \"aria-description\"?: string; \"aria-details\"?: string; \"aria-disabled\"?: (boolean | \"true\" | \"false\"); \"aria-dropeffect\"?: \"none\" | \"link\" | \"copy\" | \"execute\" | \"move\" | \"popup\"; \"aria-errormessage\"?: string; \"aria-expanded\"?: (boolean | \"true\" | \"false\"); \"aria-flowto\"?: string; \"aria-grabbed\"?: (boolean | \"true\" | \"false\"); \"aria-haspopup\"?: boolean | \"true\" | \"false\" | \"dialog\" | \"grid\" | \"listbox\" | \"menu\" | \"tree\"; \"aria-hidden\"?: (boolean | \"true\" | \"false\"); \"aria-invalid\"?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\"; \"aria-keyshortcuts\"?: string; \"aria-label\"?: string; \"aria-labelledby\"?: string; \"aria-level\"?: number; \"aria-live\"?: \"off\" | \"assertive\" | \"polite\"; \"aria-modal\"?: (boolean | \"true\" | \"false\"); \"aria-multiline\"?: (boolean | \"true\" | \"false\"); \"aria-multiselectable\"?: (boolean | \"true\" | \"false\"); \"aria-orientation\"?: \"horizontal\" | \"vertical\"; \"aria-owns\"?: string; \"aria-placeholder\"?: string; \"aria-posinset\"?: number; \"aria-pressed\"?: boolean | \"true\" | \"false\" | \"mixed\"; \"aria-readonly\"?: (boolean | \"true\" | \"false\"); \"aria-relevant\"?: \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"all\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\"; \"aria-required\"?: (boolean | \"true\" | \"false\"); \"aria-roledescription\"?: string; \"aria-rowcount\"?: number; \"aria-rowindex\"?: number; \"aria-rowindextext\"?: string; \"aria-rowspan\"?: number; \"aria-selected\"?: (boolean | \"true\" | \"false\"); \"aria-setsize\"?: number; \"aria-sort\"?: \"none\" | \"ascending\" | \"descending\" | \"other\"; \"aria-valuemax\"?: number; \"aria-valuemin\"?: number; \"aria-valuenow\"?: number; \"aria-valuetext\"?: string; autoCapitalize?: \"off\" | \"none\" | \"on\" | \"sentences\" | \"words\" | \"characters\" | (string & {}); autoCorrect?: string; autoFocus?: boolean; autoSave?: string; busy: boolean; busyLabel: string; children: ReactNode; className?: string; color?: string; content?: string; contentEditable?: (boolean | \"true\" | \"false\") | \"inherit\" | \"plaintext-only\"; contextMenu?: string; dangerouslySetInnerHTML?: { __html: string | TrustedHTML; }; datatype?: string; defaultChecked?: boolean; defaultValue?: string | number | readonly string[]; dir?: string; disabled?: boolean; draggable?: (boolean | \"true\" | \"false\"); enterKeyHint?: \"enter\" | \"done\" | \"go\" | \"next\" | \"previous\" | \"search\" | \"send\"; exportparts?: string; form?: string; formAction?: string | ((formData: FormData) => void | Promise); formEncType?: string; formMethod?: string; formNoValidate?: boolean; formTarget?: string; hidden?: boolean; id?: string; idleIcon?: ReactNode; inert?: boolean; inlist?: any; inputMode?: \"none\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"email\" | \"numeric\" | \"decimal\"; is?: string; itemID?: string; itemProp?: string; itemRef?: string; itemScope?: boolean; itemType?: string; lang?: string; name?: string; nonce?: string; onAbort?: import(\"react\").ReactEventHandler; onAbortCapture?: import(\"react\").ReactEventHandler; onAnimationEnd?: import(\"react\").AnimationEventHandler; onAnimationEndCapture?: import(\"react\").AnimationEventHandler; onAnimationIteration?: import(\"react\").AnimationEventHandler; onAnimationIterationCapture?: import(\"react\").AnimationEventHandler; onAnimationStart?: import(\"react\").AnimationEventHandler; onAnimationStartCapture?: import(\"react\").AnimationEventHandler; onAuxClick?: import(\"react\").MouseEventHandler; onAuxClickCapture?: import(\"react\").MouseEventHandler; onBeforeInput?: import(\"react\").InputEventHandler; onBeforeInputCapture?: import(\"react\").InputEventHandler; onBeforeToggle?: import(\"react\").ToggleEventHandler; onBlur?: import(\"react\").FocusEventHandler; onBlurCapture?: import(\"react\").FocusEventHandler; onCanPlay?: import(\"react\").ReactEventHandler; onCanPlayCapture?: import(\"react\").ReactEventHandler; onCanPlayThrough?: import(\"react\").ReactEventHandler; onCanPlayThroughCapture?: import(\"react\").ReactEventHandler; onChange?: import(\"react\").ChangeEventHandler; onChangeCapture?: import(\"react\").ChangeEventHandler; onClick?: import(\"react\").MouseEventHandler; onClickCapture?: import(\"react\").MouseEventHandler; onCompositionEnd?: import(\"react\").CompositionEventHandler; onCompositionEndCapture?: import(\"react\").CompositionEventHandler; onCompositionStart?: import(\"react\").CompositionEventHandler; onCompositionStartCapture?: import(\"react\").CompositionEventHandler; onCompositionUpdate?: import(\"react\").CompositionEventHandler; onCompositionUpdateCapture?: import(\"react\").CompositionEventHandler; onContextMenu?: import(\"react\").MouseEventHandler; onContextMenuCapture?: import(\"react\").MouseEventHandler; onCopy?: import(\"react\").ClipboardEventHandler; onCopyCapture?: import(\"react\").ClipboardEventHandler; onCut?: import(\"react\").ClipboardEventHandler; onCutCapture?: import(\"react\").ClipboardEventHandler; onDoubleClick?: import(\"react\").MouseEventHandler; onDoubleClickCapture?: import(\"react\").MouseEventHandler; onDrag?: import(\"react\").DragEventHandler; onDragCapture?: import(\"react\").DragEventHandler; onDragEnd?: import(\"react\").DragEventHandler; onDragEndCapture?: import(\"react\").DragEventHandler; onDragEnter?: import(\"react\").DragEventHandler; onDragEnterCapture?: import(\"react\").DragEventHandler; onDragExit?: import(\"react\").DragEventHandler; onDragExitCapture?: import(\"react\").DragEventHandler; onDragLeave?: import(\"react\").DragEventHandler; onDragLeaveCapture?: import(\"react\").DragEventHandler; onDragOver?: import(\"react\").DragEventHandler; onDragOverCapture?: import(\"react\").DragEventHandler; onDragStart?: import(\"react\").DragEventHandler; onDragStartCapture?: import(\"react\").DragEventHandler; onDrop?: import(\"react\").DragEventHandler; onDropCapture?: import(\"react\").DragEventHandler; onDurationChange?: import(\"react\").ReactEventHandler; onDurationChangeCapture?: import(\"react\").ReactEventHandler; onEmptied?: import(\"react\").ReactEventHandler; onEmptiedCapture?: import(\"react\").ReactEventHandler; onEncrypted?: import(\"react\").ReactEventHandler; onEncryptedCapture?: import(\"react\").ReactEventHandler; onEnded?: import(\"react\").ReactEventHandler; onEndedCapture?: import(\"react\").ReactEventHandler; onError?: import(\"react\").ReactEventHandler; onErrorCapture?: import(\"react\").ReactEventHandler; onFocus?: import(\"react\").FocusEventHandler; onFocusCapture?: import(\"react\").FocusEventHandler; onGotPointerCapture?: import(\"react\").PointerEventHandler; onGotPointerCaptureCapture?: import(\"react\").PointerEventHandler; onInput?: import(\"react\").InputEventHandler; onInputCapture?: import(\"react\").InputEventHandler; onInvalid?: import(\"react\").ReactEventHandler; onInvalidCapture?: import(\"react\").ReactEventHandler; onKeyDown?: import(\"react\").KeyboardEventHandler; onKeyDownCapture?: import(\"react\").KeyboardEventHandler; onKeyPress?: import(\"react\").KeyboardEventHandler; onKeyPressCapture?: import(\"react\").KeyboardEventHandler; onKeyUp?: import(\"react\").KeyboardEventHandler; onKeyUpCapture?: import(\"react\").KeyboardEventHandler; onLoad?: import(\"react\").ReactEventHandler; onLoadCapture?: import(\"react\").ReactEventHandler; onLoadedData?: import(\"react\").ReactEventHandler; onLoadedDataCapture?: import(\"react\").ReactEventHandler; onLoadedMetadata?: import(\"react\").ReactEventHandler; onLoadedMetadataCapture?: import(\"react\").ReactEventHandler; onLoadStart?: import(\"react\").ReactEventHandler; onLoadStartCapture?: import(\"react\").ReactEventHandler; onLostPointerCapture?: import(\"react\").PointerEventHandler; onLostPointerCaptureCapture?: import(\"react\").PointerEventHandler; onMouseDown?: import(\"react\").MouseEventHandler; onMouseDownCapture?: import(\"react\").MouseEventHandler; onMouseEnter?: import(\"react\").MouseEventHandler; onMouseLeave?: import(\"react\").MouseEventHandler; onMouseMove?: import(\"react\").MouseEventHandler; onMouseMoveCapture?: import(\"react\").MouseEventHandler; onMouseOut?: import(\"react\").MouseEventHandler; onMouseOutCapture?: import(\"react\").MouseEventHandler; onMouseOver?: import(\"react\").MouseEventHandler; onMouseOverCapture?: import(\"react\").MouseEventHandler; onMouseUp?: import(\"react\").MouseEventHandler; onMouseUpCapture?: import(\"react\").MouseEventHandler; onPaste?: import(\"react\").ClipboardEventHandler; onPasteCapture?: import(\"react\").ClipboardEventHandler; onPause?: import(\"react\").ReactEventHandler; onPauseCapture?: import(\"react\").ReactEventHandler; onPlay?: import(\"react\").ReactEventHandler; onPlayCapture?: import(\"react\").ReactEventHandler; onPlaying?: import(\"react\").ReactEventHandler; onPlayingCapture?: import(\"react\").ReactEventHandler; onPointerCancel?: import(\"react\").PointerEventHandler; onPointerCancelCapture?: import(\"react\").PointerEventHandler; onPointerDown?: import(\"react\").PointerEventHandler; onPointerDownCapture?: import(\"react\").PointerEventHandler; onPointerEnter?: import(\"react\").PointerEventHandler; onPointerLeave?: import(\"react\").PointerEventHandler; onPointerMove?: import(\"react\").PointerEventHandler; onPointerMoveCapture?: import(\"react\").PointerEventHandler; onPointerOut?: import(\"react\").PointerEventHandler; onPointerOutCapture?: import(\"react\").PointerEventHandler; onPointerOver?: import(\"react\").PointerEventHandler; onPointerOverCapture?: import(\"react\").PointerEventHandler; onPointerUp?: import(\"react\").PointerEventHandler; onPointerUpCapture?: import(\"react\").PointerEventHandler; onProgress?: import(\"react\").ReactEventHandler; onProgressCapture?: import(\"react\").ReactEventHandler; onRateChange?: import(\"react\").ReactEventHandler; onRateChangeCapture?: import(\"react\").ReactEventHandler; onReset?: import(\"react\").ReactEventHandler; onResetCapture?: import(\"react\").ReactEventHandler; onScroll?: import(\"react\").UIEventHandler; onScrollCapture?: import(\"react\").UIEventHandler; onScrollEnd?: import(\"react\").UIEventHandler; onScrollEndCapture?: import(\"react\").UIEventHandler; onSeeked?: import(\"react\").ReactEventHandler; onSeekedCapture?: import(\"react\").ReactEventHandler; onSeeking?: import(\"react\").ReactEventHandler; onSeekingCapture?: import(\"react\").ReactEventHandler; onSelect?: import(\"react\").ReactEventHandler; onSelectCapture?: import(\"react\").ReactEventHandler; onStalled?: import(\"react\").ReactEventHandler; onStalledCapture?: import(\"react\").ReactEventHandler; onSubmit?: import(\"react\").SubmitEventHandler; onSubmitCapture?: import(\"react\").SubmitEventHandler; onSuspend?: import(\"react\").ReactEventHandler; onSuspendCapture?: import(\"react\").ReactEventHandler; onTimeUpdate?: import(\"react\").ReactEventHandler; onTimeUpdateCapture?: import(\"react\").ReactEventHandler; onToggle?: import(\"react\").ToggleEventHandler; onTouchCancel?: import(\"react\").TouchEventHandler; onTouchCancelCapture?: import(\"react\").TouchEventHandler; onTouchEnd?: import(\"react\").TouchEventHandler; onTouchEndCapture?: import(\"react\").TouchEventHandler; onTouchMove?: import(\"react\").TouchEventHandler; onTouchMoveCapture?: import(\"react\").TouchEventHandler; onTouchStart?: import(\"react\").TouchEventHandler; onTouchStartCapture?: import(\"react\").TouchEventHandler; onTransitionCancel?: import(\"react\").TransitionEventHandler; onTransitionCancelCapture?: import(\"react\").TransitionEventHandler; onTransitionEnd?: import(\"react\").TransitionEventHandler; onTransitionEndCapture?: import(\"react\").TransitionEventHandler; onTransitionRun?: import(\"react\").TransitionEventHandler; onTransitionRunCapture?: import(\"react\").TransitionEventHandler; onTransitionStart?: import(\"react\").TransitionEventHandler; onTransitionStartCapture?: import(\"react\").TransitionEventHandler; onVolumeChange?: import(\"react\").ReactEventHandler; onVolumeChangeCapture?: import(\"react\").ReactEventHandler; onWaiting?: import(\"react\").ReactEventHandler; onWaitingCapture?: import(\"react\").ReactEventHandler; onWheel?: import(\"react\").WheelEventHandler; onWheelCapture?: import(\"react\").WheelEventHandler; part?: string; popover?: \"\" | \"auto\" | \"manual\" | \"hint\"; popoverTarget?: string; popoverTargetAction?: \"toggle\" | \"show\" | \"hide\"; prefix?: string; property?: string; radioGroup?: string; rel?: string; resource?: string; results?: number; rev?: string; role?: import(\"react\").AriaRole; security?: string; slot?: string; spellCheck?: (boolean | \"true\" | \"false\"); style?: import(\"react\").CSSProperties; suppressContentEditableWarning?: boolean; suppressHydrationWarning?: boolean; tabIndex?: number; title?: string; translate?: \"yes\" | \"no\"; type?: \"submit\" | \"reset\" | \"button\"; typeof?: string; unselectable?: \"off\" | \"on\"; value?: string | number | readonly string[]; vocab?: string;", "Breadcrumb": "className?: string; items: Crumb[];", diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 79e32aa769..cc85e8426d 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -64,7 +64,7 @@ jobs: fetch-depth: 1 - name: Run Claude Code if: ${{ env.CLAUDE_CODE_OAUTH_TOKEN != '' }} - uses: anthropics/claude-code-action@dcb57747bfceeaa1fa72638cae52295d1d853d4a # v1.0.199 + uses: anthropics/claude-code-action@a874e9ecd7bb36efdad65429c6b35815f5a08f10 # v1.0.210 with: # The action's default trigger phrase is "@claude"; it reads the # triggering comment/review and acts on the current PR head. diff --git a/.github/workflows/codex-run-pr-operator.yml b/.github/workflows/codex-run-pr-operator.yml index 7e01787e5b..17d874e324 100644 --- a/.github/workflows/codex-run-pr-operator.yml +++ b/.github/workflows/codex-run-pr-operator.yml @@ -426,7 +426,7 @@ jobs: - name: Setup Node uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24.15.0 + node-version: 26.8.1 cache: npm - name: Install locked dependencies without repository lifecycle scripts diff --git a/.github/workflows/live-web-vitals.yml b/.github/workflows/live-web-vitals.yml index a1a07924e7..81386855be 100644 --- a/.github/workflows/live-web-vitals.yml +++ b/.github/workflows/live-web-vitals.yml @@ -86,7 +86,7 @@ jobs: - name: Use the repository Node runtime uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: "24" + node-version: "26" - name: Normalize the configured origin run: | diff --git a/.node-version b/.node-version index a45fd52cc5..6f4247a625 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -24 +26 diff --git a/.nvmrc b/.nvmrc index a45fd52cc5..6f4247a625 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -24 +26 diff --git a/AGENTS.md b/AGENTS.md index 03b80a3029..b46982f787 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -73,7 +73,7 @@ workstation cannot see the thing that breaks it. This is not hypothetical: `session-start.sh` shipped `100644` while both its siblings were `100755` (found 2026-08-18). That script's body only runs when `CLAUDE_CODE_REMOTE=true`, so the sole environment it does work in is the Linux container where a non-executable checkout cannot - be run — and it is the script that provisions the Node 24 the engine floor needs, after + be run — and it is the script that provisions the Node 26 the engine floor needs, after `npm ci` EBADENGINE blocked PRs #1611, #1697, #1705 and #1740. - **Register hooks as `bash "$CLAUDE_PROJECT_DIR/…"`, never as a bare path**, so the mode is never load-bearing. `session-start.sh` was the only bare-path registration and the only one missing the @@ -100,66 +100,11 @@ For Windows Codex Desktop worktree bootstrap and dry-run instructions, see [`doc - - # Reasoning effort calibration -**Repository baseline.** Use `gpt-5.6-sol` with `high` reasoning effort unless the user explicitly -chooses another supported model or effort. `.codex/config.toml` records this default for trusted -Codex clients that honor repository configuration; a task-level selection can override it. - -**Cloud xhigh gate.** A running Cloud task cannot raise its own reasoning effort. Before substantive -inspection, planning, tool use, or edits, classify the request against the table and the risk rules -below. If `xhigh` is required and the prompt does not contain the exact marker `[xhigh-confirmed]`, -stop and ask the user to select `xhigh` in the Cloud reasoning control, then resubmit the same request -with `[xhigh-confirmed]`. Do not begin the work at `high`, and do not claim the runtime changed. When -the marker is present, treat it as the user's confirmation that `xhigh` was selected and proceed. -Requests classified as `high` or lower proceed without this gate. - -Reasoning effort is a budget in the same way verification is a budget, and it is misspent the same -way — by defaulting to the maximum instead of matching the spend to the risk. **Scale effort to how -expensive the mistake is to undo (irreversibility × branching factor), never to the phase label.** -"Plan high, build lower" is a good default, not a rule; it is wrong often enough that it must be -chosen deliberately rather than assumed. - -**Why it is a good default.** Planning errors compound and implementation errors stay local: a wrong -approach throws away the build, a wrong identifier is one edit. A plan is also a few thousand output -tokens against a build's many long turns, so effort is cheapest exactly where it has the most leverage. - -**The mechanism that makes it work — do not skip this part.** A lower-effort build only succeeds -against a plan concrete enough to execute: named files, named symbols, ordered steps, and the gate -that will prove it. Downgrading the build against a vague plan does not save effort, it relocates the -thinking into the expensive phase. If the plan cannot name those things, the build is not eligible for -the downgrade. - -| Situation | Plan | Build | -| ---------------------------------------------------------------------------------------------- | ----------- | ----------- | -| Architecture, Supabase migrations/RLS, RAG ranking surfaces, auth/privacy, ingestion contracts | xhigh | high | -| Ordinary feature or UI work with a clear shape | high | medium–high | -| Mechanical and fully specified — ledger append, docs edit, rename (not dependency maintenance) | low or skip | medium | -| Debugging an unknown failure | low | high | - -**Where the default inverts and the build needs more than the plan.** These are plan-light and -execution-heavy; treating them as plan-heavy spends the budget in the wrong place: - -- **Debugging.** The plan is "find why X fails." The real reasoning is hypothesis-forming over local - runtime, logs, and repro state during the build — hosted providers still need explicit confirmation. -- **Constraint-dense implementation.** A one-sentence plan whose edit must simultaneously satisfy - button wiring, design tokens, the one-composer rule, unlayered CSS, and the tap-target and - phone-chrome contracts. Holding all of it at once is the hard part, not deciding what to do. -- **Areas where training data is stale.** Next 16 is the standing case. Effort does not repair a wrong - prior — reading `node_modules/next/dist/docs/` does. Raising effort instead of reading is itself the - failure mode. - -**Constrain xhigh planning output, not just its effort.** Extra-high planning over-produces: -alternatives, contingency branches, and surveys that are never used, paid for twice — once generating -and once reading. Ask for the chosen approach, the files, and the gate, not a survey. - -**State the split before non-trivial planning work.** One line before starting: plan effort, build -effort, and the risk that justifies them. It is cheap, it makes a wrong allocation visible while it is -still free to change, and it stops the blanket default from being applied silently. - - +For the Codex reasoning-effort baseline, the Cloud `xhigh` confirmation gate, and the +plan-effort/build-effort table, see +[`docs/agents/codex-reasoning-effort.md`](docs/agents/codex-reasoning-effort.md). @@ -350,9 +295,14 @@ action must perform one; a page that ships must be reachable. - **`production`** — every chunk a non-mockup route reaches, plus chunks no route manifest claims (framework, polyfills, runtime). This is user-facing weight and the real regression guard. Tolerance 10%. A failure here means find the regression; do not refresh the baseline to clear it. -- **`routes`** — client JavaScript referenced by `/`, `/therapy-compass`, `/documents/search`, - `/dsm`, and `/forms`, the same journeys measured by Lighthouse. Each route has a 10% tolerance, - so local growth cannot hide inside a still-healthy repository aggregate. +- **`routes`** — client JavaScript referenced by `/` and `/documents/search`, the same journeys + measured by Lighthouse. Each route has a 10% tolerance, so local growth cannot hide inside a + still-healthy repository aggregate. `/therapy-compass`, `/dsm` and `/forms` were in this list + and were deliberately removed: home consolidation turned all three into redirect stubs that + render the same shared home as `/`, so budgeting them measured `/` three more times rather than + covering anything new (see `tests/check-lighthouse-budget.test.ts`, `COMMITTED_ROUTES`). Do not + re-add them without also un-consolidating the homes; if a mode's own surface needs its own + budget, the route to add is its `/search` view, which is a genuinely different bundle. - **`mockups`** — chunks reachable **only** from `/mockups/**`. Nobody downloads these, so this is a repo-hygiene ceiling for unbounded accumulation, not a per-mockup gate. Tolerance 25%. @@ -886,105 +836,12 @@ Run the matching planner command in `docs/productivity-workflows.md` without sid ## Codex GitHub review behavior -These instructions apply to Codex GitHub pull request reviews and Codex tasks started from PR comments. - -- Keep automatic reviews focused and cost-conscious. -- Prioritize high-confidence findings that affect correctness, security, privacy, data loss, auth/permissions, migrations, API contracts, production reliability, clinical behavior, source governance, or user-facing behavior. -- Do not comment on formatting, naming, style, minor cleanup, or speculative refactors unless they create a real bug or maintainability risk. -- Prefer fewer, stronger findings over exhaustive low-value review comments. -- An automatic review may emit at most three inline findings total. Use inline comments only for P0/P1 issues; put non-blocking P2 context in one summary and omit P3 feedback. -- A finding must cite concrete changed code and explain the failure mode. -- Do not suggest broad rewrites during review. Recommend the smallest change that resolves the issue. -- Do not propose or start fixes unless explicitly asked with an `@codex fix...` or `@codex resolve...` comment, or when the repository's Codex auto-resolve workflow posts that command. -- Treat automatic review as single-pass per pull request. Do not re-review a later head, repeat a prior finding, or create another review during an auto-resolve task unless a human explicitly requests a fresh review. - -### Severity calibration - -- P0: active security exposure, data loss/corruption already possible, severe production outage risk, credential leakage, or a critical issue that must be fixed immediately. -- P1: security vulnerability, auth bypass, data exposure/loss, destructive migration risk, production-breaking regression, public API contract break, severe clinical/user-facing bug, or missing validation with realistic exploit/failure impact. -- P2: important correctness bug, missing behavior test for meaningful changed behavior, edge case likely to affect users, reliability issue, unsafe assumption, or maintainability issue that will likely cause defects. -- P3: style, naming, formatting, small cleanup, speculative improvements, or optional refactors. Avoid raising these in automatic reviews unless explicitly requested. - -For GitHub automatic reviews, focus mainly on P1-level findings. If a P2 issue is important enough to block the PR, explain why it should be treated as P1. - -### PR risk detection - -When reviewing, identify whether the PR touches any high-risk area: - -- authentication or authorization -- user data, privacy, or private document access -- database schema, migrations, RLS, SECURITY DEFINER functions, or Supabase privileges -- clinical answer generation, source governance, retrieval/ranking, ingestion, or document access -- payment, billing, subscriptions, or quotas -- public API contracts -- production configuration or deployment behavior -- background jobs, scheduled tasks, workers, or queue processing -- file upload/download or generated document access -- AI/API provider calls, paid external services, or credential-dependent workflows - -If a high-risk area is touched, review more carefully for regressions, missing tests, rollback/safety notes, and conservative failure behavior. - -### Cost and usage control - -Avoid broad repeated review passes. Do not request exhaustive review behavior unless the PR touches security, auth, data loss, migrations, billing, production reliability, clinical output, source governance, or private document access. Prefer targeted validation and targeted review comments. A new commit from the automatic repair task is not permission for another automatic review. - -### Fix behavior - -When explicitly asked to fix or resolve review findings: - -- Always fix P0 and P1 findings using the best minimal fix. -- For P2 and lower-severity findings, decide whether the issue is worth fixing automatically. -- Fix a P2 or lower finding only when the fix is clear, scoped, low-risk, and testable. -- Do not automatically fix a P2 or lower finding when it requires broad refactoring, product judgment, dependency changes, credentials, paid/external APIs, large design decisions, or risky behavior changes. -- If a P2 or lower finding is not worth fixing automatically, comment with the reason and the recommended human decision, then resolve the review conversation when supported. -- Preserve unrelated work and avoid opportunistic refactors. -- Do not add dependencies unless the issue cannot reasonably be fixed without one. -- Do not change secrets, credentials, environment configuration, billing settings, deployment settings, or external service setup unless explicitly requested. -- Do not use external APIs, paid services, credentials, secrets, live Supabase projects, or OpenAI provider calls unless explicitly authorized. -- If a finding is ambiguous, unsafe to fix automatically, or requires a large rewrite, stop and explain the decision instead of guessing. -- Add or update the smallest relevant test when the issue affects behavior. -- Run the narrowest relevant validation for the touched surface before broader suites. -- Summarize fixed issues, changed files, validation run, and any remaining human decisions. - -### Review comment lifecycle - -- Treat closing review conversations as part of the task when asked to fix or resolve comments. -- After fixing a P0 or P1 finding, reply with the fix summary and resolve the review conversation when supported by GitHub permissions/tooling. -- After fixing an approved P2 or lower finding, reply with the fix summary and resolve the review conversation when supported. -- After deciding not to fix a P2 or lower finding, reply with the reason, note whether it is deferred or not actionable, and resolve the review conversation when supported. -- For every fixed or fully dispositioned thread, start the thread reply with ``. On the next line, use `` for a code fix or `` for a no-code disposition. The workflow closes the thread only when exactly one result is declared and a reported fixed commit is the pull request head. -- Do not use the marker when human input or new authorization is required; explain the blocker and leave that thread open. -- Do not leave a review conversation open after it has been fixed or fully dispositioned. If direct resolution is unavailable, the marker reply is the required fallback and the workflow performs the closure. - -### Automatic resolve trigger - -Automatic Codex review is review-only by default. This repository includes `.github/workflows/codex-autofix-review-comments.yml`, which requests the resolve task automatically after Codex submits a completed PR review that raised findings and the pull request passes the repository's risk/complexity router. - -- The auto-resolve request must fire only from a Codex-authored `pull_request_review` **submitted** event on an open pull request — never from the first inline comment mid-review. This guarantees the request is posted only after a code review completes; without a review there are no findings and the request is pointless. -- The request job must skip reviews with no actionable findings: skip `approved`/`dismissed` reviews, and skip when the submitted review carries zero inline comments. -- Route automatic repair only when at least one changed path is high-risk, when the pull request changes at least 10 non-test source files or 300 non-test source lines, or when the `codex-review` label explicitly opts in. Treat `skip-codex-review` as an unconditional opt-out that wins if both labels are present. -- **Clinical-decision surfaces are never routed to automatic repair**, whatever the finding's severity and whatever the routing rule above would otherwise say. The held paths are `data/**` (except `data/outstanding-issues-snapshot.json`), `src/data/**`, `src/lib/mha-act-sections.ts`, `src/lib/form-catalog.ts`, `src/lib/form-ranker.ts`, `src/components/forms/**`, `src/lib/rag/**`, and the named ranking/answer surfaces `clinical-search`, `retrieval-selection`, `released-search-order`, `ranking-config`, `answer-ranking`, `answer-verification`. The hold is evaluated before routing and has **no override**: the `codex-review` opt-in label does not release it, and a diff that also touches tests or generated files is still held. Codex still reviews these pull requests and its findings still post as inline comments — only the unattended write is withheld, so a human decides. - - Why this exists: a review finding can be sound as a code observation and wrong as an action. On PR #2314 a P1 finding contradicted the owner's explicit decision to display drafted Mental Health Act summaries behind an awaiting-review label; the automatic pass applied it, inverting the render gate and collapsing the Act-sections card from 54 forms to 1, and additionally hardcoding every summary as reviewed. The rationale for the owner's decision was written down in `docs/wiring-conventions.md` and nothing consulted it. "Always fix P0 and P1 findings" therefore stops at these paths: on a clinical surface a bot's severity label is not authority to overwrite a human decision. Enforced by `scripts/check-codex-autofix-workflow.mjs` and `tests/codex-autofix-workflow.test.ts`; do not weaken either to let a specific pull request through. - -- High-risk paths include migrations/RLS, application API routes, auth/permissions/privacy/security, clinical/RAG/retrieval/search/source/document behavior, provider or production configuration, dependencies, and CI/release workflows. Do not route docs-only, test-only, generated-only, or small low-risk UI/copy changes unless explicitly opted in. -- Read changed-file metadata through the GitHub API only; never check out or execute pull-request code in the routing job. Record the selected route in a hidden `codex-autoresolve-route` marker for auditability. -- Match the trusted Codex connector bot by exact login and bot type; do not use substring login checks. -- Keep per-pull-request concurrency on the authorized job, not the whole workflow, so unrelated events cannot displace a pending Codex request. -- Pin the supported Node 24-based `actions/github-script` release to its reviewed immutable commit SHA. -- Post the `@codex` resolve request with a real (non-bot) user identity — a fine-grained PAT held in the `CODEX_TRIGGER_TOKEN` secret. The Codex connector ignores commands authored by `github-actions[bot]`, so a bot-authored request is silently dropped. The token needs `pull-requests: write` (issue-comment) access and no more. -- The workflow must treat unmarked review-thread replies as inert. A trusted Codex reply beginning with `` may only resolve the exact containing thread, and a non-reply Codex review comment must never be turned into a new repair request. -- The workflow must ask Codex to resolve only existing actionable Codex review findings for the triggering pull request and current head using these repository instructions; the resolve task must not perform a new review or create new findings. It must name the exact repository and PR head branch, require fixes to be published there through the authenticated GitHub connector, forbid detached `work` branches and stacked pull requests, and treat a local-only commit as a visible failure. -- The workflow may request one automatic repair pass per pull request lifetime. Later heads require an explicit human request. -- Only trust a pull-request deduplication marker when it was posted by the trigger-token account (the same identity that posts the request), resolved at runtime rather than hard-coded. -- Permission failures while reading or creating pull-request comments must fail the workflow visibly, not return a successful soft-skip. -- The workflow must not run Codex directly with API credentials. -- P0 and P1 findings should always be fixed. -- P2 and lower findings should be fixed only when clear, scoped, low-risk, and testable; otherwise explain the decision and resolve or mark ready for human resolution. - -### Primary PR command - -`@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. The workflow will provide the only allowed repository, pull-request head branch, and starting commit. Publish every approved fix to that exact head branch through the authenticated GitHub connector; never use a detached or synthetic work branch and never create a stacked pull request. Verify the pull-request head contains the pushed commit before reporting success. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. For a fixed thread, reply with followed by . For a no-code disposition, use followed by . A local-only commit is not a fix. If publication or verification fails, use neither result marker, do not claim success, and leave the thread open with the blocker. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation.` +For Codex's automated GitHub pull request review and auto-resolve behavior — severity +calibration, PR risk detection, cost controls, the review comment lifecycle, the automatic +resolve trigger, and the primary PR command — see +[`docs/agents/codex-github-review.md`](docs/agents/codex-github-review.md). That file is the +exact text `scripts/check-codex-autofix-workflow.mjs` enforces against the live workflow; do not +let a copy in this file drift from it. ## Codex Cloud environment @@ -1001,9 +858,5 @@ Codex Cloud uses an isolated Linux container and does not inherit desktop creden ## Cursor Cloud specific instructions (not Codex Cloud) -Durable notes for Cursor Cloud agents (see `docs/agents-guide.md` and `docs/testing.md` for full reference): - -- Context7 peer-library docs habit and Next 16 local docs live in `docs/agents-guide.md`. -- Requires Node >=24.15.0 <25 / npm 11.x (installed via nvm, symlinked to `/usr/local/cargo/bin`). -- Offline verification: `npm run lint`, `npm run typecheck`, `npm run test`, `npm run verify:cheap`, and `npm run verify:pr-local` all pass without secrets. -- For authorized GitHub work, use the connected GitHub connector/MCP tools as primary interface (`BigSimmo` write access). +For Cursor Cloud agent setup, live-vs-demo mode detection, verification commands, and GitHub +connector guidance, see [`docs/agents/cursor-cloud.md`](docs/agents/cursor-cloud.md). diff --git a/Dockerfile b/Dockerfile index a78519ab98..cd7b5551a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ # syntax=docker/dockerfile:1 # PsychSift app tier (Next.js). See docs/deployment-architecture.md. # -# The repo is engine-strict (Node 24.x / npm 11.x via .npmrc + preinstall -# guard), so every stage pins the same Node 24 base image. The build stage +# The repo is engine-strict (Node 26.x / npm 11.x via .npmrc + preinstall +# guard), so every stage pins the same Node 26 base image. The build stage # runs the repo's own `npm run build` (guard-next-build + next build) so the # image build fails exactly where a local build would. # @@ -18,7 +18,7 @@ # NEVER baked into the image — inject them at run time from the host's # secret store. -FROM node:24-bookworm-slim@sha256:235600a8101ab264e117b1768e925532262668dc9b581ef1dd7d96ced463b8e7 AS node-base +FROM node:26-bookworm-slim@sha256:367679cf9792759492a486e4aa4b421764d71a9546a6dae8aab81a99eb797b3e AS node-base FROM node-base AS deps WORKDIR /app diff --git a/Dockerfile.worker b/Dockerfile.worker index 52f0644a60..bd6d8f1ed2 100644 --- a/Dockerfile.worker +++ b/Dockerfile.worker @@ -4,7 +4,7 @@ # container instead of completing the edge-agent migration. # # Runtime contents (2026-07-13 audit, finding 12 — slimmed): -# - Node 24 + PRODUCTION-ONLY node_modules: the worker runs as a +# - Node 26 + PRODUCTION-ONLY node_modules: the worker runs as a # prebuilt esbuild bundle (dist/worker/index.mjs), so tsx and the rest # of the dev toolchain never reach the image. # - Tesseract OCR (Debian package, bundles English language data). @@ -27,7 +27,7 @@ # `server-only` marker to the standalone stub at build time (the job # run-tsx.mjs previously did at runtime) and keeps npm packages external, # so the bundle resolves them from the runner's production node_modules. -FROM node:24-bookworm-slim@sha256:235600a8101ab264e117b1768e925532262668dc9b581ef1dd7d96ced463b8e7 AS node-base +FROM node:26-bookworm-slim@sha256:367679cf9792759492a486e4aa4b421764d71a9546a6dae8aab81a99eb797b3e AS node-base FROM node-base AS build WORKDIR /app diff --git a/README.md b/README.md index 94a82998c7..9b9f6abb1b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ questions with source citations that link back to the original PDF/document. ## Setup -1. Use Node.js 24.x with npm 11.x. CI runs on Node 24, and `.nvmrc` / +1. Use Node.js 26.x with npm 11.x. CI runs on Node 26, and `.nvmrc` / `.node-version` pin the same runtime for local version managers. CI also runs `npm run check:edge:functions`, which requires Deno v2.x. 2. Install dependencies: diff --git a/bundle-budget.json b/bundle-budget.json index a377012dbe..3cc5044d36 100644 --- a/bundle-budget.json +++ b/bundle-budget.json @@ -9,6 +9,12 @@ "gzipBytes": 627814, "tolerancePct": 25 }, + "serverPages": { + "/mockups/development/review-state": { + "rawBytesCeiling": 2500000, + "gzipBytesCeiling": 350000 + } + }, "routes": { "/": { "gzipBytes": 285184, diff --git a/data/calculators/evidence.json b/data/calculators/evidence.json new file mode 100644 index 0000000000..fdf9881bb2 --- /dev/null +++ b/data/calculators/evidence.json @@ -0,0 +1,135 @@ +{ + "sources": [ + { + "id": "source:governance", + "issuer": "PsychSift", + "title": "Calculator clinical-safety governance record", + "type": "internal_governance_record", + "version": "2026-09-01", + "url": "https://github.com/BigSimmo/Database/blob/main/docs/superpowers/specs/2026-09-01-calculators-clinical-safety.md", + "jurisdiction": "Australia", + "status": "reviewed", + "claimsSupported": [ + "claim:phq9:interpretation", + "claim:phq9:safety-flag", + "claim:gad7:interpretation", + "claim:k10:interpretation", + "claim:cage:interpretation", + "claim:auditc:interpretation", + "claim:mdq:interpretation", + "claim:sadpersons:interpretation", + "claim:ybocs:interpretation" + ], + "limitations": ["Records the release decision and does not replace an instrument source."] + }, + { + "id": "source:phq9", + "issuer": "Kroenke, Spitzer & Williams", + "title": "The PHQ-9: Validity of a Brief Depression Severity Measure", + "type": "journal_article", + "version": "2001 validation study", + "url": "https://pmc.ncbi.nlm.nih.gov/articles/PMC1495268/", + "jurisdiction": "International", + "status": "reviewed", + "claimsSupported": ["claim:phq9:interpretation"], + "limitations": [ + "A completed score supports assessment and monitoring; it is not a diagnosis or management decision." + ] + }, + { + "id": "source:gad7", + "issuer": "Spitzer et al.", + "title": "A Brief Measure for Assessing Generalized Anxiety Disorder", + "type": "journal_article", + "version": "2006 validation study", + "url": "https://pubmed.ncbi.nlm.nih.gov/16717171/", + "jurisdiction": "International", + "status": "reviewed", + "claimsSupported": ["claim:gad7:interpretation"], + "limitations": ["A completed score supports anxiety assessment and does not establish a diagnosis."] + }, + { + "id": "source:k10", + "issuer": "Australian Bureau of Statistics", + "title": "Kessler Psychological Distress Scale (K10)", + "type": "government_information_paper", + "version": "2003 information paper", + "url": "https://www.abs.gov.au/ausstats/abs%40.nsf/papersbyReleaseDate/4D5BD324FE8B415FCA2579D500161D57", + "jurisdiction": "Australia", + "status": "reviewed", + "claimsSupported": ["claim:k10:interpretation"], + "limitations": ["The K10 measures psychological distress and is not a diagnostic category."] + }, + { + "id": "source:cage", + "issuer": "Ewing", + "title": "Detecting alcoholism: The CAGE questionnaire", + "type": "journal_article", + "version": "1984 original article", + "url": "https://pubmed.ncbi.nlm.nih.gov/6471323/", + "jurisdiction": "International", + "status": "reviewed", + "claimsSupported": ["claim:cage:interpretation"], + "limitations": ["CAGE is a lifetime problem-drinking screen and does not assess current use or withdrawal."] + }, + { + "id": "source:auditc", + "issuer": "Australian Government Department of Health", + "title": "Australian standard drinks guide", + "type": "government_web_guidance", + "version": "Current web guidance", + "url": "https://www.health.gov.au/topics/alcohol/about-alcohol/standard-drinks-guide", + "jurisdiction": "Australia", + "status": "reviewed", + "claimsSupported": ["claim:auditc:interpretation"], + "limitations": ["The standard-drink reference does not establish a diagnosis or management decision."] + }, + { + "id": "source:mdq", + "issuer": "Hirschfeld et al.", + "title": "Development and validation of a screening instrument for bipolar spectrum disorder", + "type": "journal_article", + "version": "2000 validation study", + "url": "https://pubmed.ncbi.nlm.nih.gov/11058490/", + "jurisdiction": "International", + "status": "permission_review_required", + "claimsSupported": ["claim:mdq:interpretation"], + "limitations": ["Quarantined pending approval of digital-use rights and completion semantics."] + }, + { + "id": "source:sadpersons", + "issuer": "Patterson et al.", + "title": "Evaluation of suicidal patients: the SAD PERSONS scale", + "type": "journal_article", + "version": "1983 original article", + "url": "https://pubmed.ncbi.nlm.nih.gov/6867245/", + "jurisdiction": "International", + "status": "not_for_active_use", + "claimsSupported": ["claim:sadpersons:interpretation"], + "limitations": ["Not released for active score-based clinical decision support."] + }, + { + "id": "source:ybocs", + "issuer": "Goodman et al.", + "title": "The Yale-Brown Obsessive Compulsive Scale. I. Development, Use, and Reliability", + "type": "journal_article", + "version": "1989 original article", + "url": "https://pubmed.ncbi.nlm.nih.gov/2684084/", + "jurisdiction": "International", + "status": "permission_review_required", + "claimsSupported": ["claim:ybocs:interpretation"], + "limitations": ["Quarantined because the current generic anchors are not a verified Y-BOCS implementation."] + } + ], + "claims": [ + { "id": "claim:phq9:interpretation", "sourceIds": ["source:phq9", "source:governance"] }, + { "id": "claim:phq9:safety-flag", "sourceIds": ["source:phq9", "source:governance"] }, + { "id": "claim:gad7:interpretation", "sourceIds": ["source:gad7", "source:governance"] }, + { "id": "claim:k10:interpretation", "sourceIds": ["source:k10", "source:governance"] }, + { "id": "claim:cage:interpretation", "sourceIds": ["source:cage", "source:governance"] }, + { "id": "claim:auditc:interpretation", "sourceIds": ["source:auditc", "source:governance"] }, + { "id": "claim:mdq:interpretation", "sourceIds": ["source:mdq", "source:governance"] }, + { "id": "claim:sadpersons:interpretation", "sourceIds": ["source:sadpersons", "source:governance"] }, + { "id": "claim:ybocs:interpretation", "sourceIds": ["source:ybocs", "source:governance"] } + ] +} diff --git a/data/outstanding-issues-snapshot.json b/data/outstanding-issues-snapshot.json index f2c29a9844..ff5cd51854 100644 --- a/data/outstanding-issues-snapshot.json +++ b/data/outstanding-issues-snapshot.json @@ -10,7 +10,7 @@ "p2": 49, "p3": 31, "queued": 7, - "pending": 38, + "pending": 85, "resolved": 431 }, "queue": [ @@ -830,6 +830,24 @@ "summary": "Caring Contacts: the vocabulary scan rewards leaving developer identifiers on screen, and the sealed domain it pushes copy into is watched by no vocabulary scan at all", "created_at": "2026-08-25" }, + { + "request_id": "084e06ec-e097-4b27-933d-2c3138ffbb01", + "action": "done", + "summary": "#XHNTHD: Added --allow-partial CLI flag and resolveEvaluationCases() fail-safe throwing when cases.length === 0 or when cases are filtered without --allow-partial in scripts/eval-retrieval.ts; added unit tests in tests/eval-retrieval.test.ts.", + "created_at": "2026-08-31" + }, + { + "request_id": "0e0c8272-b098-4cce-9a19-04450b931432", + "action": "cancel", + "summary": "Cancel request 9e88c6de-2225-4134-888c-aec117a116e7: Target issue is no longer in Open items on current main; cancel stale PR-bundled done request so check:docs-links can dry-apply the pending inbox.", + "created_at": "2026-08-31" + }, + { + "request_id": "10743a6e-53e4-45e9-b71a-468bf54682f2", + "action": "done", + "summary": "#S4R2W3: Added isBareDocumentSupportListAnswer and guarded documentSupportListIntent so clinical questions do not return bare title lists without substantive prose.", + "created_at": "2026-08-31" + }, { "request_id": "11e9136a-6db8-4d3f-84c1-4d39cf18774a", "action": "cancel", @@ -842,18 +860,48 @@ "summary": "Caring Contacts: the interface-vocabulary scan cannot express a job title, so correct plain-words role labels fail while raw identifiers pass", "created_at": "2026-08-25" }, + { + "request_id": "1742520a-b0d1-4d65-98bf-719f030e98d2", + "action": "cancel", + "summary": "Cancel request 45fe36c1-2d0b-47d2-afc4-f449b7dc3c84: Superseded by an earlier pending done request already on main for the same issue; keep main claim and drop this PR duplicate to unblock check:docs-links mutation conflicts.", + "created_at": "2026-08-31" + }, { "request_id": "17e26e25-5180-4ed4-9391-834ec456d605", "action": "done", "summary": "#G693BZ: Verified PanelSection extracted in panel-primitives.tsx and adopted across routes, documentation, test-health, review-state, and ledger pages.", "created_at": "2026-08-27" }, + { + "request_id": "184404de-c45e-43e3-8048-2825799b349e", + "action": "add", + "summary": "Nothing compares the replayed migration chain against supabase/schema.sql, so a migration that changes a function body without a matching schema.sql edit passes every pre-merge gate and only surfaces post-merge in live-drift", + "created_at": "2026-09-01" + }, { "request_id": "2166a980-bac5-4565-b009-f7f4ea530e5c", "action": "done", "summary": "#GQ5X8T: NOT REPRODUCED ON PINNED LINUX; WINDOWS-SPECIFIC CAUSE UNRESOLVED. Eleven retained PR #2313 ubuntu-24.04 Lighthouse artifacts used Lighthouse 12.8.2 and pinned HeadlessChrome/151.0.0.0. In the nine-run synchronized-baseline subset, mobile-root LCP was 2260.791-2300.923ms with median 2287.574ms versus 2274.017ms baseline (+13.557ms, +0.60%); TBT median was 430.602ms versus 436.600ms baseline (-1.37%); CLS was 0 throughout. Across all eleven heads LCP was 2221.675-2300.923ms, never reproducing the Windows 7422-8032ms state. The exact local 883f1007 control / 7cf37b53 treatment snapshots had identical lock hashes, but the local host stayed at 42.3-62.9% CPU and the control build hit the 10-minute Linux cap after compiling, before measurement; that attempt is environment-blocked and was not rerun. This excludes a repeatable cross-platform regression but cannot distinguish Windows host noise from a Windows-specific client regression. No product, budget, tolerance, request-count, or proven CLS change was made. Reopen if a pinned Linux exact-head run breaches repeatably or a synchronized quiet-host Windows control reproduces the slowdown. Evidence: docs/evidence/mobile-root-timing-control-2026-08-26.md.", "created_at": "2026-08-26" }, + { + "request_id": "224cda1c-ae72-4921-b8aa-e6b3e315796a", + "action": "update", + "summary": "#ZBAC9D: detail → documents.owner_id is ON DELETE SET NULL (supabase/schema.sql) while a null owner independently means 'public corpus' in retrieval. CORRECTION (Codex on PR #2494): deleting an auth user does NOT silently republish private documents — documents_require_publication_approval (BEFORE INSERT OR UPDATE) runs guard_document_publication_transition, which rejects every non-null-to-null owner transition without matching publication evidence, so the user-delete transaction fails instead of orphaning/republishing. Distinguish blocked user deletion from any pre-existing orphan rows. RE-VERIFIED against main and the P2 -> P1 escalation still holds on the retrieval hazard, not on a live delete-to-publish path. (1) The retrieval path is NOT closed at any layer. public.retrieval_owner_matches still resolves the public sentinel to 'row_owner_id is null' with no public_corpus condition, and grep for 'public_corpus' returns ZERO occurrences across the entire retrieval chain: rag/rag-candidate-sources.ts, corpus-grounding.ts, deep-memory.ts, document-enrichment.ts, owner-scope.ts, clinical-search.ts and query-privacy.ts. Any ownerless row (pre-existing orphan or armed hole) is therefore retrievable as public corpus and citable in an answer to an anonymous query. (2) The helper written to close exactly this hazard is DEAD CODE. src/lib/documents/is-public-document.ts requires owner_id === null AND metadata.public_corpus === true, and tests/documents-is-public.test.ts:22 pins the precise case ('Orphaned after user delete' -> false), but grep across src/ and worker/ finds NO production caller outside its own module and the barrel re-export. Same shape as #A3AHCX. (3) The only genuinely closed application read path is the REST document read: withOwnerReadScope in src/lib/public-api-access.ts applies .is('owner_id', null).eq('metadata->>public_corpus','true'). NEXT, in order: (a) one read-only live count of documents where owner_id is null and metadata->>'public_corpus' is distinct from 'true' — this decides whether the hole is currently leaking or merely armed, and needs owner approval as a provider-backed read; (b) close the retrieval path, which is a genuine retrieval behaviour change on a protected RAG surface and so needs the RAG flag plus a live eval-canary pair; (c) add a database proof that deleting an owner of an unapproved private document fails and creates no ownerless indexed row, then in an approved production window change documents.owner_id to ON DELETE RESTRICT (or an explicit visibility column) and align the RPC predicate. Note the offline proof that no table whose null owner means public declares ON DELETE SET NULL cannot be added green today — it would fail on documents.owner_id, so it is the guard that accompanies (c), not a step that precedes it.; priority → P1; source → repo-wide audit at 3ed1932 (six domain reviewers); re-verified and expanded against main a633eb6e3 by the database coordination chat 2026-09-01; deletion-path correction per Codex review on PR #2494", + "created_at": "2026-09-01" + }, + { + "request_id": "256a5104-df13-44d3-9734-645e08b2b0df", + "action": "cancel", + "summary": "Cancel request 97bbfd51-435b-4e31-b956-7138753eb0a8: Target issue is no longer in Open items on current main; cancel stale PR-bundled done request so check:docs-links can dry-apply the pending inbox.", + "created_at": "2026-08-31" + }, + { + "request_id": "272880e4-0cc3-4522-9422-1c152a6d838d", + "action": "cancel", + "summary": "Cancel request 932b96f8-6af8-48a2-aeac-d78d5bb506a9: Superseded by an earlier pending done request already on main for the same issue; keep main claim and drop this PR duplicate to unblock check:docs-links mutation conflicts.", + "created_at": "2026-08-31" + }, { "request_id": "2f077536-00d5-46da-a7c2-63ad9d0221e5", "action": "done", @@ -866,12 +914,24 @@ "summary": "Caring Contacts: a draft plan's contacts are already in state scheduled and listSendableContacts has no plan-state gate", "created_at": "2026-08-25" }, + { + "request_id": "356a1eb2-70c3-4303-bf98-4af30d9502ce", + "action": "cancel", + "summary": "Cancel request b2d0158c-09c0-441e-97af-fac42cbe5f12: Superseded by an earlier pending done request already on main for the same issue; keep main claim and drop this PR duplicate to unblock check:docs-links mutation conflicts.", + "created_at": "2026-08-31" + }, { "request_id": "35b8730d-b1f3-41bd-9bc2-7e485446391a", "action": "done", "summary": "#V27DZ1: Resolved Playwright strict-mode locator uniqueness on Ward Flow role screens by unifying root containers and verified with tests/ward-management-role.test.ts", "created_at": "2026-08-27" }, + { + "request_id": "368839d5-3ef0-46d4-94bc-b9d93842490c", + "action": "done", + "summary": "#WP76E6: Added Dismissal model section to docs/caring-contacts/interaction-matrix.md.", + "created_at": "2026-08-31" + }, { "request_id": "36b7dc6b-733e-4116-8a47-4ef35e86039f", "action": "done", @@ -902,12 +962,36 @@ "summary": "Caring Contacts: no browser evidence exists for any activation-wizard stage, including a two-write middle state where a clinician presses a writing control a second time", "created_at": "2026-08-25" }, + { + "request_id": "428fedef-b62c-461a-8211-ce645549e2d2", + "action": "add", + "summary": "The document cover-thumbnail hook and its /api/documents/[id]/cover route have no product consumer since the source drawer stopped rendering a front-page thumbnail", + "created_at": "2026-09-01" + }, + { + "request_id": "44b322b0-0a87-4564-878a-9d5ac97bcdc3", + "action": "update", + "summary": "#WG24JB: summary → Ward Flow: spec D9: the discharge board is reachable only from the sidebar, not from the capacity board's own figures", + "created_at": "2026-08-31" + }, { "request_id": "44e5f87c-d30f-4334-9184-75c1544773ef", "action": "done", "summary": "#PM9SP1: Relabelled Therapy Compass copy from 'Decision support' to 'Source-grounded therapy reference' across workspace, screens, and app-modes", "created_at": "2026-08-27" }, + { + "request_id": "45fe36c1-2d0b-47d2-afc4-f449b7dc3c84", + "action": "done", + "summary": "#V27DZ1: Disambiguated ward-diagram-unplaced-unit-${unit.id} and ward-shortlist-unresolved-referred-badge in flow-diagram.tsx and shortlist-panel.tsx.", + "created_at": "2026-08-31" + }, + { + "request_id": "461a406d-4937-41c2-afbb-64d1b75f4bcf", + "action": "done", + "summary": "#AGRAKQ: Fixed regex in tests/helpers/caring-contacts-prohibited-language.ts to exclude plural job titles like clinical leads.", + "created_at": "2026-08-31" + }, { "request_id": "4e58bc6b-982a-4aca-b89f-d976ab47b61d", "action": "done", @@ -920,6 +1004,12 @@ "summary": "Cancel request 373b1aa3-596e-4d5f-bc4f-b05ad44a76f5: Scoped to hydration when filed after Task 7; Tasks 8 and 9 widened the consequence far beyond that, and the row as written understates what is unproven. Same root cause and same trigger to close, so superseded by one replacement stating the full cost rather than adding a second row.", "created_at": "2026-08-25" }, + { + "request_id": "6a1eb6b9-8a29-4f7c-89e3-6e5b9b498b3b", + "action": "cancel", + "summary": "Cancel request 461a406d-4937-41c2-afbb-64d1b75f4bcf: Target issue is no longer in Open items on current main; cancel stale PR-bundled done request so check:docs-links can dry-apply the pending inbox.", + "created_at": "2026-08-31" + }, { "request_id": "6b49d272-6636-4004-b13d-3073b75b7339", "action": "add", @@ -938,6 +1028,12 @@ "summary": "#Y0BDBJ: Implemented ReviewStateTable with 50-item responsive pagination reducing HTML payload from 1.73 MB to <100 KB.", "created_at": "2026-08-27" }, + { + "request_id": "7492964b-2a87-4116-a492-8212f5270a99", + "action": "cancel", + "summary": "Cancel request eaa89aeb-c7a4-47d8-b81f-7ef8a6689c4e: Owner approvals closed the HMAC and retention items; replace with a six-blocker request.", + "created_at": "2026-09-01" + }, { "request_id": "7ab6b272-d913-43f5-bf08-a34dcb362f9a", "action": "add", @@ -950,18 +1046,48 @@ "summary": "#EFETZT: detail → UPDATE 2026-08-27 (PR #2413, second and third occurrences): this is not only a staleness problem, it also produces hard git merge conflicts that block auto-merge. data/repo-awareness-snapshot.json conflicted twice in fifteen minutes on one PR head — first when main advanced to be65b8a, again when main advanced to fb88b47 — each time setting mergeable_state=dirty, which suppresses refs/pull//merge so pull_request CI (CI, Gitleaks, Semgrep) does not run at all and the check list reads empty rather than red. Both were resolved identically: git merge origin/main, git checkout --theirs the snapshot, npm run snapshot:repo-awareness, commit, push. The staleness half also reproduced: check:repo-awareness-snapshot failed Static PR checks with 'review_state differs from the repository'. Cost per occurrence is one full CI round trip plus a push, and it scales with how many PRs are open, since every merge to main regenerates the file for all of them. Reinforces the fix direction already proposed on this row: exclude docs/branch-review-records/ from the compared content the way captured_revision already is, or compare a narrower key set. Adding .gitattributes merge guidance for this path would blunt the conflict half but not the CI-staleness half.", "created_at": "2026-08-27" }, + { + "request_id": "86ba753c-f2fd-4be7-92f4-df3d54cf4a04", + "action": "update", + "summary": "#QCNE6N: detail → generate-drift-manifest.ts replays supabase/schema.sql and embeds ITS sha256; CI's db-reset-verify runs supabase migration up --local, proving the chain APPLIES but never diffing the result against schema.sql. A migration whose function or policy body diverges from the mirror passes every gate. Object-NAME parity does hold today (all create targets across 211 migrations resolve into schema.sql) but it is enforced by hand-written per-object tests, not systematically. FIX: after migration up --local, replay schema.sql into a second scratch database and diff schema_drift_snapshot() between the two. Fully offline and containerised, no provider access. DEMONSTRATED 2026-09-01, no longer theoretical: live-drift run 33484535655 (red on main at d3074946a) reported its sole unexpected finding as public.correct_clinical_query_terms(text,real) def_hash manifest e2356565 vs live 2ebaf978. Cause was exactly this gap - migration 20260831100000 (PR #2477) redefined that function with a duplicated 'and length(canonical) between 4 and 40' predicate and supabase/schema.sql was never updated to match, so the manifest disagreed with live while every pre-merge gate stayed green. The db-reset-verify assertion that did run is committed.schema_sha256 === generated.schema_sha256 (.github/workflows/ci.yml ~L1082-1095), which only catches an unrefreshed manifest, not a chain/mirror divergence. Behaviour impact of that instance was nil (the duplicate predicate is a boolean no-op) but it cost a red daily alarm and a remediation PR, and it is the second occurrence of this failure class after the #316 root cause (SET work_mem absent from schema.sql). Strengthens the case for scheduling the containerised two-database diff above.; source → repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f; demonstrated by live-drift run 33484535655, classified by the database coordination chat 2026-09-01", + "created_at": "2026-09-01" + }, + { + "request_id": "870d4673-65da-40b8-94ba-15ba3c6ec6db", + "action": "cancel", + "summary": "Cancel request b6d16549-d2cb-4815-9386-9c38854e7c0b: Target issue is no longer in Open items on current main; cancel stale PR-bundled done request so check:docs-links can dry-apply the pending inbox.", + "created_at": "2026-08-31" + }, { "request_id": "8cf62eef-79a8-4b23-83cc-9290eb71dff3", "action": "add", "summary": "mode-home-page-skeleton still subtracts a chrome estimate from 100dvh, the pattern invariant 24 retired everywhere else", "created_at": "2026-08-27" }, + { + "request_id": "8d78d861-64cc-4e96-ad68-8dd7560cafad", + "action": "done", + "summary": "#2X46NY: Added comprehensive JSDoc docstrings across exported and internal functions in src/lib/rag/ to raise answer-source coverage above 80%.", + "created_at": "2026-08-31" + }, + { + "request_id": "932b96f8-6af8-48a2-aeac-d78d5bb506a9", + "action": "done", + "summary": "#HX1KSZ: Added component metric boundary prohibition table row and §3c rule to docs/design-system/GATES.md.", + "created_at": "2026-08-31" + }, { "request_id": "964e1147-56b2-4750-8794-4b8b1d8e4e04", "action": "add", "summary": "Ward Flow pinned-clock fix is committed only to an unpushed local branch, and the Phase 6 morning page still carries the defect, its workaround, and an untested D5 branch", "created_at": "2026-08-27" }, + { + "request_id": "97bbfd51-435b-4e31-b956-7138753eb0a8", + "action": "done", + "summary": "#EG4Q7W: Modularized postgres-repository.ts into 5 cohesive domain modules (core, plans, contacts, referrals-pathways, assignments-services) with full test suite passing.", + "created_at": "2026-08-31" + }, { "request_id": "998e657c-4852-4572-a4f8-5cf919ea2fea", "action": "done", @@ -974,6 +1100,18 @@ "summary": "Cancel request b60f9982-f48c-4a70-ae92-99d88313cab3: The summary and detail both state a test count that this branch's own next commit falsified, and one of them double-counts. Filed while the caring-contacts database suite was 193 tests; moving the PLAN_COLUMNS guard to the offline project made it 192, so the record was true when written and false before it could be reconciled. The summary also reads 'its 193 tests and the PLAN_COLUMNS narrowing guard', which double-counts the guard that was already inside the 193. Superseded by a replacement that states the invariant instead of a tally, per Ruling 94. The underlying issue is real and unchanged.", "created_at": "2026-08-25" }, + { + "request_id": "9d654032-6e3d-439f-81ac-70d8314f4763", + "action": "update", + "summary": "#9GPWT3: summary → Ward Flow: bed-release state model is unvalidated by any ward clinician", + "created_at": "2026-08-31" + }, + { + "request_id": "9e88c6de-2225-4134-888c-aec117a116e7", + "action": "done", + "summary": "#S4K1GA: Documented Reduce Motion iPhone acceptance criteria in docs/search-chrome-behaviour.md.", + "created_at": "2026-08-31" + }, { "request_id": "a2cff633-7bfe-480e-91bc-8d97f68c66c7", "action": "done", @@ -998,6 +1136,18 @@ "summary": "Lighthouse desktop-root LCP reads 100-175ms above main on a feature branch, decays run over run, and reddened PR #2422 once", "created_at": "2026-08-27" }, + { + "request_id": "b1630592-46f5-4e1a-aa03-97aa30e516af", + "action": "done", + "summary": "#0HYHTH: Verified static prohibited vocabulary checks in tests/caring-contacts-interface-vocabulary.test.ts.", + "created_at": "2026-08-31" + }, + { + "request_id": "b2d0158c-09c0-441e-97af-fac42cbe5f12", + "action": "done", + "summary": "#PDM6NG: Added screen registration parity test in tests/caring-contact-route-files.test.ts.", + "created_at": "2026-08-31" + }, { "request_id": "b2e5f3fc-ab2d-4a14-a055-6742d6446784", "action": "add", @@ -1010,24 +1160,108 @@ "summary": "CI never runs the caring-contacts database suite, so its 193 tests and the PLAN_COLUMNS narrowing guard only fire on a developer's machine", "created_at": "2026-08-25" }, + { + "request_id": "b6d16549-d2cb-4815-9386-9c38854e7c0b", + "action": "done", + "summary": "#61TZJA: Verified PR #2199 merged and AWAITING_BASELINE in tests/ui-visual-baseline.spec.ts is empty.", + "created_at": "2026-08-31" + }, + { + "request_id": "bb121cd3-235b-45f1-817e-a82d7c6c5a31", + "action": "add", + "summary": "Close remaining privacy provider, legal, and clinical approvals", + "created_at": "2026-09-01" + }, { "request_id": "bda0d7e0-7d0a-4946-9f22-8062959a6908", "action": "add", "summary": "Caring Contacts: most wizard test cases are proved alive rather than proved complete, and the denominator is now counted", "created_at": "2026-08-25" }, + { + "request_id": "c3cf6c59-298b-424e-90bf-9e2cdd9c559e", + "action": "done", + "summary": "#F5EYAJ: Verified BLOCK_REASON_WORDING in overlay-host.tsx and DOM tests in tests/caring-contacts-overlay-host.dom.test.tsx.", + "created_at": "2026-08-31" + }, { "request_id": "c4bc5cb3-442e-42ed-9af4-29ae72f02fb6", "action": "add", "summary": "Caring Contacts: the plans list query fetches every patient's mobile number and identifiers on every caseload render", "created_at": "2026-08-24" }, + { + "request_id": "c5f2cb44-cd51-4acc-a9d1-40a5fc1c56a0", + "action": "cancel", + "summary": "Cancel request dc26c333-1935-47e8-ab71-b5206c84e19b: Superseded by an earlier pending done request already on main for the same issue; keep main claim and drop this PR duplicate to unblock check:docs-links mutation conflicts.", + "created_at": "2026-08-31" + }, { "request_id": "c66f01fb-e52a-40fa-a4b2-f4d1449ddb11", "action": "done", "summary": "#DTSABC: Authored Form 12A statutory Authority and Criteria prose from committed PDF and asserted in tests/forms.test.ts", "created_at": "2026-08-27" }, + { + "request_id": "c733065e-9076-4f43-a7b2-9e0b5d5dcedd", + "action": "cancel", + "summary": "Cancel request 44b322b0-0a87-4564-878a-9d5ac97bcdc3: Superseded by an earlier pending done request already on main for the same issue; keep main claim and drop this PR duplicate to unblock check:docs-links mutation conflicts.", + "created_at": "2026-08-31" + }, + { + "request_id": "c862828e-276c-4d90-829b-2c2b35e04274", + "action": "done", + "summary": "#59JT7W: Verified resolveClosingContactMessageBody and tests/caring-contacts-message-policy.test.ts.", + "created_at": "2026-08-31" + }, + { + "request_id": "c995abce-f7d4-4be2-b057-80b173a5246f", + "action": "add", + "summary": "An agent push rewrote tests/ui-smoke.spec.ts from 6,019 lines to 1,081, deleting 74 of 82 UI journey tests, and only a merge conflict stopped it auto-merging", + "created_at": "2026-08-31" + }, + { + "request_id": "cbe7c359-b8e1-4218-a4df-59e869133e0a", + "action": "cancel", + "summary": "Cancel request 184404de-c45e-43e3-8048-2825799b349e: Duplicate of the existing open row #QCNE6N, which already records that CI never diffs the replayed migration chain against supabase/schema.sql and already proposes the same containerised two-database fix. Retracted the same session it was queued; the new 2026-09-01 evidence was reissued as an update to #QCNE6N instead (request 86ba753c-f2fd-4be7-92f4-df3d54cf4a04).", + "created_at": "2026-09-01" + }, + { + "request_id": "cd767916-0034-4e5a-b2db-276609d0deec", + "action": "cancel", + "summary": "Cancel request c862828e-276c-4d90-829b-2c2b35e04274: Target issue is no longer in Open items on current main; cancel stale PR-bundled done request so check:docs-links can dry-apply the pending inbox.", + "created_at": "2026-08-31" + }, + { + "request_id": "d5ea0d4f-652f-480c-9a87-3be0ff7ef4b6", + "action": "cancel", + "summary": "Cancel request dba6b1fc-cb98-49e6-a035-8ca83946232a: Superseded by an earlier pending done request already on main for the same issue; keep main claim and drop this PR duplicate to unblock check:docs-links mutation conflicts.", + "created_at": "2026-08-31" + }, + { + "request_id": "d6e41550-1a6a-4f4f-aff5-d95004011942", + "action": "done", + "summary": "#PWZYY8: Added aria-hidden=\"true\" to icon call-sites and updated eslint-rules/require-lucide-icon-aria.mjs to cover Icon/ActiveIcon.", + "created_at": "2026-08-31" + }, + { + "request_id": "da5b168d-a9fa-475f-a456-867edfe3694c", + "action": "cancel", + "summary": "Cancel request 368839d5-3ef0-46d4-94bc-b9d93842490c: Superseded by an earlier pending done request already on main for the same issue; keep main claim and drop this PR duplicate to unblock check:docs-links mutation conflicts.", + "created_at": "2026-08-31" + }, + { + "request_id": "dba6b1fc-cb98-49e6-a035-8ca83946232a", + "action": "done", + "summary": "#76GGRG: Implemented full WAI-ARIA Menu keyboard navigation with Arrow Up/Down/Left/Right/Home/End/Escape in search-pins-menu.tsx.", + "created_at": "2026-08-31" + }, + { + "request_id": "dc26c333-1935-47e8-ab71-b5206c84e19b", + "action": "done", + "summary": "#1CYR4V: Verified 640-767px overlay modal responsive boundary contract in tests/caring-contacts-overlay-host.dom.test.tsx.", + "created_at": "2026-08-31" + }, { "request_id": "dc7ad7ef-1a27-4305-b1f5-326ee08985eb", "action": "add", @@ -1040,17 +1274,65 @@ "summary": "#HX1KSZ: Codified 3-way component-metric boundary rule in docs/design-system/GATES.md section 3 prohibition table without value conversions.", "created_at": "2026-08-27" }, + { + "request_id": "df2e763d-c1ad-4495-9b62-046f01ab377c", + "action": "done", + "summary": "#DHAR98: Harden all SECURITY DEFINER functions with explicit pg_temp search_path.", + "created_at": "2026-08-31" + }, + { + "request_id": "e2c82b28-4e33-46cf-839e-d00ad26d35d7", + "action": "cancel", + "summary": "Cancel request f3288efd-b1b6-492f-9a4a-8b316e35fe71: Superseded by an earlier pending done request already on main for the same issue; keep main claim and drop this PR duplicate to unblock check:docs-links mutation conflicts.", + "created_at": "2026-08-31" + }, + { + "request_id": "e3892275-41ea-475c-bade-e57d2d348fc4", + "action": "done", + "summary": "#0JJ4RD: Consolidated live purge_expired_rag_response_cache overloads into single bounded signature in migration 20260831100000_harden_security_definer_search_paths_and_purge_cache.sql and schema mirror.", + "created_at": "2026-08-31" + }, + { + "request_id": "eaa89aeb-c7a4-47d8-b81f-7ef8a6689c4e", + "action": "add", + "summary": "Close current privacy provider, legal, and clinical approvals", + "created_at": "2026-09-01" + }, + { + "request_id": "f3288efd-b1b6-492f-9a4a-8b316e35fe71", + "action": "update", + "summary": "#V27DZ1: summary → Ward Flow: role screens intermittent Playwright strict-mode violation, a role screen's own test id resolves to 2 elements", + "created_at": "2026-08-31" + }, { "request_id": "f6cffbd6-98e9-4233-8c2f-315e5fcc722a", "action": "done", "summary": "#F5EYAJ: Created connection and permission unavailable fallback components, wired routing in app/caring-contacts/error.tsx, and dynamically supplied blockReason in workspace-overlays.tsx", "created_at": "2026-08-27" }, + { + "request_id": "fa3a637d-a2b9-44b9-bdb9-eb23c912505a", + "action": "cancel", + "summary": "Cancel request c3cf6c59-298b-424e-90bf-9e2cdd9c559e: Superseded by an earlier pending done request already on main for the same issue; keep main claim and drop this PR duplicate to unblock check:docs-links mutation conflicts.", + "created_at": "2026-08-31" + }, + { + "request_id": "fbfb32c2-6ca8-4504-adb5-b7647d1d9174", + "action": "cancel", + "summary": "Cancel request b1630592-46f5-4e1a-aa03-97aa30e516af: Target issue is no longer in Open items on current main; cancel stale PR-bundled done request so check:docs-links can dry-apply the pending inbox.", + "created_at": "2026-08-31" + }, { "request_id": "fed7ba66-652e-4081-8845-3c3b70496507", "action": "done", "summary": "#ZW43ZT: Bypassed GlobalMockupSearchShell for /mockups/development in MockupsLayoutClient to eliminate duplicate dev main landmark collisions.", "created_at": "2026-08-27" + }, + { + "request_id": "ff7bcba4-5dee-44b7-9b4f-a1aefdd9ab85", + "action": "cancel", + "summary": "Cancel request d6e41550-1a6a-4f4f-aff5-d95004011942: Target issue is no longer in Open items on current main; cancel stale PR-bundled done request so check:docs-links can dry-apply the pending inbox.", + "created_at": "2026-08-31" } ] } diff --git a/data/repo-awareness-snapshot.json b/data/repo-awareness-snapshot.json index be9dcfb6b9..321c19f478 100644 --- a/data/repo-awareness-snapshot.json +++ b/data/repo-awareness-snapshot.json @@ -1,8 +1,8 @@ { "version": "repo-awareness-snapshot-v1", "captured_revision": { - "sha": "f340cd0b9052459a438ab8ae8d52de19c88dd55e", - "committed_at": "2026-08-29T12:26:43+08:00" + "sha": "90db76d9cd22b434b10739b23eec3dd6deb7a9ed", + "committed_at": "2026-09-01T21:40:44+08:00" }, "routes": { "modes": [ @@ -573,6 +573,11 @@ "file": "src/app/mockups/development/page.tsx", "area": "mockup" }, + { + "path": "/mockups/development/clinical-answer-failures", + "file": "src/app/mockups/development/clinical-answer-failures/page.tsx", + "area": "mockup" + }, { "path": "/mockups/development/clinical-trust", "file": "src/app/mockups/development/clinical-trust/page.tsx", @@ -1420,9 +1425,9 @@ ], "counts": { "modes": 15, - "pages": 198, + "pages": 199, "product_pages": 63, - "mockup_pages": 135, + "mockup_pages": 136, "redirects": 17, "api": 60 } @@ -1464,6 +1469,11 @@ "section": "agents", "catalogued": false }, + { + "path": "docs/agents/codex-reasoning-effort.md", + "section": "agents", + "catalogued": false + }, { "path": "docs/agents/codex-review-throttling.md", "section": "agents", @@ -2869,6 +2879,16 @@ "section": "root", "catalogued": true }, + { + "path": "docs/governance/privacy-closeout-2026-09-01.md", + "section": "governance", + "catalogued": true + }, + { + "path": "docs/governance/privacy-role-attestation-pack-2026-09-01.md", + "section": "governance", + "catalogued": true + }, { "path": "docs/ingestion-concurrency-fix-workorder.md", "section": "root", @@ -3314,6 +3334,11 @@ "section": "root", "catalogued": true }, + { + "path": "docs/services-mode-governance.md", + "section": "root", + "catalogued": false + }, { "path": "docs/site-map.md", "section": "root", @@ -3549,6 +3574,16 @@ "section": "superpowers", "catalogued": false }, + { + "path": "docs/superpowers/plans/2026-09-01-calculators-clinical-safety.md", + "section": "superpowers", + "catalogued": false + }, + { + "path": "docs/superpowers/plans/2026-09-01-services-safety-provenance.md", + "section": "superpowers", + "catalogued": false + }, { "path": "docs/superpowers/rag-upgrade/canonical/approval-matrix.md", "section": "superpowers", @@ -3834,6 +3869,16 @@ "section": "superpowers", "catalogued": false }, + { + "path": "docs/superpowers/specs/2026-09-01-calculators-clinical-safety.md", + "section": "superpowers", + "catalogued": false + }, + { + "path": "docs/superpowers/specs/2026-09-01-services-safety-provenance-design.md", + "section": "superpowers", + "catalogued": false + }, { "path": "docs/testing.md", "section": "root", @@ -4234,6 +4279,9 @@ { "name": "evidence" }, + { + "name": "governance" + }, { "name": "plans" }, @@ -4263,10 +4311,10 @@ } ], "counts": { - "documents": 554, - "catalogued": 109, - "uncatalogued": 445, - "sections": 20 + "documents": 562, + "catalogued": 111, + "uncatalogued": 451, + "sections": 21 } }, "test_health": { @@ -4278,6 +4326,86 @@ }, "review_state": { "records": [ + { + "date": "2026-09-01", + "ref": "PR-2470", + "head": "3e7fc9b44ae5dca9d6b353585300036640adde88", + "scope": "Run PR sweep: merge main after Node 26 CI repair", + "outcome": "Merged current main after resolving the AGENTS guidance and generated repository-snapshot conflicts; retained the Node 26 CI repair; no review threads remain open.", + "checks": "npm ci; 41 focused runtime tests; lint; typecheck; lockfile parity; docs and policy checks; merge-tree conflict review; regenerated repository snapshot; full unit suite started but stopped before completion when it attempted a protected production Supabase request." + }, + { + "date": "2026-09-01", + "ref": "PR-2470", + "head": "7cd20b1ea121d9cf9f66efa04971f8fefee63f05", + "scope": "Run PR sweep: Node 26 container CI compatibility", + "outcome": "Fixed the Node 26 Docker-image versus Node 24 engine-contract mismatch; no review threads were open.", + "checks": "npm ci; 41 focused runtime, hook, Cloud, and worker tests passed; lint and typecheck passed; offline static, docs, workflow, lockfile, and ledger gates passed; full unit suite stopped before completion after a protected Supabase call; no provider-backed checks completed." + }, + { + "date": "2026-09-01", + "ref": "PR-2470", + "head": "ddc42a13bf3ecab29e15f511d0165db266eafd46", + "scope": "Run PR sweep: final Node 26 lockfile alignment", + "outcome": "Aligned package-lock runtime metadata with the Node 26 engine contract after the container CI repair; no review threads were open.", + "checks": "npm ci; 41 focused runtime, hook, Cloud, and worker tests passed; lint and typecheck passed; offline static, docs, workflow, lockfile, and ledger gates passed; full unit suite stopped before completion after a protected Supabase call; no provider-backed checks completed." + }, + { + "date": "2026-08-31", + "ref": "codex/answer-surface-compact-20260830", + "head": "705561dd1f9b1ac8f72c7a4858e3819b9ee5a40e", + "scope": "compact answer source safety and library UI", + "outcome": "No P0-P2 findings; compact source status, answer utilities, safety row, and library placement ready for PR", + "checks": "13 focused DOM tests passed; targeted Chromium 1/1 passed; lint and typecheck passed; build passed 1998 routes; design contracts passed; production-readiness CI READY; offline RAG 628/628 and adversarial 25/25 passed; full unit 11656 passed with 6 unrelated Windows Claude Cloud harness exit-127 failures; no provider-backed checks run" + }, + { + "date": "2026-08-31", + "ref": "codex/chat-smart-natural-mode-search-pr-2480-landed-verify", + "head": "f64762dd6b16bd90e94773b1dc892760180747e1", + "scope": "Smart natural-language catalogue search separation", + "outcome": "no P0/P1 findings; ready for PR", + "checks": "208 focused tests; 53 post-merge tests; production Chromium 4/4; typecheck; lint; docs links; diff check" + }, + { + "date": "2026-08-31", + "ref": "core_rag_security_hub", + "head": "d39db0ec72583680f666078a4a366083bf37fcf9", + "scope": "PR Group 2: Core RAG, Security, DB Functions & Developer Hub (#DHAR98, #0JJ4RD, #2X46NY, #S4R2W3, #N8B176, #VKH7N1, #CJCH2E, #FDST2Q, #XKS6FD, #ZW43ZT, #G693BZ)", + "outcome": "PASS: All 11 Group 2 tasks verified clean against database guards, RAG quality gates, DOM component tests, full typecheck, and Prettier formatting with zero regressions.", + "checks": "npm run check:migration-role, npm run check:function-grants, npm run check:owner-scope, npm run typecheck, vitest run tests/developer-hub-components.dom.test.tsx tests/developer-ingestion-page.dom.test.tsx tests/documents-is-public.test.ts tests/supabase-schema.test.ts tests/extractive-answer-formatting.test.ts (247/247 passed), npm run format" + }, + { + "date": "2026-08-31", + "ref": "gemini/pr-group-3-ui-a11y-caring-contacts-ward-flow (PR #2479)", + "head": "8ff3f26c6e4f5302c1883f940f4cc1b22fbeec5c", + "scope": "Run PR sweep: existing unresolved review threads", + "outcome": "0 → 2 threads resolved: useful-actions disclosure semantics, workspace route registration assertion", + "checks": "Focused Vitest 18/18; typecheck passed; no provider-backed checks run" + }, + { + "date": "2026-08-30", + "ref": "codex/smart-natural-search-current-main", + "head": "7190c2ccd87dfc25e49e488b22705fb6b7b60931", + "scope": "Smart natural search CI reconciliation exact-tree review", + "outcome": "No open P0/P1/P2 findings; maintainability blocker fixed by cohesive extraction", + "checks": "maintainability budgets; 86 focused Vitest; provider-free Chromium Smart suite; lint; typecheck; formatting; diff check" + }, + { + "date": "2026-08-30", + "ref": "codex/smart-natural-search-current-main", + "head": "8de6dae0e541166dad23523ca3a4e2340eb6c217", + "scope": "Smart natural search exact-tree implementation and review", + "outcome": "P2 findings fixed; no open P0/P1/P2 findings", + "checks": "105 focused contracts; enabled Chromium 6 passed/1 skipped; default-off Chromium 1 passed; production build passed; PR-local 11616 passed with 6 exact-main Windows Bash failures" + }, + { + "date": "2026-08-30", + "ref": "codex/smart-natural-search-current-main", + "head": "b762e1363b9bbb993f0f74a9e00a2c2ccb1f56be", + "scope": "Smart natural search final CI test correction review", + "outcome": "No open P0/P1/P2 findings; stale extracted-owner tests corrected", + "checks": "6 focused Vitest; DSM production Chromium; formatting; diff check" + }, { "date": "2026-08-29", "ref": "PR-2454", @@ -4286,6 +4414,14 @@ "outcome": "Confirmed PR-specific repo-awareness drift and two P2 documentation findings; corrected the generated snapshot, Windows LCP delta, and Linux-only qualification. Main coverage/browser failures did not reproduce on the PR head.", "checks": "PR/base Actions logs; repo-awareness check; outstanding-issues check; docs links; targeted Prettier; arithmetic verification" }, + { + "date": "2026-08-29", + "ref": "PR-2457", + "head": "0e5631ba6a98b22ef1ddf5557e6ce38b824e78b2", + "scope": "PR #2457 review-and-fix", + "outcome": "Fixed repository breakpoint coverage, conservative same-band Tailwind cascade handling, and the stale compact-disclosure UI assertion; no other material PR-introduced defects confirmed.", + "checks": "57 focused Vitest tests passed; design-system contract/adoption/sync passed; typecheck passed; local Playwright unavailable because pinned Chromium is not installed, with exact-head Actions reproducing the corrected 42px assertion." + }, { "date": "2026-08-27", "ref": "2398", @@ -4406,6 +4542,14 @@ "outcome": "mergeable; snapshot regenerated; 5 premature inbox done requests removed; dev-drive-cache fails closed; Medication pin /medications", "checks": "vitest session-start-hook+sidebar-production 18/18; check:dev-drive-cache; check:outstanding-issues-snapshot" }, + { + "date": "2026-08-27", + "ref": "ds-hazard-1-2-sweep", + "head": "19ee085b1cc952e8c40e893fb2fce99538415259", + "scope": "design-system", + "outcome": "Findings — 0 P0/P1; 4 structural P2 (disabled-send border jump, hero leading on composer, --leading-prose on chrome, stale conventions.md per-step type scale); residuals on specifier focus ring and leftover Lucide sizes", + "checks": "read-only origin/main...HEAD token/UI/docs inspection; ledger lookup NOT REVIEWED; no contract gate, browser, or providers" + }, { "date": "2026-08-27", "ref": "ds-hazard-1-2-sweep (PR #2416)", @@ -25456,8 +25600,8 @@ } ], "counts": { - "records": 2647, - "refs": 1615 + "records": 2659, + "refs": 1623 } } } diff --git a/docs/README.md b/docs/README.md index 1de9fdfd0e..0acba0ec21 100644 --- a/docs/README.md +++ b/docs/README.md @@ -80,6 +80,9 @@ npm run docs:check-links - [rag-injection-threat-model.md](rag-injection-threat-model.md) — prompt-injection threat model - [privacy-impact-assessment.md](privacy-impact-assessment.md) — PIA findings and launch blockers - [openai-cross-border-basis.md](openai-cross-border-basis.md) — cross-border data-processing basis +- [governance/privacy-readiness.v1.json](governance/privacy-readiness.v1.json) — authoritative machine-checkable privacy readiness status +- [governance/privacy-closeout-2026-09-01.md](governance/privacy-closeout-2026-09-01.md) — current provider, retention, legal, notice, and clinical closeout evidence +- [governance/privacy-role-attestation-pack-2026-09-01.md](governance/privacy-role-attestation-pack-2026-09-01.md) — evidence and role decisions for eight requirements; two owner approvals are complete and six remain - [production-readiness-checklist.md](production-readiness-checklist.md) — release readiness criteria - [samd-classification-medication-considerations.md](samd-classification-medication-considerations.md) — SaMD classification and medication considerations diff --git a/docs/agents-guide.md b/docs/agents-guide.md index 0f9bbdb616..072ef441a1 100644 --- a/docs/agents-guide.md +++ b/docs/agents-guide.md @@ -17,7 +17,7 @@ duplicate those rules, so it cannot drift from them. ## Human quickstart -- Node 24.x / npm 11.x are hard requirements (`engine-strict`); the app is +- Node 26.x / npm 11.x are hard requirements (`engine-strict`); the app is Next.js 16 + Supabase + OpenAI. - Copy `.env.example` to `.env.local` and fill in values (never commit secrets). Without Supabase/OpenAI values the app runs in demo mode on a diff --git a/docs/agents/codex-desktop-worktree-setup.md b/docs/agents/codex-desktop-worktree-setup.md index d89256441c..c3b90fd4ce 100644 --- a/docs/agents/codex-desktop-worktree-setup.md +++ b/docs/agents/codex-desktop-worktree-setup.md @@ -5,7 +5,7 @@ # Codex Desktop worktree setup - The Windows Codex Desktop environment setup command is `node scripts/setup-codex-worktree.mjs`. - It must work before `node_modules` exists, validate Node 24/npm 11, reuse only a complete + It must work before `node_modules` exists, validate Node 26/npm 11, reuse only a complete byte-identical local installation, and otherwise run the locked npm install. - Never configure Windows Desktop worktrees to run `bash scripts/setup-codex-cloud.sh`. That script is Linux/Cloud-only; Windows launches it through WSL outside the worktree and cannot provision the @@ -13,6 +13,6 @@ - `.codex/environments/environment.toml` is autogenerated and ignored. Change the Database environment through Codex settings, then verify the effective command with the generated file and `node scripts/setup-codex-worktree.mjs --dry-run`. -- **Web container runtime requirements:** Package manifests enforce strict Node 24 (`>=24.15.0 <25`) and npm 11 engines. If a web container environment boots with Node 22 on `PATH`, do not drop engine-strict; export `/opt/node24/bin` at the front of `PATH` or install Node 24 to satisfy repository engine contracts before running `npm ci`. +- **Web container runtime requirements:** Package manifests enforce strict Node 26 (`>=26.0.0 <27`) and npm 11 engines. If a web container environment boots with Node 22 on `PATH`, do not drop engine-strict; export `/opt/node26/bin` at the front of `PATH` or install Node 26 to satisfy repository engine contracts before running `npm ci`. diff --git a/docs/agents/codex-reasoning-effort.md b/docs/agents/codex-reasoning-effort.md new file mode 100644 index 0000000000..21c8fa2438 --- /dev/null +++ b/docs/agents/codex-reasoning-effort.md @@ -0,0 +1,62 @@ +# Codex Reasoning Effort Calibration + + + +# Reasoning effort calibration + +**Repository baseline.** Use `gpt-5.6-sol` with `high` reasoning effort unless the user explicitly +chooses another supported model or effort. `.codex/config.toml` records this default for trusted +Codex clients that honor repository configuration; a task-level selection can override it. + +**Cloud xhigh gate.** A running Cloud task cannot raise its own reasoning effort. Before substantive +inspection, planning, tool use, or edits, classify the request against the table and the risk rules +below. If `xhigh` is required and the prompt does not contain the exact marker `[xhigh-confirmed]`, +stop and ask the user to select `xhigh` in the Cloud reasoning control, then resubmit the same request +with `[xhigh-confirmed]`. Do not begin the work at `high`, and do not claim the runtime changed. When +the marker is present, treat it as the user's confirmation that `xhigh` was selected and proceed. +Requests classified as `high` or lower proceed without this gate. + +Reasoning effort is a budget in the same way verification is a budget, and it is misspent the same +way — by defaulting to the maximum instead of matching the spend to the risk. **Scale effort to how +expensive the mistake is to undo (irreversibility × branching factor), never to the phase label.** +"Plan high, build lower" is a good default, not a rule; it is wrong often enough that it must be +chosen deliberately rather than assumed. + +**Why it is a good default.** Planning errors compound and implementation errors stay local: a wrong +approach throws away the build, a wrong identifier is one edit. A plan is also a few thousand output +tokens against a build's many long turns, so effort is cheapest exactly where it has the most leverage. + +**The mechanism that makes it work — do not skip this part.** A lower-effort build only succeeds +against a plan concrete enough to execute: named files, named symbols, ordered steps, and the gate +that will prove it. Downgrading the build against a vague plan does not save effort, it relocates the +thinking into the expensive phase. If the plan cannot name those things, the build is not eligible for +the downgrade. + +| Situation | Plan | Build | +| ---------------------------------------------------------------------------------------------- | ----------- | ----------- | +| Architecture, Supabase migrations/RLS, RAG ranking surfaces, auth/privacy, ingestion contracts | xhigh | high | +| Ordinary feature or UI work with a clear shape | high | medium–high | +| Mechanical and fully specified — ledger append, docs edit, rename (not dependency maintenance) | low or skip | medium | +| Debugging an unknown failure | low | high | + +**Where the default inverts and the build needs more than the plan.** These are plan-light and +execution-heavy; treating them as plan-heavy spends the budget in the wrong place: + +- **Debugging.** The plan is "find why X fails." The real reasoning is hypothesis-forming over local + runtime, logs, and repro state during the build — hosted providers still need explicit confirmation. +- **Constraint-dense implementation.** A one-sentence plan whose edit must simultaneously satisfy + button wiring, design tokens, the one-composer rule, unlayered CSS, and the tap-target and + phone-chrome contracts. Holding all of it at once is the hard part, not deciding what to do. +- **Areas where training data is stale.** Next 16 is the standing case. Effort does not repair a wrong + prior — reading `node_modules/next/dist/docs/` does. Raising effort instead of reading is itself the + failure mode. + +**Constrain xhigh planning output, not just its effort.** Extra-high planning over-produces: +alternatives, contingency branches, and surveys that are never used, paid for twice — once generating +and once reading. Ask for the chosen approach, the files, and the gate, not a survey. + +**State the split before non-trivial planning work.** One line before starting: plan effort, build +effort, and the risk that justifies them. It is cheap, it makes a wrong allocation visible while it is +still free to change, and it stops the blanket default from being applied silently. + + diff --git a/docs/agents/cursor-cloud.md b/docs/agents/cursor-cloud.md index f0e48fe4f4..683ef91660 100644 --- a/docs/agents/cursor-cloud.md +++ b/docs/agents/cursor-cloud.md @@ -7,7 +7,7 @@ Durable notes for Cloud Agents. Standard commands live in `README.md` and `package.json`; only non-obvious caveats are captured here. - Context7 peer-library docs habit (and the Next 16 local-docs carve-out) lives in `docs/agents-guide.md`. Project MCP is local `@upstash/context7-mcp@3.2.5` with `CONTEXT7_API_KEY` from env/Secrets. If the host-injected Context7 MCP returns quota exceeded, use `npx ctx7 library|docs …` with the same secret — do not invent peer APIs from training data. -- Runtime: the app hard-requires Node >=24.15.0 <25 / npm 11.x (`engine-strict`; the preinstall and runtime gates enforce the minor floor, while `scripts/dev-free-port.mjs` rejects other majors). A compatible Node 24 is installed via nvm and symlinked into `/usr/local/cargo/bin` (first entry in `PATH`) so `node`/`npm` resolve to it in every shell. If a shell ever resolves `/exec-daemon/node` (v22) instead, prepend the installed nvm Node 24 bin to `PATH` (for example `"$HOME/.nvm/versions/node/v24.18.1/bin"`; run `ls "$HOME/.nvm/versions/node"` to confirm the exact patch version). +- Runtime: the app hard-requires Node >=26.0.0 <27 / npm 11.x (`engine-strict`; the preinstall and runtime gates enforce the supported range, while `scripts/dev-free-port.mjs` rejects other majors). A compatible Node 26 is installed via nvm and symlinked into `/usr/local/cargo/bin` (first entry in `PATH`) so `node`/`npm` resolve to it in every shell. If a shell ever resolves `/exec-daemon/node` (v22) instead, prepend the installed nvm Node 26 bin to `PATH` (for example `"$HOME/.nvm/versions/node/v26.8.1/bin"`; run `ls "$HOME/.nvm/versions/node"` to confirm the exact patch version). - Live vs demo mode: the app auto-detects. When the Supabase + OpenAI env vars below are present (set them as Cloud Agent **Secrets** so they inject into `.env.local`/`process.env`), `isDemoMode()` (`src/lib/env.ts`) is false and the app runs against the live `Clinical KB Database` project (~2000 indexed docs) with OpenAI answer generation. When they are absent, dev auto-falls back to demo mode using the synthetic corpus in `src/lib/demo-data.ts` / `public/demo-documents/`. Required for live mode: `NEXT_PUBLIC_SUPABASE_URL`, `SUPABASE_PROJECT_REF`, `SUPABASE_PROJECT_NAME`, `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY` (`sb_publishable_…`), `SUPABASE_SERVICE_ROLE_KEY` (accepts the `sb_secret_…` secret key), `OPENAI_API_KEY`. Keep `RAG_PROVIDER_MODE=auto` so OpenAI is used with graceful source-only fallback. `E2E_USER_EMAIL`/`E2E_USER_PASSWORD` power CI env-check and Playwright. - Live-mode caveat: `RAG_PROVIDER_MODE=auto` attempts OpenAI (fast → strong route); if generation fails the built-in quality gates it silently degrades to a deterministic "Source-only" answer that still cites real documents — this is expected, not a failure. The header sign-in UI exposes magic-link + OAuth only (no password field), but the `/api/answer` + retrieval flow works server-side without a browser session. - What still won't run in this VM even with secrets: `npm run worker` also needs the Python OCR stack (`worker/python/requirements.txt`) and heavy parsing deps; Supabase edge functions need Deno v2.x + deployment. `verify:release` additionally runs governance/eval gates. Treat missing-secret failures of `check:supabase-project`/`verify:release` in demo mode as expected, not regressions. diff --git a/docs/audit/live-drift-forensics-2026-08.md b/docs/audit/live-drift-forensics-2026-08.md index 4426d98449..a4b533f518 100644 --- a/docs/audit/live-drift-forensics-2026-08.md +++ b/docs/audit/live-drift-forensics-2026-08.md @@ -2273,7 +2273,7 @@ is worse than the red job it replaces. When neither path works, the error names _Superseded by the 2026-08-20 window section below: the migration was already applied on production before the window opened, and D4 is no longer treated as OFF. Kept as the pre-window record._ -The migration is **not deployed**. D4 is OFF, so merging does not apply it, and until it is applied +The migration is **not deployed**. D4 is OFF _(superseded: see §D4 — SETTLED 2026-08-21; deploy-on-merge is ON)_, so merging does not apply it, and until it is applied `check:drift` will report `migration_history_versions` as a missing function — i.e. merging before the window trades one red for another. **Deploy from the branch first, then merge**, which is the order Phase 4 used (§Phase 4 completion). Staging needs the same migration by the Phase 2 method to hold the diff --git a/docs/branch-review-records/08497f19070d98a449377c03d2cde65dcea10da02d09da32585949a3830eac3b.record.md b/docs/branch-review-records/08497f19070d98a449377c03d2cde65dcea10da02d09da32585949a3830eac3b.record.md new file mode 100644 index 0000000000..af3103cb0e --- /dev/null +++ b/docs/branch-review-records/08497f19070d98a449377c03d2cde65dcea10da02d09da32585949a3830eac3b.record.md @@ -0,0 +1 @@ +| 2026-09-01 | PR-2470 | 7cd20b1ea121d9cf9f66efa04971f8fefee63f05 | Run PR sweep: Node 26 container CI compatibility | Fixed the Node 26 Docker-image versus Node 24 engine-contract mismatch; no review threads were open. | npm ci; 41 focused runtime, hook, Cloud, and worker tests passed; lint and typecheck passed; offline static, docs, workflow, lockfile, and ledger gates passed; full unit suite stopped before completion after a protected Supabase call; no provider-backed checks completed. | diff --git a/docs/branch-review-records/2eb7a598118b7a73fb36384e1fc6ca3ca986eeea19ff40c7cb2b195dfa9b9e67.record.md b/docs/branch-review-records/2eb7a598118b7a73fb36384e1fc6ca3ca986eeea19ff40c7cb2b195dfa9b9e67.record.md new file mode 100644 index 0000000000..3b2dc7fb2d --- /dev/null +++ b/docs/branch-review-records/2eb7a598118b7a73fb36384e1fc6ca3ca986eeea19ff40c7cb2b195dfa9b9e67.record.md @@ -0,0 +1 @@ +| 2026-09-01 | PR-2470 | 3e7fc9b44ae5dca9d6b353585300036640adde88 | Run PR sweep: merge main after Node 26 CI repair | Merged current main after resolving the AGENTS guidance and generated repository-snapshot conflicts; retained the Node 26 CI repair; no review threads remain open. | npm ci; 41 focused runtime tests; lint; typecheck; lockfile parity; docs and policy checks; merge-tree conflict review; regenerated repository snapshot; full unit suite started but stopped before completion when it attempted a protected production Supabase request. | diff --git a/docs/branch-review-records/38c77f1167740922262bdf237802d21945497a9d612a51685a62002989e57c7d.record.md b/docs/branch-review-records/38c77f1167740922262bdf237802d21945497a9d612a51685a62002989e57c7d.record.md new file mode 100644 index 0000000000..1a8575d984 --- /dev/null +++ b/docs/branch-review-records/38c77f1167740922262bdf237802d21945497a9d612a51685a62002989e57c7d.record.md @@ -0,0 +1 @@ +| 2026-08-31 | gemini/pr-group-3-ui-a11y-caring-contacts-ward-flow (PR #2479) | 8ff3f26c6e4f5302c1883f940f4cc1b22fbeec5c | Run PR sweep: existing unresolved review threads | 0 → 2 threads resolved: useful-actions disclosure semantics, workspace route registration assertion | Focused Vitest 18/18; typecheck passed; no provider-backed checks run | diff --git a/docs/branch-review-records/46928e14c627bd4e8be016b6d181c434e996a4a21e5d788b2862b12296656a21.record.md b/docs/branch-review-records/46928e14c627bd4e8be016b6d181c434e996a4a21e5d788b2862b12296656a21.record.md new file mode 100644 index 0000000000..27a7bb7ab7 --- /dev/null +++ b/docs/branch-review-records/46928e14c627bd4e8be016b6d181c434e996a4a21e5d788b2862b12296656a21.record.md @@ -0,0 +1 @@ +| 2026-08-31 | codex/answer-surface-compact-20260830 | 705561dd1f9b1ac8f72c7a4858e3819b9ee5a40e | compact answer source safety and library UI | No P0-P2 findings; compact source status, answer utilities, safety row, and library placement ready for PR | 13 focused DOM tests passed; targeted Chromium 1/1 passed; lint and typecheck passed; build passed 1998 routes; design contracts passed; production-readiness CI READY; offline RAG 628/628 and adversarial 25/25 passed; full unit 11656 passed with 6 unrelated Windows Claude Cloud harness exit-127 failures; no provider-backed checks run | diff --git a/docs/branch-review-records/59910cd5286d4386f2d1b58327c7fd4deec2fff472836a8e5e7a8d91cee14d82.record.md b/docs/branch-review-records/59910cd5286d4386f2d1b58327c7fd4deec2fff472836a8e5e7a8d91cee14d82.record.md new file mode 100644 index 0000000000..74fa5d6cb1 --- /dev/null +++ b/docs/branch-review-records/59910cd5286d4386f2d1b58327c7fd4deec2fff472836a8e5e7a8d91cee14d82.record.md @@ -0,0 +1 @@ +| 2026-08-31 | codex/chat-smart-natural-mode-search-pr-2480-landed-verify | f64762dd6b16bd90e94773b1dc892760180747e1 | Smart natural-language catalogue search separation | no P0/P1 findings; ready for PR | 208 focused tests; 53 post-merge tests; production Chromium 4/4; typecheck; lint; docs links; diff check | diff --git a/docs/branch-review-records/9252ad9dfa57000de74b55496e6c60bbdde1ca84db9e2bfa7faf784cdcf7d98d.record.md b/docs/branch-review-records/9252ad9dfa57000de74b55496e6c60bbdde1ca84db9e2bfa7faf784cdcf7d98d.record.md new file mode 100644 index 0000000000..220038ea82 --- /dev/null +++ b/docs/branch-review-records/9252ad9dfa57000de74b55496e6c60bbdde1ca84db9e2bfa7faf784cdcf7d98d.record.md @@ -0,0 +1 @@ +| 2026-08-27 | ds-hazard-1-2-sweep | 19ee085b1cc952e8c40e893fb2fce99538415259 | design-system | Findings — 0 P0/P1; 4 structural P2 (disabled-send border jump, hero leading on composer, --leading-prose on chrome, stale conventions.md per-step type scale); residuals on specifier focus ring and leftover Lucide sizes | read-only origin/main...HEAD token/UI/docs inspection; ledger lookup NOT REVIEWED; no contract gate, browser, or providers | diff --git a/docs/branch-review-records/e328623e22f9d73a0f3f86b200c9e1f40eb3d73e503acc4fc64fe26740fe4dc5.record.md b/docs/branch-review-records/e328623e22f9d73a0f3f86b200c9e1f40eb3d73e503acc4fc64fe26740fe4dc5.record.md new file mode 100644 index 0000000000..ff05175815 --- /dev/null +++ b/docs/branch-review-records/e328623e22f9d73a0f3f86b200c9e1f40eb3d73e503acc4fc64fe26740fe4dc5.record.md @@ -0,0 +1 @@ +| 2026-09-01 | PR-2470 | ddc42a13bf3ecab29e15f511d0165db266eafd46 | Run PR sweep: final Node 26 lockfile alignment | Aligned package-lock runtime metadata with the Node 26 engine contract after the container CI repair; no review threads were open. | npm ci; 41 focused runtime, hook, Cloud, and worker tests passed; lint and typecheck passed; offline static, docs, workflow, lockfile, and ledger gates passed; full unit suite stopped before completion after a protected Supabase call; no provider-backed checks completed. | diff --git a/docs/branch-review-records/eb3b45f1c916b2533496cd8afa3563a413625a0b67ddd31360d242f48cb9a2b3.record.md b/docs/branch-review-records/eb3b45f1c916b2533496cd8afa3563a413625a0b67ddd31360d242f48cb9a2b3.record.md new file mode 100644 index 0000000000..5107066480 --- /dev/null +++ b/docs/branch-review-records/eb3b45f1c916b2533496cd8afa3563a413625a0b67ddd31360d242f48cb9a2b3.record.md @@ -0,0 +1 @@ +| 2026-08-31 | core_rag_security_hub | d39db0ec72583680f666078a4a366083bf37fcf9 | PR Group 2: Core RAG, Security, DB Functions & Developer Hub (#DHAR98, #0JJ4RD, #2X46NY, #S4R2W3, #N8B176, #VKH7N1, #CJCH2E, #FDST2Q, #XKS6FD, #ZW43ZT, #G693BZ) | PASS: All 11 Group 2 tasks verified clean against database guards, RAG quality gates, DOM component tests, full typecheck, and Prettier formatting with zero regressions. | npm run check:migration-role, npm run check:function-grants, npm run check:owner-scope, npm run typecheck, vitest run tests/developer-hub-components.dom.test.tsx tests/developer-ingestion-page.dom.test.tsx tests/documents-is-public.test.ts tests/supabase-schema.test.ts tests/extractive-answer-formatting.test.ts (247/247 passed), npm run format | diff --git a/docs/caring-contacts/interaction-matrix.md b/docs/caring-contacts/interaction-matrix.md index 0da6a54f35..fd7a7edc39 100644 --- a/docs/caring-contacts/interaction-matrix.md +++ b/docs/caring-contacts/interaction-matrix.md @@ -29,6 +29,11 @@ The source of truth is `completionOverlayDefinitions` in `src/components/caring- | `team-switcher` | Header active-team context | Bottom sheet | Dialog | Yes | Escape, backdrop, close | | `draft-version-conflict` | Draft/version guard | Full-screen stage | Dialog | No | Escape, backdrop, close | +## Dismissal model + +- **Standard dismissals (`Escape, backdrop, close`):** Standard modal overlays and inspection drawers allow clinician dismissal via pressing the `Escape` key, clicking the backdrop, or activating the close/cancel button. Closing restores focus to the triggering element. +- **System gate dismissals (`Recovery action only`):** Non-dismissible blocking gates (such as `session-expiry` and `offline-banner`) deliberately disallow passive dismissal (`Escape`, backdrop tap, or close icon). They persist until the explicit recovery action is completed (e.g. re-authenticating at the session gate or regaining network connectivity). + ## Feedback contract - Success: announce the synthetic in-memory outcome and update the visible plan/audit summary. diff --git a/docs/claude-cloud.md b/docs/claude-cloud.md index 2be0a1313d..7d91b436e6 100644 --- a/docs/claude-cloud.md +++ b/docs/claude-cloud.md @@ -15,7 +15,7 @@ provisioners. | Order | Command | Timeout | Does | | ----- | ----------------------------------------- | ------- | -------------------------------------------------- | -| 1 | `.claude/hooks/session-start.sh` | 900s | Installs Node 24 to `$HOME/.node24`, then `npm ci` | +| 1 | `.claude/hooks/session-start.sh` | 900s | Installs Node 26 to `$HOME/.node26`, then `npm ci` | | 2 | `scripts/setup-claude-cloud.sh --session` | 300s | Everything below | Both no-op instantly unless `CLAUDE_CODE_REMOTE=true`, so a local Windows session is untouched. diff --git a/docs/clinical-governance.md b/docs/clinical-governance.md index 044c7509b4..12582a694b 100644 --- a/docs/clinical-governance.md +++ b/docs/clinical-governance.md @@ -21,6 +21,14 @@ PsychSift is currently a source-backed clinical reference prototype. Before prod - Confirm OpenAI and Supabase data-processing arrangements are acceptable for the intended clinical setting. - Define audit requirements for uploads, document access, user queries, generated answers, copied drafts, and source opening. +The status authority is [`governance/privacy-readiness.v1.json`](governance/privacy-readiness.v1.json), +with the current evidence summary in +[`governance/privacy-closeout-2026-09-01.md`](governance/privacy-closeout-2026-09-01.md). As of +2026-09-01, OpenAI API sharing and call logging are disabled and optional hosted tools are disabled; +OpenAI has acknowledged the ZDR request, but ZDR, DPA, APP 8, APP 1/5 notice, and clinical +PHI-minimisation approvals remain release blockers. Do not infer approval from request submission or +from the public `/privacy` transparency page. + ## Clinical Use Rules - Generated answers and copied drafts must be verified against linked source text, local policy, and patient context before use. @@ -60,11 +68,12 @@ Authority must come from registered publisher codes or compatible canonical publ ## Mode-aware Clinical Ask governance -Clinical Ask remains dormant by default. When `CLINICAL_ASK_ENABLED=true`, the existing shared composer exposes a -server-gated Smart entry point for seven exhaustive clinician-reference modes: Services, Forms, Differentials, -Formulation, DSM-5 Diagnosis, Specifiers, and Therapy. Unsupported or denylisted modes and all deployments with the -flag off retain deterministic search/filter behaviour and show no Smart promise. There is no microphone control or -separate Ask rail. Every Smart request uses the same deterministic Evidence Ladder: local +Clinical Ask remains dormant by default and is separate from Smart mode search. The shared mode composer never +routes a natural-language query to Clinical Ask: Services, Forms, Differentials, Formulation, DSM-5 Diagnosis, +Specifiers, and Therapy interpret natural language locally and show their ordinary deterministic catalogue results. +`CLINICAL_ASK_ENABLED` governs only the dormant answer workflow; it does not enable, disable, or alter Smart search. +There is no microphone control or separate Ask rail. If Clinical Ask is exposed through a dedicated governed-answer +surface in future, every request must use the same deterministic Evidence Ladder: local Catalogue first, authorised owner-scoped Indexed evidence second, and an allowlisted External Authority only when there is a deterministic evidence gap, unresolved conflict, stale material, or a `needs_review` source. An unsupported conclusion is rendered as an Evidence Gap; source conflict and review state remain visible, and clinically material @@ -88,5 +97,6 @@ protected-staging canary, contractual, or physical-device evidence. An `answered` stream payload fails closed unless it contains governed evidence and every visible lead, section, and conflict claim references evidence present in that payload. An Evidence Gap may still carry zero or partial evidence. -Production activation remains separately gated by named human clinical and contractual/privacy approval plus physical -iPhone Safari and installed-PWA acceptance of the enabled Smart path. +Clinical Ask production activation remains separately gated by named human clinical and contractual/privacy approval +plus physical iPhone Safari and installed-PWA acceptance. These answer-workflow gates do not block provider-free Smart +catalogue search. diff --git a/docs/codebase-index.md b/docs/codebase-index.md index a9829195ec..dd5258dd1e 100644 --- a/docs/codebase-index.md +++ b/docs/codebase-index.md @@ -188,6 +188,7 @@ domain-extracted directory; imported as `@/lib/rag/rag*`). Other modules below r | Module | Role | | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `differentials.ts`, `forms.ts`, `services.ts`, `registry-records.ts` | Shared catalogue content with optional owner overrides | +| `services-canonical-data/` | Generated, partitioned canonical WA Services source records, carrying safety, provenance, availability, and review metadata for the governed overlay | | `mha-act-sections.ts` | Mental Health Act 2014 (WA) section summaries shared across forms; `actSectionsForCue` resolves a form's `sourceFacts.sectionCue` and withholds the whole list until every cited section has a summary; `drafted` entries render with an awaiting-clinical-review note, `reviewed` ones name their reviewer (`docs/wiring-conventions.md`) | | `dictionary-data.ts`, `dictionary.ts` | Governed terminology, sources, topics, aliases, filters; `dictionaryCatalogue` is the one selector behind the merged Terms surface | | `dsm.ts` | Local DSM diagnosis catalogue and comparison helpers | @@ -704,7 +705,7 @@ One shared composer (`master-search-header.tsx`) serves every mode. Placement: | File | Role | | ------------------------------------------ | --------------------------------------------------------- | -| `package.json` | Scripts, deps, Node 24 / npm 11 | +| `package.json` | Scripts, deps, Node 26 / npm 11 | | `.env.example` | Full env template | | `next.config.ts` | CSP, security headers, build config | | `tsconfig.json` | Strict TS; excludes `supabase/functions/**` | diff --git a/docs/codex-cloud.md b/docs/codex-cloud.md index d8f4cee772..71ef422f09 100644 --- a/docs/codex-cloud.md +++ b/docs/codex-cloud.md @@ -4,7 +4,7 @@ > Windows uses `npm run setup:codex-worktree`; pointing Desktop at this Cloud > script starts WSL outside the Windows worktree and cannot provision it. -This repository supports reproducible Codex Cloud work with Node 24, npm 11, locked +This repository supports reproducible Codex Cloud work with Node 26, npm 11, locked development dependencies, Deno 2, Python/OCR tooling, and the Chromium, Firefox, and WebKit Playwright browser matrix. The repository setup can prepare and validate the container. It cannot grant GitHub installation permissions, workspace RBAC, agent-network diff --git a/docs/codex-prompt-playbook.md b/docs/codex-prompt-playbook.md index d5972a75d7..be2838b1fb 100644 --- a/docs/codex-prompt-playbook.md +++ b/docs/codex-prompt-playbook.md @@ -7,7 +7,7 @@ Before using any prompt, keep these project defaults in mind: - Start from `AGENTS.md`, `README.md`, `package.json`, and current `git status`. - Preserve unrelated staged, unstaged, and untracked work. -- Use Node 24.x and npm 11.x. Do not switch package managers. +- Use Node 26.x and npm 11.x. Do not switch package managers. - For Next.js source changes, read the relevant guide under `node_modules/next/dist/docs/` before editing. - Use `npm run ensure` before browser/UI work and use the URL it prints. diff --git a/docs/database-remediation-playbook.md b/docs/database-remediation-playbook.md index 3fec4c9381..5288e09b4a 100644 --- a/docs/database-remediation-playbook.md +++ b/docs/database-remediation-playbook.md @@ -115,8 +115,8 @@ against a pinned canonical definition, and only then marks a fail-fast guard mig close request for `#333` was queued after confirming a `#333` row existed — but the match was the **archived** row, and the invalid request threw `#333 is already archived`, red-lining `docs:check-links` for the whole branch. -- **Node 24 is mandatory** (`engine-strict`). A cloud container may ship Node 20/22; `npm ci` then - fails `EBADENGINE` and leaves `tsx` unresolvable, which fails `check:runtime`. Install Node 24 +- **Node 26 is mandatory** (`engine-strict`). A cloud container may ship Node 20/22; `npm ci` then + fails `EBADENGINE` and leaves `tsx` unresolvable, which fails `check:runtime`. Install Node 26 before anything else. - **Two known tooling failures are fixed — recognise the symptoms rather than re-diagnosing them.** `cancel request … targets missing pending request` was the ledger cancel-race (fixed in PR #1978; diff --git a/docs/deploy-corrector-public-titles.md b/docs/deploy-corrector-public-titles.md index af76b5000d..ea9f7d7b57 100644 --- a/docs/deploy-corrector-public-titles.md +++ b/docs/deploy-corrector-public-titles.md @@ -80,7 +80,7 @@ records the blocker; it does not itself authorize or implement the database clea ## Prerequisites -- The repo cloned locally, Node 24.x / npm 11.x. +- The repo cloned locally, Node 26.x / npm 11.x. - Your **production** secrets in `.env.local` (the same ones the live app uses): `NEXT_PUBLIC_SUPABASE_URL`, `SUPABASE_PROJECT_REF=sjrfecxgysukkwxsowpy`, `SUPABASE_PROJECT_NAME=Clinical KB Database`, `SUPABASE_SERVICE_ROLE_KEY`. diff --git a/docs/deployment-architecture.md b/docs/deployment-architecture.md index bc590ebc67..ca61c7899a 100644 --- a/docs/deployment-architecture.md +++ b/docs/deployment-architecture.md @@ -77,7 +77,7 @@ Railway's private network — see §2.1). Both Railway services deploy from ### Decision -Run the Next.js app as a **single long-lived container** (Node 24, image built +Run the Next.js app as a **single long-lived container** (Node 26, image built from `Dockerfile`) on **Railway**, pinned to the **Southeast Asia (Singapore)** region — the closest Railway region to the Supabase project's ap-southeast-2 (Sydney) home. Keep one warm replica (no scale-to-zero). @@ -206,7 +206,7 @@ comparable (~200 ms) from Singapore or Sydney and does not favour either host. ### Image contract (`Dockerfile`) -- `node:24-bookworm-slim` is pinned by multi-platform SHA-256 digest in a +- `node:26-bookworm-slim` is pinned by multi-platform SHA-256 digest in a shared `node-base` stage and used by every stage. Dependency installs use ordinary Docker layer caching. The shared Dockerfiles deliberately avoid BuildKit cache mounts because Railway requires hard-coded, service-specific @@ -283,7 +283,7 @@ check and watch patterns rather than relying on dashboard defaults. ### Decision: containerized worker (recommended) over completing the edge-agent migration -Ship the existing worker as a container (`Dockerfile.worker`: pinned Node 24 + +Ship the existing worker as a container (`Dockerfile.worker`: pinned Node 26 + a prebuilt esbuild bundle over production-only `node_modules` + Tesseract + a Python venv with a hashed `worker/python/requirements.txt` + a provider-free `dist/worker/validate-runtime.mjs` gate) and run **one diff --git a/docs/design-system/COMPONENTS.md b/docs/design-system/COMPONENTS.md index 1ad3cae5c5..b50e0360cb 100644 --- a/docs/design-system/COMPONENTS.md +++ b/docs/design-system/COMPONENTS.md @@ -1052,7 +1052,7 @@ This generated snapshot is a local source-derived inventory. It does not assert | `Progress` | feedback | yes | yes | no | yes | no | 0 | | `Quantity` | answer | yes | yes | inherited-global-root | yes | no | 1 | | `RadioGroup` | controls | yes | yes | no | yes | no | 0 | -| `RetrievalStateBanner` | answer | yes | yes | inherited-global-root | yes | no | 1 | +| `RetrievalStateBanner` | answer | yes | yes | inherited-global-root | yes | no | 2 | | `SafeBoldText` | layout | yes | yes | inherited-global-root | yes | no | 8 | | `SearchField` | controls | yes | yes | no | yes | no | 0 | | `SegmentedControl` | controls | yes | yes | inherited-global-root | yes | no | 9 | @@ -1070,6 +1070,6 @@ This generated snapshot is a local source-derived inventory. It does not assert | `ToastRegion` | feedback | yes | yes | no | yes | no | 0 | | `ToggleSwitch` | controls | yes | yes | inherited-global-root | yes | no | 2 | | `Tooltip` | feedback | yes | yes | no | yes | no | 0 | -| `VerificationNotice` | answer | yes | yes | inherited-global-root | yes | no | 1 | +| `VerificationNotice` | answer | yes | yes | inherited-global-root | yes | no | 2 | diff --git a/docs/design-system/GATES.md b/docs/design-system/GATES.md index f341d83dff..5aee1547e3 100644 --- a/docs/design-system/GATES.md +++ b/docs/design-system/GATES.md @@ -10,6 +10,57 @@ than CI, two of them by the second reader; review does not scale past two carefu - **Date:** 5 August 2026 · companions: [SPEC.md](SPEC.md) · [TOKENS.md](TOKENS.md) · [COMPONENTS.md](COMPONENTS.md) · [DECISIONS.md](DECISIONS.md) +> **Figures.** The generated table in §0 is the only current count in this document. +> Numbers written into the prose of §2, §4 and §5 are the historical record of what was +> true when that passage was written, and several are now badly stale — the same metric +> once appeared here as three different hand-typed values, none of them right. Read the +> prose for _why_ a gate is shaped the way it is; read §0 for _how much_ is left. Where +> they disagree, §0 wins, and per [README.md](README.md) the baseline outranks both. + +--- + +## 0 · Current ratchet figures + + + + + +| Metric | Current pin | Files pinned | +| --------------------------------- | ------------------ | ------------ | +| `arbitraryTracking` | **0** (hard floor) | 0 | +| `colourOnlyStatusIndicators` | **0** (hard floor) | 0 | +| `darkColorOverrides` | **0** (hard floor) | 0 | +| `edgeOwnershipConflicts` | 5 | 2 | +| `elevationInversions` | 6 | 5 | +| `errorStateCountProps` | **0** (hard floor) | 0 | +| `failedStateResultCounts` | **0** (hard floor) | 0 | +| `handRolledCommandButtons` | 8 | 7 | +| `hardcodedCssMotionDurations` | 26 | 1 | +| `interactiveTapFloorDeclarations` | 2 | 1 | +| `layoutTransitionExceptions` | 10 | 4 | +| `legacyPaletteUtilities` | **0** (hard floor) | 0 | +| `legacyShadowAliases` | 34 | 28 | +| `legacyTapClasses` | **0** (hard floor) | 0 | +| `literalShadowClasses` | **0** (hard floor) | 0 | +| `onePixelShadowSpreads` | **0** (hard floor) | 0 | +| `rawColorLiterals` | **0** (hard floor) | 0 | +| `rawCssZIndices` | 4 | 1 | +| `rawGapLiterals` | 11 | 1 | +| `rawLineHeightLiterals` | **0** (hard floor) | 0 | +| `rawMarginLiterals` | 34 | 4 | +| `rawPaddingLiterals` | 22 | 4 | +| `rawRadiusLiterals` | 15 | 3 | +| `sameFileTextSmMinusMix` | 25 | 25 | +| `statusColouredNumerals` | **0** (hard floor) | 0 | +| `textSoftConsumers` | **0** (hard floor) | 0 | + +26 metrics, 13 of them pinned at zero. A metric at zero is a hard floor: +the check asserts `value <= baseline`, so any reintroduction fails. A non-zero pin is +recorded debt with per-path pins, so a new occurrence fails even while the total stands. + + + --- ## 1 · What actually runs today @@ -146,35 +197,35 @@ theme-list parity, and remote design-project publication remain separate concern ## 3 · The prohibition table -| Never do this | Gate | Status | -| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Reintroduce `.ckb-v2:not(.dark)` | Cascade computed-style test (PR 1) + contract-test selector filter update | implemented-blocking — landed #1538; contract parser + dark-ink regression guard | -| Add `prefers-color-scheme` rules to the v2 layer | — (users pin a theme against OS preference) | **manual** | -| Paint a numeral with a status colour; use amber/red decoratively | `check:design-system-contract` — `statusColouredNumerals` + `colourOnlyStatusIndicators` | **implemented-blocking** — both metrics and their per-path baselines are pinned at **0**; see §5 | -| Put `--decoration-soft`/`--text-soft` on a text node | Tier pinned both sides in the token contract; recipe AST (`decoration-on-text.contract.test.ts`) | implemented-blocking (recipes); broader AST follow-on | -| Uppercase a dose unit | `Quantity` pins `normal-case` + unit test | implemented-blocking (branch) | -| Invert a PDF, diagram or clinical image in any theme | `check:design-system-contract` — `imageInversions`, pinned at **zero**, not ratcheted | **implemented-blocking** (9 Aug 2026) — CSS `filter`/`backdrop-filter` plus the Tailwind `invert`/`hue-rotate` utilities; see §5 | -| Border **and** ring on one surface, or a 1px spread in a drop shadow | `check:design-system-contract` — `edgeOwnershipConflicts` (6) + `onePixelShadowSpreads` (0) | **implemented-blocking for new use** — AST/CSS ratchets with per-path pins; the recorded debt itself is Gate 8's remaining half | -| A child shadow heavier than its parent's | Gate 7 | implemented-partial | -| Use `--shadow-tight`/any alias in new code | `check:design-system-contract` — `legacyShadowAliases`, ratcheted at 34 with per-path pins | **implemented-blocking for new use** — a new alias in any file fails. `--shadow-tight` itself is retired onto `--e1` and additionally pinned by a tracked-tree sweep in `tests/design-token-contract.test.ts` (it was silently reverted once by the `acf78bf` merge); the remaining 34 are the other six aliases after the e2 (`--shadow-card`/`--shadow-soft`) family pay-down, still `#262` | -| Raw pixel size, padding, radius, gap or line-height in markup | `check:design-system-contract` — `rawPaddingLiterals` (52), `rawRadiusLiterals` (20), `rawGapLiterals` (25), `rawMarginLiterals` (74), `rawLineHeightLiterals` (0) | **implemented-blocking for new use** (9 Aug 2026) — per-path ratchets over both the utility and the CSS-declaration spelling, so a literal cannot move into `globals.css` to escape. Values containing a CSS function (`env(`, `clamp(`, `max(`, `calc(`) are sanctioned computed forms and exempt. Raw _size_ is still covered only for tap/shadow/colour | -| Animate `width`, `height`, `grid-template-*`, `top`, `left`, `gap` | `check:design-system-contract` — `layoutTransitionExceptions`, ratcheted at 11 with per-path pins | **implemented-blocking for new use** — `SAFE_TRANSITION_PROPERTIES` carries the compositor-only allowlist; phone chrome's deliberate `grid-template-rows` is in the recorded 11 | -| Hardcode a transition duration | `check:design-system-contract` — `hardcodedMotionClasses` (**zero**) + `hardcodedCssMotionDurations` (41) | **implemented-blocking** for the Tailwind `duration-*`/`delay-*`/`transition-all` form; the CSS form is a ratchet, so its 41 are debt | -| A `z-[N]` literal outside the named rungs, in JS/TS only | `require-z-index-ladder` (ESLint) | implemented-blocking. Scope: `z-[N]` literals in JS/TS AST nodes. Plain `z-30` utilities and every `.css` / `.module.css` declaration are invisible to an ESLint rule - `sidebar-live-shell.module.css` carries an off-ladder `z-index: var(--z-sidebar-flyout, 50)` this cannot see. | -| Ship a registered component without a direct publication test | Gate 12 | implemented-blocking | -| Lower a production tap target below 48px | `check:design-system-contract` — `interactiveTapFloorDeclarations` (9) + Gate 2's phone-viewport `ui-style-contract` audit + `ui-smoke` flake history | **implemented-blocking for new use** (15 Aug 2026) — a new interactive control declaring a sub-floor `min-h-*`, including comparable arbitrary values and any reachable conditional branch, fails on both the total and its own path; the 9 recorded sites remain debt. Component-wrapper tags such as `` are a known blind spot | -| Construct an enabled control with no action, destination or name | PR 4 discriminated unions + DOM contracts; `require-button-wiring` covers raw ` {expanded ? ( @@ -114,7 +114,7 @@ function HeaderMockup({ expanded = false, compact = false }: { expanded?: boolea : "border-[color:var(--border)] bg-[color:var(--surface-raised)]", ].join(" ")} > - +