diff --git a/README.md b/README.md index 0b64e0b6..b674d18b 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ modex/ when it is unused, or retries in place while another turn is running or opening. A second failure shows the card with both attempts recorded. Early send/retry rejections (for example, a busy thread) return to the caller without adding transcript items. -- **Code Review.** A PR inbox, overview and remote diffs bound to the selected base/head revision, through your own `gh` login; a read-only Claude review and questions; comments, reviews and merge behind an explicit preview. It is on by default from the v0.5.0 candidate. Settings → General → "Show Code Review in the rail" turns it off. Live and human acceptance are still open; see [the design](docs/design/code-review.md). +- **Code Review.** A PR inbox, overview and remote diffs bound to the selected base/head revision, through your own `gh` login; a read-only Claude review and questions; comments, reviews and merge behind an explicit preview. It is hidden by default. Settings → General → "Show Code Review in the rail" shows it, or hides it again. Live and human acceptance are still open; see [the design](docs/design/code-review.md). - **Tabbed workspace.** Review working-tree changes with numbered diffs, a filterable file tree, and confirmed per-file discard. Open Files, Terminal, Canvas, or browser tabs beside the chat; use Shift + Command + B for a new tab or Shift + Command + F for full view. Escape exits diff --git a/apps/desktop/e2e/code-review-diff.spec.ts b/apps/desktop/e2e/code-review-diff.spec.ts index 46632577..94a13687 100644 --- a/apps/desktop/e2e/code-review-diff.spec.ts +++ b/apps/desktop/e2e/code-review-diff.spec.ts @@ -43,6 +43,7 @@ async function open(files?: PrFile[], hold: string | null = null) { return { requestId: req.requestId, op: req.op, scope: { account: req.key?.account ?? data.account, key: req.key ?? null, revision: req.revision ?? null }, result }; }); }, { files: files ?? R4.files.value.files, overview: R4.overview, alternate: R1.overview, connection: R1.connection, inbox: R1.inbox, at: NOW, account: R4.overview.key.account, hold }); + await page.evaluate(() => localStorage.setItem("modex.codeReview", "on")); await page.reload(); await tid(page, "rail-code-review").click(); await page.locator('[data-testid="code-review-row"][data-pr="example-org/field-guide#91"]').click(); diff --git a/apps/desktop/e2e/code-review-enablement.spec.ts b/apps/desktop/e2e/code-review-enablement.spec.ts index 05180fd2..5e1804bc 100644 --- a/apps/desktop/e2e/code-review-enablement.spec.ts +++ b/apps/desktop/e2e/code-review-enablement.spec.ts @@ -4,8 +4,8 @@ import { tid } from "./support.js"; import { baseWorld, launchWithGh, prRow, type Harness } from "./code-review-support.js"; /** - * The Code Review setting (#319, #327): on by default in this build, off as a visible rollback in Settings → General, - * and an old explicit off kept. Turning it off hides the surface; it does not touch main's records. + * The Code Review setting (#319, #327): hidden by default in a built app until its gates pass, shown from Settings → + * General or by the old switch's "on". Turning it off hides the surface; it does not touch main's records. */ let h: Harness; test.afterEach(async () => { await h?.close(); }); @@ -16,13 +16,20 @@ async function general() { return tid(h.page, "code-review-in-rail"); } -test("Code Review is on by default: the rail shows it, Settings says so, and the inbox opens", async () => { - h = await launchWithGh(baseWorld()); +test("Code Review is hidden by default: the rail leaves it out, makes no gh read, and Settings can show it", async () => { + h = await launchWithGh(baseWorld(), { showCodeReview: false }); const { page } = h; - await expect(tid(page, "rail-code-review")).toBeVisible(); + await expect(tid(page, "open-settings")).toBeVisible(); + await expect(tid(page, "rail-code-review")).toHaveCount(0); + const box = await general(); + await expect(box).not.toBeChecked(); + expect(h.calls().filter((call) => call.includes("search"))).toEqual([]); + await box.check(); + await page.keyboard.press("Escape"); await tid(page, "rail-code-review").click(); await expect(prRow(page, "example-org/lending#73")).toBeVisible(); - await expect(await general()).toBeChecked(); + await page.reload(); + await expect(tid(page, "rail-code-review")).toBeVisible(); }); test("turning it off is the rollback: the surface leaves at once, survives a relaunch, keeps a draft, and can come back", async () => { diff --git a/apps/desktop/e2e/code-review-ipc.spec.ts b/apps/desktop/e2e/code-review-ipc.spec.ts index 7abbff28..bd18621f 100644 --- a/apps/desktop/e2e/code-review-ipc.spec.ts +++ b/apps/desktop/e2e/code-review-ipc.spec.ts @@ -71,6 +71,7 @@ test("real Electron IPC reads synthetic inbox, distinct PRs and revision-bound f { path: "src/holds/move.ts", patch: { kind: "text" } }, { path: "assets/shelf.png", patch: { kind: "omitted" } }, ] } }); // CR-5: drive the populated Changes pane through the production read path too. + await page.evaluate(() => localStorage.setItem("modex.codeReview", "on")); await page.reload(); await tid(page, "rail-code-review").click(); await page.locator('[data-testid="code-review-row"][data-pr="example-org/lending#73"]').click(); diff --git a/apps/desktop/e2e/code-review-navigation.spec.ts b/apps/desktop/e2e/code-review-navigation.spec.ts index d5beb43e..3c471c81 100644 --- a/apps/desktop/e2e/code-review-navigation.spec.ts +++ b/apps/desktop/e2e/code-review-navigation.spec.ts @@ -122,6 +122,7 @@ async function open(data: Served = served()): Promise { ({ home, repo } = seedHome()); ({ app, page } = await launch(home)); await serve(app, data); + await page.evaluate(() => localStorage.setItem("modex.codeReview", "on")); await page.reload(); await expect(tid(page, "rail-code-review")).toBeVisible(); } diff --git a/apps/desktop/e2e/code-review-overview.spec.ts b/apps/desktop/e2e/code-review-overview.spec.ts index 86311181..4987fb8f 100644 --- a/apps/desktop/e2e/code-review-overview.spec.ts +++ b/apps/desktop/e2e/code-review-overview.spec.ts @@ -42,6 +42,7 @@ async function open(hostile = false, mode = "normal") { return { requestId: req.requestId, op: req.op, scope: { account: req.key?.account ?? "1001", key: req.key ?? null, revision: req.revision ?? null }, result }; }); }, { R1, R2, R4, NOW, hostile, mode }); + await page.evaluate(() => localStorage.setItem("modex.codeReview", "on")); await page.reload(); await tid(page, "rail-code-review").click(); await page.locator('[data-testid="code-review-row"][data-pr="example-org/lending#73"]').first().click(); diff --git a/apps/desktop/e2e/code-review-support.ts b/apps/desktop/e2e/code-review-support.ts index eaf58e2c..77688c53 100644 --- a/apps/desktop/e2e/code-review-support.ts +++ b/apps/desktop/e2e/code-review-support.ts @@ -39,7 +39,7 @@ export interface Harness { const RECORDER = (log: string) => `#!/bin/sh\nprintf '%s %s\\n' "$(basename "$0")" "$*" >> '${log}'\nexit 1\n`; -export async function launchWithGh(initial: GhWorld): Promise { +export async function launchWithGh(initial: GhWorld, options: { showCodeReview?: boolean } = {}): Promise { const { home, repo } = seedHome(); const bin = path.join(home, "bin"), config = path.join(home, "gh-config"); fs.mkdirSync(bin); @@ -73,7 +73,10 @@ export async function launchWithGh(initial: GhWorld): Promise { // Row ages and captures read the renderer's clock: pin it to the fixtures' (test/fixtures/code-review.ts NOW). await page.clock.setFixedTime(new Date("2026-10-18T20:41:00Z")); await page.reload(); - // Code Review's own button may be off (e2e/code-review-enablement.spec.ts); Settings is always in the rail. + // Built apps hide Code Review by default (e2e/code-review-enablement.spec.ts); these specs test the workspace itself. + if (options.showCodeReview !== false) await page.evaluate(() => localStorage.setItem("modex.codeReview", "on")); + await page.reload(); + // Settings is always in the rail, whether or not Code Review's button is. await expect(tid(page, "open-settings")).toBeVisible(); return { app, page }; }; diff --git a/apps/desktop/package.json b/apps/desktop/package.json index b7fb3b17..ec575804 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@modex/desktop", - "version": "0.4.1", + "version": "0.4.2", "description": "Modex desktop — a Codex-App-style GUI: projects, parallel agent threads, worktrees, inline approvals, and a live Changes panel.", "license": "MIT", "private": true, diff --git a/apps/desktop/src/renderer/layout.ts b/apps/desktop/src/renderer/layout.ts index b1501e42..f4d4e5f7 100644 --- a/apps/desktop/src/renderer/layout.ts +++ b/apps/desktop/src/renderer/layout.ts @@ -3,7 +3,7 @@ import { DEFAULT_LAYOUT, loadLayout, saveLayout, type Layout } from "../shared/l /** The pane layout, loaded once from localStorage and written back on every change. */ export function useLayout(persist = true): [Layout, (patch: Partial | ((l: Layout) => Partial)) => void] { - const [layout, setLayout] = useState(() => persist ? loadLayout(localStorage) : { ...DEFAULT_LAYOUT }); + const [layout, setLayout] = useState(() => persist ? loadLayout(localStorage, import.meta.env.DEV) : { ...DEFAULT_LAYOUT }); const update = useCallback((patch: Partial | ((l: Layout) => Partial)) => { setLayout((l) => { const next = { ...l, ...(typeof patch === "function" ? patch(l) : patch) }; diff --git a/apps/desktop/src/shared/layout.ts b/apps/desktop/src/shared/layout.ts index e8780486..8f3f91c5 100644 --- a/apps/desktop/src/shared/layout.ts +++ b/apps/desktop/src/shared/layout.ts @@ -16,26 +16,26 @@ export type Layout = { streamerMode: boolean; /** Analytics' icon in the rail (Settings → General). */ analytics: boolean; - /** Code Review's destination in the rail (Settings → General). Turning it off is the rollback: main's records and any write already sent stay. */ + /** Code Review's destination in the rail (Settings → General), off by default until its live and human gates pass (#319). Turning it off hides it: main's records and any write already sent stay. */ codeReview: boolean; }; export const LAYOUT_KEY = "modex.layout"; /** Pre-layout key that stored only the sidebar as "open" | "closed"; read once and migrated. */ export const LEGACY_SIDEBAR_KEY = "modex.sidebar"; -/** The per-home switch Code Review had before this setting: "on" showed it, anything else set hid it; read until the layout saves a choice. */ +/** The per-home switch Code Review had before this setting: "on" showed it; read until the layout saves a choice. */ export const LEGACY_CODE_REVIEW_SWITCH = "modex.codeReview"; -export const DEFAULT_LAYOUT: Layout = { sidebar: true, changes: true, streamerMode: false, analytics: true, codeReview: true }; +export const DEFAULT_LAYOUT: Layout = { sidebar: true, changes: true, streamerMode: false, analytics: true, codeReview: false }; type Storage = { getItem(key: string): string | null; setItem(key: string, value: string): void }; -/** Reads the saved layout; unknown or malformed fields fall back to their defaults, never throw. */ -export function loadLayout(storage: Storage): Layout { - const layout: Layout = { ...DEFAULT_LAYOUT }; - // Someone who explicitly set the old switch to anything but "on" keeps Code Review hidden. An unset switch was only ever the old default. +/** Reads the saved layout; unknown or malformed fields fall back to their defaults, never throw. Development builds show Code Review unless told otherwise. */ +export function loadLayout(storage: Storage, development = false): Layout { + const layout: Layout = { ...DEFAULT_LAYOUT, codeReview: development }; + // The old switch keeps its meaning: "on" shows Code Review, and any other value that was set hides it. try { const legacy = storage.getItem(LEGACY_CODE_REVIEW_SWITCH); - if (legacy !== null && legacy !== "on") layout.codeReview = false; + if (legacy !== null) layout.codeReview = legacy === "on"; } catch { /* Storage is blocked: the default stands. */ } let saved: unknown = null; try { diff --git a/apps/desktop/test/code-review-workspace.test.ts b/apps/desktop/test/code-review-workspace.test.ts index 9a4674d6..24f91181 100644 --- a/apps/desktop/test/code-review-workspace.test.ts +++ b/apps/desktop/test/code-review-workspace.test.ts @@ -243,14 +243,16 @@ test("pins are local, per account, validated on load and capped", () => { test("the destination defaults on, keeps an explicit old off, and the saved choice wins over the old switch", () => { const storage = (value: string | null, saved?: object) => ({ getItem: (k: string) => k === LEGACY_CODE_REVIEW_SWITCH ? value : k === LAYOUT_KEY && saved ? JSON.stringify(saved) : null, setItem: () => {} }); - assert.equal(loadLayout(storage(null)).codeReview, true); + assert.equal(loadLayout(storage(null)).codeReview, false); + assert.equal(loadLayout(storage(null), true).codeReview, true); + assert.equal(loadLayout(storage("off"), true).codeReview, false); assert.equal(loadLayout(storage("on")).codeReview, true); assert.equal(loadLayout(storage("off")).codeReview, false); assert.equal(loadLayout(storage("yes")).codeReview, false); assert.equal(loadLayout(storage("off", { v: 1, codeReview: true })).codeReview, true); assert.equal(loadLayout(storage(null, { v: 1, codeReview: false })).codeReview, false); - assert.equal(loadLayout(storage(null, { v: 1, sidebar: false })).codeReview, true); - assert.equal(loadLayout({ getItem: () => { throw new Error("blocked"); }, setItem: () => {} }).codeReview, true); + assert.equal(loadLayout(storage(null, { v: 1, sidebar: false })).codeReview, false); + assert.equal(loadLayout({ getItem: () => { throw new Error("blocked"); }, setItem: () => {} }).codeReview, false); }); test("the workspace's pane widths persist with the layout, and invalid saved values fall back", () => { diff --git a/apps/desktop/test/layout.test.ts b/apps/desktop/test/layout.test.ts index bd5b4ce5..f90b2cd8 100644 --- a/apps/desktop/test/layout.test.ts +++ b/apps/desktop/test/layout.test.ts @@ -17,11 +17,11 @@ test("layout: defaults, round trip, and bad data falls back per field", () => { saveLayout(s, { sidebar: false, changes: false, streamerMode: true, analytics: false, codeReview: false }); assert.deepEqual(loadLayout(s), { sidebar: false, changes: false, streamerMode: true, analytics: false, codeReview: false }); assert.deepEqual(loadLayout(memory({ [LAYOUT_KEY]: "{not json" })), DEFAULT_LAYOUT); - assert.deepEqual(loadLayout(memory({ [LAYOUT_KEY]: JSON.stringify({ changes: "no", sidebar: false }) })), { sidebar: false, changes: true, streamerMode: false, analytics: true, codeReview: true }, "a layout saved before Analytics shows it"); + assert.deepEqual(loadLayout(memory({ [LAYOUT_KEY]: JSON.stringify({ changes: "no", sidebar: false }) })), { sidebar: false, changes: true, streamerMode: false, analytics: true, codeReview: false }, "a layout saved before Analytics shows it"); }); test("layout: migrates the old sidebar key, and the new key wins once written", () => { - assert.deepEqual(loadLayout(memory({ [LEGACY_SIDEBAR_KEY]: "closed" })), { sidebar: false, changes: true, streamerMode: false, analytics: true, codeReview: true }); + assert.deepEqual(loadLayout(memory({ [LEGACY_SIDEBAR_KEY]: "closed" })), { sidebar: false, changes: true, streamerMode: false, analytics: true, codeReview: false }); assert.deepEqual(loadLayout(memory({ [LEGACY_SIDEBAR_KEY]: "open" })), DEFAULT_LAYOUT); const s = memory({ [LEGACY_SIDEBAR_KEY]: "closed" }); saveLayout(s, { ...loadLayout(s), sidebar: true }); diff --git a/docs/design/code-review.md b/docs/design/code-review.md index 6af6042e..5ba49c9a 100644 --- a/docs/design/code-review.md +++ b/docs/design/code-review.md @@ -6,7 +6,7 @@ screenshots (R1–R4). The work is tracked in [#319](https://github.com/TypeSafe This page is CR-1's contract ([#320](https://github.com/TypeSafeAI/modex/issues/320)). Every later slice works against it. -**Where it stands (2026-11-01).** The core CR-1 through CR-7 slices have landed on `main`, and Code Review is present but hidden in the v0.4 releases, whose built apps require the local switch `modex.codeReview` to be `on`. The v0.5.0 candidate replaces that switch with a default-on setting and a rollback in Settings → General, pending Val's approval. Built apps required the switch because Gate A (#319) is not accepted ([v0.4.0 review](../reviews/2026-10-24-v0.4.0-release-review.md)). The candidate also carries the CR-7 sign-out/draft regression and CR-4 through CR-7 accessibility coverage (C76). Remaining engineering work is composer-clearance verification. C58 attachments and C59 effort remain omitted without an approved deviation, alongside the unresolved composer decisions below. Original-reference comparison, human keyboard/VoiceOver, the live CR-6 smoke and the CR-7 canary retain their separate acceptance gates. Each issue's ticked boxes and next action are in [status.md](../status.md#open-issue-checklists). +**Where it stands (2026-11-01).** The core CR-1 through CR-7 slices have landed on `main`, and Code Review is present but hidden in the v0.4 releases, whose built apps require the local switch `modex.codeReview` to be `on`. From v0.4.2 a setting in Settings → General replaces that switch, off by default; Val chose on 2026-11-03 to keep Code Review hidden until its gates pass. Built apps required the switch because Gate A (#319) is not accepted ([v0.4.0 review](../reviews/2026-10-24-v0.4.0-release-review.md)). The candidate also carries the CR-7 sign-out/draft regression and CR-4 through CR-7 accessibility coverage (C76). Remaining engineering work is composer-clearance verification. C58 attachments and C59 effort remain omitted without an approved deviation, alongside the unresolved composer decisions below. Original-reference comparison, human keyboard/VoiceOver, the live CR-6 smoke and the CR-7 canary retain their separate acceptance gates. Each issue's ticked boxes and next action are in [status.md](../status.md#open-issue-checklists). CR-1 itself added a pure read contract, synthetic fixtures and this page, with no IPC channel, UI or GitHub request. Gate A then followed in order: @@ -521,10 +521,10 @@ replaces (`reader.ts`). The overview's content (#323) and the remote diff and fi panes the host lays out. Until then, those panes say so. **Visibility.** The v0.4 releases hide the rail button in a built app unless `localStorage["modex.codeReview"]` -is `"on"`. The v0.5.0 candidate replaces that with the layout's `codeReview` setting, on by default, with -Settings → General → "Show Code Review in the rail" as the rollback; an old explicit off is kept. There is no main -flag. Whether it ships on before Gate A completes is Val's decision, recorded in the -[candidate review](../reviews/2026-11-02-v0.5.0-candidate.md). +is `"on"`. From v0.4.2 the layout's `codeReview` setting replaces that, off by default, with +Settings → General → "Show Code Review in the rail" as the control; the old switch's value is honoured until the layout +saves a choice. Development builds default it on. There is no main flag. Val chose on 2026-11-03 to keep it hidden +until Gate A completes ([v0.4.2 review](../reviews/2026-11-03-v0.4.2-release-review.md)). **Arrangements.** A PR opens with two tabs in the title bar, PR and Changes, starting where the inbox ends. They show one pane (R1), or both side by side behind the right-panel button (R4). Each PR keeps diff --git a/docs/reviews/2026-11-01-cr8-acceptance-matrix.md b/docs/reviews/2026-11-01-cr8-acceptance-matrix.md index f1074f75..b708ed4e 100644 --- a/docs/reviews/2026-11-01-cr8-acceptance-matrix.md +++ b/docs/reviews/2026-11-01-cr8-acceptance-matrix.md @@ -96,8 +96,8 @@ found that asserts it; it is not a pass. | C47 | Independent sidebar scroll | #323 | built (#393 `6c40547`) | `code-review-scroll.spec.ts` (#438 `afec85b`, where the sidebar is its own scroller) | — | — | — | | C48 | Activity heading and filter | #323 | built (#393 `6c40547`) | `code-review-overview.spec.ts` "refresh does not mix fresh and pending cached pages…" (filter keeps focus) | — | — | open (VoiceOver) | | C49 | Commit groups | #323 | built (#393 `6c40547`) | `code-review-overview.spec.ts` ("7 commits"); `code-review-overview.test.ts` (dedup) | `A-main` (activity 36, read), `A-323` (35, complete) | — | open (VoiceOver) | -| C50 | "opened this pull request" | #323 | built (#393 `6c40547`; rendered in `PrOverview.tsx`) | candidate `cr-v050` (#481): `code-review-overview.spec.ts` "activity opening and comment link…" asserts actor, wording and time | `A-323` (opening enrichment read) | — | open: reference comparison | -| C51 | Bot comment card (⋯ menu: Copy comment link, Add to chat) | #323 | built (#393 `6c40547`) | `code-review-overview.spec.ts` (cards); candidate `cr-v050` (#481): the same spec's activity-opening case asserts the ⋯ menu's Copy comment link by keyboard, with the exact URL; its Add to chat is covered under C54 | — | — | open (VoiceOver) | +| C50 | "opened this pull request" | #323 | built (#393 `6c40547`; rendered in `PrOverview.tsx`) | on `main` (#481 via #490): `code-review-overview.spec.ts` "activity opening and comment link…" asserts actor, wording and time | `A-323` (opening enrichment read) | — | open: reference comparison | +| C51 | Bot comment card (⋯ menu: Copy comment link, Add to chat) | #323 | built (#393 `6c40547`) | `code-review-overview.spec.ts` (cards); on `main` (#481 via #490): the same spec's activity-opening case asserts the ⋯ menu's Copy comment link by keyboard, with the exact URL; its Add to chat is covered under C54 | — | — | open (VoiceOver) | | C52 | Deployment table and preview links | #323 | built (#393 `6c40547`) | `code-review-overview.test.ts` "preview destinations reject credentials…"; `code-review-read-service.test.ts`; `code-review-overview.spec.ts` | `A-323` (one real deployment and status) | — | open (VoiceOver) | | C53 | Third-party bot button | #323 | deviation D4 (inert) | `code-review-overview.spec.ts` "hostile Markdown is inert…"; `code-review-regressions.spec.ts` "hostile content" | — | — | — | | C54 | Reaction and Add to chat | #325 (reactions D3) | built: Add to chat (#422 `b643617`); reactions deviation D3 | `code-review-agent.spec.ts` "a question is answered by a run of its own, with 'Add to chat' quoting a comment into it" | — | open (needs repo Val names, #325) | open (VoiceOver) | @@ -107,22 +107,22 @@ found that asserts it; it is not a pass. | C58 | Attach (+) | #325 | not built (left out; no numbered decision covers it) | — | — | — | — | | C59 | Model and reasoning effort | #325 | built: model select only, effort left out (#422 `b643617`) | `code-review-agent.spec.ts` (runs with `--model`); `code-review-agent.test.ts` (unknown model refused) | — | open (needs repo Val names, #325) | open (VoiceOver) | | C60 | Voice input | deferred | deviation D5 | — | — | — | — | -| C61 | Composer clearance | #323 / #324 | built at fixture (#393 `6c40547`): bottom clearance; final Comment in viewport at 900×600 | `code-review-overview.spec.ts` "hostile Markdown is inert and narrow content remains reachable"; `code-review-scroll.spec.ts`. Candidate `cr-v050-followups`: `code-review-agent.spec.ts` "C61…" measures Ask and the last activity item beside CR-6's inline section and its open panel, at 900×600 and 1440×900; red under an overlay and under a panel that takes the pane's height | — | — | open (VoiceOver) | +| C61 | Composer clearance | #323 / #324 | built at fixture (#393 `6c40547`): bottom clearance; final Comment in viewport at 900×600 | `code-review-overview.spec.ts` "hostile Markdown is inert and narrow content remains reachable"; `code-review-scroll.spec.ts`. On `main` (#491): `code-review-agent.spec.ts` "C61…" measures Ask and the last activity item beside CR-6's inline section and its open panel, at 900×600 and 1440×900; red under an overlay and under a panel that takes the pane's height | — | — | open (VoiceOver) | | C62 | Header repeated over Changes | #322 | built; now repeats the live C21 and C27–C29 | `code-review-navigation.spec.ts`; journey (R4); `code-review-a11y.spec.ts` | — | — | open (VoiceOver) | -| C63 | Changes ⋯ menu and view toggles | #324 | built in part (#404 `e3527f4`): "Wrap lines" and "Highlight changes" toggles. The ⋯ menu and the tree/image/layout toggles are left out, as the inventory allows | candidate `cr-v050` (#484): `code-review-diff.spec.ts` "wrapping and highlighting change rendered code…" | — | — | open (VoiceOver); open: reference comparison | +| C63 | Changes ⋯ menu and view toggles | #324 | built in part (#404 `e3527f4`): "Wrap lines" and "Highlight changes" toggles. The ⋯ menu and the tree/image/layout toggles are left out, as the inventory allows | on `main` (#484 via #490): `code-review-diff.spec.ts` "wrapping and highlighting change rendered code…" | — | — | open (VoiceOver); open: reference comparison | | C64 | File header | #324 | built (#404 `e3527f4`) | `code-review-diff.spec.ts` "grouped diffs, both gutters, hierarchy, filter…"; review-flow (#441) | `A-324` (service: 15/15 files) | — | open (VoiceOver); open: reference comparison | | C65 | Viewed toggle | needs decision 5 | needs decision 5 (not built) | — | — | — | — | -| C66 | File ⋯ menu (never Discard) | #324 | built (#404 `e3527f4`): Copy path and Open in GitHub only | `code-review-regressions.spec.ts` "local Changes stay local"; `code-review-diff.spec.ts` "…use remote data only". Candidate `cr-v050` (#484): the same file's "file menu copies the remote path…" opens the menu and asserts both items and no Discard | — | — | open (VoiceOver) | +| C66 | File ⋯ menu (never Discard) | #324 | built (#404 `e3527f4`): Copy path and Open in GitHub only | `code-review-regressions.spec.ts` "local Changes stay local"; `code-review-diff.spec.ts` "…use remote data only". On `main` (#484 via #490): the same file's "file menu copies the remote path…" opens the menu and asserts both items and no Discard | — | — | open (VoiceOver) | | C67 | Folded lines, expand | #324 | built (#404 `e3527f4`) | `code-review-diff.spec.ts` "expanding context asks the exact remote revision…", "review: context expands adjacent to both ends…", "context text budget is shared…"; `code-review-diff.test.ts` "context windows reveal both ends…" | `A-main` (context read), `A-324` (base context matched the merge base) | — | open (VoiceOver) | | C68 | Line numbers, add/remove rows | #324 | built (#404 `e3527f4`) | `code-review-diff.test.ts` ×9; `code-review-diff.spec.ts` "line anchors have one keyboard entry per file…", "old-line anchors name the merge base…"; review-flow | `A-324` (15/15 patches parsed; anchor) | — | open (VoiceOver) | -| C69 | "More unchanged context…" | #324 | built (#404 `e3527f4`; `ContextFold` when `count` is null) | candidate `cr-v050` (#484): `code-review-diff.spec.ts` "more unchanged context discloses uncertainty…" finds the control by this text and asserts the one request it sends | — | — | open (VoiceOver) | +| C69 | "More unchanged context…" | #324 | built (#404 `e3527f4`; `ContextFold` when `count` is null) | on `main` (#484 via #490): `code-review-diff.spec.ts` "more unchanged context discloses uncertainty…" finds the control by this text and asserts the one request it sends | — | — | open (VoiceOver) | | C70 | Collapsed file sections | #324 | built (#404 `e3527f4`) | `code-review-diff.spec.ts` (Collapse a file; "1,000 files keep… mounted patch bodies bounded") | — | — | open (VoiceOver) | -| C71 | Horizontal scroll per file | #324 | built (#404 `e3527f4`; `.cr-patch { overflow-x: auto }` with wrap off) | candidate `cr-v050` (#484): `code-review-diff.spec.ts` "…horizontal scroll stays per file" | — | — | — | +| C71 | Horizontal scroll per file | #324 | built (#404 `e3527f4`; `.cr-patch { overflow-x: auto }` with wrap off) | on `main` (#484 via #490): `code-review-diff.spec.ts` "…horizontal scroll stays per file" | — | — | — | | C72 | Filter files | #324 | built (#404 `e3527f4`) | `code-review-diff.spec.ts` (Filter remote files) | — | — | open (VoiceOver) | | C73 | Folder tree, selected file | #324 | built (#404 `e3527f4`) | `code-review-diff.spec.ts` "…hierarchy, filter and keyboard selection…", "narrow tree selection returns to the diff…"; `code-review-scroll.spec.ts`; journey R4 populated (#464 `3d72c6e`) | — | — | open (VoiceOver); open: reference comparison | | C74 | Overview / Changes divider | #322 | built | `code-review-navigation.spec.ts` (keyboard resize persists across reload) | — | — | open (VoiceOver) | -| C75 | Diff / tree divider | #322 | built | candidate `cr-v050` (#484): `code-review-diff.spec.ts` "file tree divider resizes with keyboard and pointer and persists on reload"; `code-review-a11y.spec.ts` (reached by Tab as a separator); `code-review-navigation.spec.ts` (tree folds when narrow) | — | — | open (VoiceOver) | -| C76 | Keyboard path, 900×600 and 1440×900 | #327 | built; candidate `cr-v050` requires the CR-3 to CR-7 controls and checks every text run in the CR-4 to CR-7 sections | `code-review-a11y.spec.ts`; `code-review-navigation.spec.ts` keyboard test; `code-review-scroll.spec.ts` (focus kept); per-slice keyboard cases in the overview, diff, agent and actions specs | — | — | **open: [VoiceOver checklist](2026-10-22-cr8-voiceover/checklist.md) parts A–D for Val (result row empty)** | +| C75 | Diff / tree divider | #322 | built | on `main` (#484 via #490): `code-review-diff.spec.ts` "file tree divider resizes with keyboard and pointer and persists on reload"; `code-review-a11y.spec.ts` (reached by Tab as a separator); `code-review-navigation.spec.ts` (tree folds when narrow) | — | — | open (VoiceOver) | +| C76 | Keyboard path, 900×600 and 1440×900 | #327 | built; `main` (#490) requires the CR-3 to CR-7 controls and checks every text run in the CR-4 to CR-7 sections | `code-review-a11y.spec.ts`; `code-review-navigation.spec.ts` keyboard test; `code-review-scroll.spec.ts` (focus kept); per-slice keyboard cases in the overview, diff, agent and actions specs | — | — | **open: [VoiceOver checklist](2026-10-22-cr8-voiceover/checklist.md) parts A–D for Val (result row empty)** | | C77 | Overview column scroll | #323 | built (#393 `6c40547`) | `code-review-scroll.spec.ts` (#438 `afec85b`); journey R2 (overview moves, inbox doesn't) | — | — | — | | C78 | Diff pane scroll | #324 | built (#404 `e3527f4`) | `code-review-scroll.spec.ts`; `code-review-diff.spec.ts` "returning from Overview preserves… overall scroll position" | — | — | — | | C79 | File tree scroll | #324 | built (#404 `e3527f4`) | `code-review-scroll.spec.ts` (#438 `afec85b`) | — | — | — | @@ -155,7 +155,7 @@ Decisions 2–4 (C21, C31, C35, C38, C43's +) were settled by Val on 2026-10-25 | #323 AC2 | Pagination and refresh: no duplicates, lost scroll or cross-PR bleed; partial never zero | `code-review-overview.spec.ts` ×8 (failed pages, refresh, delayed bleed, partial first page, retry cursor); `code-review-overview.test.ts`; [cr4-overview](2026-10-23-cr4-overview.md) | `A-323` (activity complete) | — | met at fixture (#393 `6c40547`). A live account switch was not exercised | | #323 AC3 | Stale checks, inferred stack and comment-derived previews kept distinct | `code-review-overview.spec.ts` "stale checks and unavailable collections…"; `code-review-overview.test.ts` (stack, preview destinations, stale retention); `code-review-read-service.test.ts` | `A-323` (deployment; stack unavailable) | — | met at fixture (#393 `6c40547`) | | #323 AC4 | Hostile Markdown/HTML, unsafe schemes, misleading previews inert | `code-review-overview.spec.ts` "hostile Markdown is inert…"; `code-review-regressions.spec.ts` "hostile content"; `code-review-overview.test.ts` "preview destinations reject…" | — | — | met at fixture (#393 `6c40547`). No live untrusted PR content was run | -| #323 AC5 | Long titles, large descriptions, narrow windows readable; composer clearance | `code-review-overview.spec.ts` (900×600, final Comment in viewport); `code-review-scroll.spec.ts` (#438 `afec85b`) | — | — | met at fixture (#393). Candidate `cr-v050-followups` measures clearance next to CR-6's inline review/Ask section (C61). **Open:** VoiceOver (Val) | +| #323 AC5 | Long titles, large descriptions, narrow windows readable; composer clearance | `code-review-overview.spec.ts` (900×600, final Comment in viewport); `code-review-scroll.spec.ts` (#438 `afec85b`) | — | — | met at fixture (#393). On `main` (#491) measures clearance next to CR-6's inline review/Ask section (C61). **Open:** VoiceOver (Val) | | #323 AC6 | Every visible write control is connected to a verified CR-7 capability or is unavailable; no mutation by #323 | At #393 all writes were unavailable ([cr4-overview](2026-10-23-cr4-overview.md)). Since #416 `b3aad0d` they are connected through `code-review:write` (`code-review-actions.spec.ts`) | — | see #326 AC6 | met at fixture | | #324 AC1 | R4 overview-plus-diff, tree, folds, selected file through real controls; match approved reference geometry | `code-review-diff.spec.ts` "captures populated R4 diff geometry and narrow keyboard file selection"; journey R4 populated (#464 `3d72c6e`); review-flow (#441 `eca2bd9`). Evidence: [cr5-remote-diffs](2026-10-23-cr5-remote-diffs.md) | — | — | met at fixture for the controls (#404 `e3527f4`). **Open:** approved-reference geometry, since the original R4 is missing (Val) | | #324 AC2 | Old/new line references for additions, deletions, renames, no final newline; context keeps them | `code-review-diff.test.ts` ×9; `code-review-diff.spec.ts` "review: deleted ranges and renamed paths retain base anchors…", "old-line anchors name the merge base…"; [cr5-merge-base](2026-10-24-cr5-merge-base.md) | `A-324` (anchor; merge-base match) | — | met at fixture (#404 `e3527f4`), plus an authenticated service probe | @@ -190,13 +190,9 @@ Decisions 2–4 (C21, C31, C35, C38, C43's +) were settled by Val on 2026-10-25 ## Staged enablement and rollback -**Candidate `cr-v050` (2026-11-02), awaiting Val's approval:** the switch is replaced by a persisted setting, Settings → -General → "Show Code Review in the rail", on by default. An old explicit value other than `"on"` stays off until the -person chooses (`shared/layout.ts`, `code-review-enablement.spec.ts`). This takes Stage 2's product step before Stage 1 -and 2's gates have run, so that one candidate can carry them all. It reaches nobody until Val merges and releases it; -[the Val session](2026-11-02-v0.5.0-val-session.md) lists the gates to pass or accept first. +**From v0.4.2 (Val's decision, 2026-11-03):** a persisted setting replaces the switch: Settings → General → "Show Code Review in the rail", off by default in built apps and on in development builds. The old switch's value is honoured until the layout saves a choice (`shared/layout.ts`, `code-review-enablement.spec.ts`). The stages below still hold: turning it on by default is Stage 2, and a product PR Val approves. -Before that candidate, the only switch was the per-home `localStorage["modex.codeReview"]`: +Before v0.4.2, the only switch was the per-home `localStorage["modex.codeReview"]`: development builds show the destination, packaged builds hide it unless the value is `"on"`. Nothing else reads it, and main's channels answer regardless of whether the destination is visible. @@ -206,7 +202,7 @@ it, and main's channels answer regardless of whether the destination is visible. - **Stage 2, after VoiceOver and Val's call on the references, decisions 1, 5, 6, 7, and the C58/C59 omissions:** remove the switch (or invert it) in a product PR that Val approves. That PR updates `docs/status.md` and this matrix. -**Rollback:** in the candidate, clearing "Show Code Review in the rail" hides the destination at once and across a +**Rollback:** from v0.4.2, clearing "Show Code Review in the rail" hides the destination at once and across a quit. On earlier builds, setting the value to anything but `"on"` (or clearing site data for that home) hides the destination on the next launch. That hides the surface; it does not cancel a write already sent. An in-doubt write keeps its record in `/review-actions/state.json` and an unconfirmed review run in @@ -233,11 +229,11 @@ the destination is visible. A product rollback (removing a capability) is a code ## Follow-ups found in this refresh (agent work, not gates) - C22 is closed on `main`: #477 binds the accepted `overview.locator`, which ends D6. -- Done in candidate `cr-v050`: `code-review-a11y.spec.ts` requires the overview, diff, review and write controls +- Done in `main` (#490): `code-review-a11y.spec.ts` requires the overview, diff, review and write controls (C76). Its new per-text contrast check found the file header and tree deletion count at 4.02:1 in OpenCoven; fixed. -- Done in candidate `cr-v050`: `code-review-budgets.spec.ts` gates the agreed opening budgets (AC5), and +- Done in `main` (#490): `code-review-budgets.spec.ts` gates the agreed opening budgets (AC5), and `code-review-actions.spec.ts` proves drafts stay hidden after sign-out (#326). -- Done in candidate `cr-v050` (#481, #484): asserting specs for C50, C51, C63, C66, C69, C71 and C75. Done in `cr-v050-followups`: C61's clearance beside CR-6's inline Ask section. +- Done in `main` (#490) (#481, #484): asserting specs for C50, C51, C63, C66, C69, C71 and C75. Done in #491: C61's clearance beside CR-6's inline Ask section. - CR-4's and CR-5's authenticated evidence is service-level on pre-merge candidates (`A-323`, `A-324`); an authenticated Electron read of the overview and diff on `main` needs Val's authorization (GET only). diff --git a/docs/reviews/2026-11-02-v0.5.0-candidate.md b/docs/reviews/2026-11-02-v0.5.0-candidate.md index 172024be..f38643a7 100644 --- a/docs/reviews/2026-11-02-v0.5.0-candidate.md +++ b/docs/reviews/2026-11-02-v0.5.0-candidate.md @@ -1,6 +1,6 @@ # v0.5.0 candidate review (2026-11-02) -**Status: a candidate. Nothing here is merged, tagged or released, and no live or human gate has run on it.** +**Superseded on 2026-11-03.** Val merged this work (#490, #491) and chose v0.4.2 with Code Review hidden, not a v0.5.0 with it on; see the [v0.4.2 review](2026-11-03-v0.4.2-release-review.md). What follows is the record of the candidate as tested. No live or human gate has run on it. Branch `cr-v050`, on `main` at `b785054e88836ac9df5ca917425031ab697d4d30`. The candidate is `370db33b877fa143cdfe463c6c8dda6046c9afc0`; the commit after it adds only this page's results. diff --git a/docs/reviews/2026-11-02-v0.5.0-val-session.md b/docs/reviews/2026-11-02-v0.5.0-val-session.md index 4538d11d..ea1eaf91 100644 --- a/docs/reviews/2026-11-02-v0.5.0-val-session.md +++ b/docs/reviews/2026-11-02-v0.5.0-val-session.md @@ -1,15 +1,14 @@ -# v0.5.0 candidate: Val's one session (2026-11-02) +# Code Review and conversation acceptance: Val's one session (2026-11-02) Everything left for a person on Code Review, CF-3 and CF-8, in one order, on one candidate. Agents have marked none of these rows passed. The rows' full wording stays in the linked checklists, which remain the receipts' home. -**Candidate:** the head of branch `cr-v050`; its tested SHA is in [the candidate review](2026-11-02-v0.5.0-candidate.md). It carries the default-on Code -Review setting, so step 1 is the decision the rest depends on. +**Source:** current `main`, which carries #490 and #491. On 2026-11-03 Val chose v0.4.2 with Code Review hidden (row 1.1), so this session runs after that release and decides whether Code Review can later go on by default. Write the SHA you test on every receipt. **One build for everything** (about 2 minutes). Every launch below starts from here: ```sh -cd /.worktrees/cr-v050/apps/desktop # at the candidate SHA +cd /apps/desktop pnpm install --frozen-lockfile && pnpm build git rev-parse HEAD # write this SHA on every receipt ``` @@ -23,7 +22,7 @@ Post each answer on the issue named. Modex builds nothing further until you do. | # | Decide | What to look for | Receipt | | --- | --- | --- | --- | -| 1.1 | Ship Code Review on by default before every gate below has passed, or only after them | This candidate takes [Stage 2](2026-11-01-cr8-acceptance-matrix.md#staged-enablement-and-rollback) early so one build can carry them. The alternative is to flip `DEFAULT_LAYOUT.codeReview` to false and release hidden | Comment on #319 | +| 1.1 | Ship Code Review on by default before every gate below has passed, or only after them | **Answered 2026-11-03: hidden in v0.4.2, on only after the gates.** Turning it on later is a small product PR | #319 | | 1.2 | Decision 1: avatars (C16, C81): login only, or fetch avatars | [Open decisions](../design/code-review.md#open-decisions). Fetching adds an image request per person to GitHub's avatar host | #319 | | 1.3 | Decision 5: the Viewed toggle (C65) | Not built. Build it, or record it as left out | #324 | | 1.4 | Decision 6: new tab (C07) and decision 7: access-mode indicator (C82) | C07 is disabled and C82 is words in the panel today | #319 | @@ -39,8 +38,8 @@ env -u MODEX_E2E -u MODEX_DEMO MODEX_HOME=/tmp/modex-v050-rollback npx electron | Row | Do | Expect | Receipt | | --- | --- | --- | --- | -| 2.1 | Look at the rail on this fresh home | Code Review is there | Comment on #327 | -| 2.2 | Settings → General → clear "Show Code Review in the rail", then quit and reopen | It leaves at once and stays gone | #327 | +| 2.1 | Look at the rail on this fresh home | Code Review is not there | Comment on #327 | +| 2.2 | Settings → General → tick "Show Code Review in the rail"; clear it again; then quit and reopen | It appears, then leaves at once and stays gone | #327 | ## 3. CR-8 keyboard and VoiceOver (about 45 minutes) diff --git a/docs/reviews/2026-11-03-v0.4.2-release-review.md b/docs/reviews/2026-11-03-v0.4.2-release-review.md new file mode 100644 index 00000000..23bc42a0 --- /dev/null +++ b/docs/reviews/2026-11-03-v0.4.2-release-review.md @@ -0,0 +1,50 @@ +# v0.4.2 release review + +Desktop v0.4.2 is a patch release. On 2026-11-03 Val asked for the Code Review work to merge and for the next patch release, and chose v0.4.2 with Code Review hidden. The iPhone Companion stays at 0.4.0 (9); nothing in this release changes it. + +**Status: in preparation. Not tagged or published.** The sections below that need a tag or a signed build are empty until those steps run. + +## Scope since v0.4.1 + +| Area | Pull requests | +| --- | --- | +| Claude account pool: Claude Code profiles, OrcaRouter keys and opt-in rotation at a usage limit | #467 | +| Long threads open faster: Markdown far from the view is deferred | #463 | +| Local-model safety, in main only: context provenance, local-only auxiliary dispatch, refusal of known cloud-routed and non-generative models. Local coding execution stays unadmitted | #462, #461, #465 | +| Code Review, still hidden: a setting replaces the switch; links follow a renamed repository; diff buttons get theme colours; contrast repair in OpenCoven | #490, #491, #477 | +| Tests: Code Review a11y (C76), performance budgets, clearance (C61), CR-7 sign-out, CF-3 question drafts and YOLO boundaries | #490, #491, #487 | +| Worktree retirement safety | #488 | +| Records: CF-8, CF-3, CR-8 and issue-checkbox reconciliation | #459, #460, #464, #466, #468, #470, #472 to #474, #476, #483 | + +## Behaviour and compatibility + +- **Updates.** v0.4.1 reaches v0.4.2 through the background update, as v0.4.1 did from v0.4.0. No release workflow, signing script or updater file changed since v0.4.1, so no rc rehearsal is required by [the release procedure](../release-signing.md#rehearsing-with-an-rc-tag). +- **Code Review stays hidden.** A fresh built app doesn't show it, and makes no Code Review read. Settings → General → "Show Code Review in the rail" is new and visible to everyone, so people can now find and turn on a workspace whose gates are still open. Someone who set the old `modex.codeReview` switch to `on` keeps it. Any other value keeps it hidden. +- **Account pool.** New in Settings → Coding CLIs → Claude Code and opt-in. Unit and E2E coverage use a synthetic `claude`. No live OrcaRouter turn, second subscription or real limit message has run ([limits](../claude-accounts.md)). +- **A feature in a patch.** The account pool and the new setting are additions, not fixes. Val chose the patch number knowing the setting; v0.4.2 is also the first release to carry the pool. + +## Not established + +No live provider run, live GitHub write, VoiceOver or keyboard pass by a person, or reference comparison ran for this release. Those gates stay open in [Val's session](2026-11-02-v0.5.0-val-session.md), and they don't block v0.4.2 because Code Review stays hidden. CF-3, CF-7 and CF-8 human rows stay open as well. + +## Local preparation + +One local run on `4d46b46` (this release's product and test changes on top of `main` at `a4f2f70`), 2026-11-04 03:03 to 04:11 UTC, no reruns. Only this section was added after it. + +| Step | Result | +|---|---| +| `pnpm install --frozen-lockfile` | exit 0 | +| `pnpm build` | exit 0 | +| `pnpm test` | exit 0; desktop 2137 tests, 2117 passed, 20 skipped, 0 failed; other packages 147, 16, 27 (6 skipped), 10 and 16, 0 failed | +| `pnpm test:e2e` | exit 0; desktop 497 passed, 7 skipped (33.1 min); site 7 passed | +| `pnpm --filter @modex/desktop dist` | exit 0; ad-hoc signed, bundle version 0.4.2 | +| `pnpm --filter @modex/store-desktop build` | exit 0 | +| `pnpm test:e2e` with `MODEX_PACKAGED_APP` set to the ad-hoc bundle | exit 0; desktop 497 passed, 7 skipped (28.4 min); site 7 passed | + +This Mac has no Developer ID certificate or notary key, so this run does not exercise signing, notarization, the native updater acceptance or Gatekeeper. The `Release` workflow does those, and their receipts belong under "Published". + +Log: `rehearsal.log`, kept outside the tree (SHA-256 `0b4e04322611a2cb9dfca8709880ed0f76889e508afb3542cda138d9b7e7ab21`). + +## Published + +_Not published._ diff --git a/docs/status.md b/docs/status.md index 4b0550d4..da7c880e 100644 --- a/docs/status.md +++ b/docs/status.md @@ -4,7 +4,7 @@ The living map of what has shipped, what is on `main` but unreleased, what is in what is next. Update it in the same PR that changes any of those; a reader should be able to plan the next session from this page alone. Release evidence lives in [`reviews/`](reviews/). -_Last updated 2026-11-01 UTC._ +_Last updated 2026-11-04 UTC._ ## Shipped @@ -71,7 +71,7 @@ Three review-found repairs landed before v0.4.0 and shipped in it; their evidenc ## On main, unreleased -The published application source is `6a97d23aaea315b29008890f25dde2d7ea07f1d0` (v0.4.1). Later source deliveries remain unreleased: #462 records local-model context provenance; #461 enforces local-only auxiliary dispatch; #465 refuses known cloud-routed and non-generative models; #463 (`32f1cb4`) defers Markdown far from the view so long threads open fast. Main still does not construct `LocalExecution`; native qualification and product admission remain separate gates. #459, #460 and #464 (`3d72c6e`, the CR-3 acceptance re-run) are evidence/documentation deliveries after the release source. +The published application source is `6a97d23aaea315b29008890f25dde2d7ea07f1d0` (v0.4.1). v0.4.2 is in preparation with Code Review hidden ([review](reviews/2026-11-03-v0.4.2-release-review.md)). Later source deliveries remain unreleased: #462 records local-model context provenance; #461 enforces local-only auxiliary dispatch; #465 refuses known cloud-routed and non-generative models; #463 (`32f1cb4`) defers Markdown far from the view so long threads open fast. Main still does not construct `LocalExecution`; native qualification and product admission remain separate gates. #459, #460 and #464 (`3d72c6e`, the CR-3 acceptance re-run) are evidence/documentation deliveries after the release source. ## v0.4.1 publication and remaining acceptance @@ -246,8 +246,8 @@ before relying on a tick. | [#280](https://github.com/TypeSafeAI/modex/issues/280) CF-7 | 11/14 | Steps 1, 2 and 4: model-proposed relations, file hashes and selection/notes context are not built; step-level linking was retired | Val reviews the two saved Notes and records steps 1, 2 and 4 as deferred or retired | | [#281](https://github.com/TypeSafeAI/modex/issues/281) CF-8 | 8/27 | Keyboard A1–A8, VoiceOver B1–B7, CF-3 D1–D5, P1–P4 and P6; memory measurement; ChatGPT multi-agent restriction check; staged release recommendation; list of remaining visual differences. C1–C5, V1–V3 and P5 are deferred | Val chooses the candidate, then runs A1–A8 and D1–D5 | | [#319](https://github.com/TypeSafeAI/modex/issues/319) CR tracker | 2/10 | CR-3 to CR-8 acceptance; R1–R4 reconstruction comparison | Land #472 (current CR-8 matrix) | -| [#322](https://github.com/TypeSafeAI/modex/issues/322) CR-3 | 10/14 | Original-reference comparison (AC1), keyboard/VoiceOver (AC4); clearance beside CR-6's inline section is measured only in candidate `cr-v050-followups`, not on `main` | Val runs the [CR-8 VoiceOver checklist](reviews/2026-10-22-cr8-voiceover/checklist.md) | -| [#323](https://github.com/TypeSafeAI/modex/issues/323) CR-4 | 10/14 | Original R1/R2 comparison, VoiceOver; clearance beside CR-6's inline section is measured only in candidate `cr-v050-followups`, not on `main`; avatars wait on decision 1 | Val decides on references | +| [#322](https://github.com/TypeSafeAI/modex/issues/322) CR-3 | 10/14 | Original-reference comparison (AC1), keyboard/VoiceOver (AC4); clearance beside CR-6's inline section is measured on `main` (#491); re-check the issue box against it | Val runs the [CR-8 VoiceOver checklist](reviews/2026-10-22-cr8-voiceover/checklist.md) | +| [#323](https://github.com/TypeSafeAI/modex/issues/323) CR-4 | 10/14 | Original R1/R2 comparison, VoiceOver; clearance beside CR-6's inline section is measured on `main` (#491); re-check the issue box against it; avatars wait on decision 1 | Val decides on references | | [#324](https://github.com/TypeSafeAI/modex/issues/324) CR-5 | 12/14 | Original R4 comparison (AC1); the Viewed toggle waits on decision 5 | Val decides on R4 references or re-captures | | [#325](https://github.com/TypeSafeAI/modex/issues/325) CR-6 | 13/14 | Authorized live native smoke (AC6) | Val names `owner/repo#n` for the smoke | | [#326](https://github.com/TypeSafeAI/modex/issues/326) CR-7 | 12/14 | No on-main test of draft visibility after sign-out; live canary on a disposable repository (AC6) | Add the sign-out test; Val names a disposable repository | @@ -263,7 +263,7 @@ before relying on a tick. ## In flight -- **v0.5.0 candidate: Code Review on by default** (#319, #327; branch `cr-v050`, full run clean on `370db33`). Carries #481, #484 and #485, a11y coverage of the CR-4 to CR-7 controls (C76), performance budgets as gates, the CR-7 sign-out and CF-3 question-card regressions, and a default-on setting with a rollback in Settings → General that replaces the `modex.codeReview` switch. It stays a candidate: nothing is merged, tagged or released, and no live or human gate has run on it. [Candidate review](reviews/2026-11-02-v0.5.0-candidate.md); [Val's one session](reviews/2026-11-02-v0.5.0-val-session.md). The C61 clearance test beside CR-6's inline section is on the stacked branch `cr-v050-followups`, whose own full run passed on `7e45440`. +- **Code Review setting and its gates** (#490, #491; #319, #327). A persisted setting in Settings → General replaces the `modex.codeReview` switch, off by default as Val chose on 2026-11-03, on in development builds. Adds a11y coverage of the CR-4 to CR-7 controls (C76), performance budgets as gates, CR-7 sign-out and CF-3 question-card regressions, C61 clearance beside CR-6's inline section, and theme colours for the diff's plain buttons. No live or human gate has run. [v0.4.2 review](reviews/2026-11-03-v0.4.2-release-review.md); [Val's session](reviews/2026-11-02-v0.5.0-val-session.md). - **Unmerged-work retirement safety repair (#486):** signed candidate `783898a` is pushed on `retire-safety-repair`; it requires matching ownership, archives index and working state separately, verifies raw working/ignored recovery, and retains the original checkout indefinitely instead of deleting files. Focused disposable-fixture results and design are in [the repair review](reviews/2026-11-02-retirement-safety-repair.md). Pinned installation, build, full unit tests (2,325 passed, 26 skipped), typecheck and independent follow-up review passed; protected CI and landing remain pending. No real worktree retirement or repair-specific Electron e2e has been exercised. - **Claude account pool with OrcaRouter keys.** Settings → Coding CLIs → Claude Code adds separate Claude Code profiles (each with its own `CLAUDE_CONFIG_DIR` and browser login), OrcaRouter gateway keys in OS-encrypted storage, an account for new conversations, and opt-in rotation when an account hits its usage limit. Threads bind to their first account; a turn that had not started work retries once on the next account. Unit and E2E coverage use a synthetic `claude`; no live OrcaRouter turn, second subscription or real limit message has been run. OmniRouter was evaluated and not integrated. [Design and limits](claude-accounts.md).