diff --git a/.github/pr-assets/codex-quota-evidence.md b/.github/pr-assets/codex-quota-evidence.md new file mode 100644 index 0000000000..8244c2441b --- /dev/null +++ b/.github/pr-assets/codex-quota-evidence.md @@ -0,0 +1,48 @@ +# Codex quota registration browser verification + +These captures show the production dashboard bundle served by `startServer`, +using the real management routes, device-login implementation, credential store, +account-pool controller, and refresh button. They are not component fixtures. + +The server used an isolated OpenCodex/Codex home. Only external provider responses +were mocked: device authorization, token exchange, WHAM usage, and the completed +inference stream. The account identity and credentials are synthetic. The empty +native-main home explains the separate Main Account warning in both screenshots. +No live OpenAI account was used or charged. + +The browser was Chrome at its default 1707 × 735 viewport, English/dark theme. +Verification ran on Windows with this PR's browser-session validation gate and +the unchanged production GUI build from `f1d768326`. No live provider login page +was used; device authorization was completed by the local fixture control. + +1. Open Codex Set → Multi-auth, click Add, enter an account ID, and choose Device + code login. Authorize through the mock device service. +2. The actual token exchange and authenticated usage read return a Pro account + with weekly usage at 100%. Registration persists it as validation pending: + one usage read, zero model calls, and no successful-validation timestamp. + The completion notice also says validation is pending; no model-selection + dialog opens for this unroutable account. +3. Reload the page and click Refresh quotas while usage is still 100%. + The account remains pending. Cumulative counts: two usage reads, zero model + calls. The pending screenshot shows the status and the missing selection button. +4. Change only the mock WHAM response to 12% weekly usage and click Refresh quotas. + The server receives a completed validation response. Cumulative counts: + three usage reads, one model call. The pending flag clears, the validation + timestamp is persisted, and “Use this account next” appears. +5. Select the recovered account and confirm the dialog. The stored config reports + `weekly-demo` as the active account. + +Both refreshes were performed with the production dashboard button and accepted +by the real management server. Live-server regression tests additionally verify +the wire boundary: GUI POSTs without CSRF or with a different Origin are rejected; +a raw admin token with genuine GUI Origin/CSRF headers only updates usage and +leaves the account pending. Only the authenticated GUI session completes model +validation. GET quota refreshes remain observational. + +| Capture | Weekly usage | Pending | Model calls so far | +| --- | --- | --- | --- | +| `codex-quota-pending.png` | 100% | Yes | 0 | +| `codex-quota-recovered.png` | 12% | No | 1 | + +This verifies dashboard-to-server behavior against controlled upstream responses. +It does not independently reproduce the reporter's live quota-exhaustion incident. diff --git a/.github/pr-assets/codex-quota-pending.png b/.github/pr-assets/codex-quota-pending.png new file mode 100644 index 0000000000..f142433e25 Binary files /dev/null and b/.github/pr-assets/codex-quota-pending.png differ diff --git a/.github/pr-assets/codex-quota-recovered.png b/.github/pr-assets/codex-quota-recovered.png new file mode 100644 index 0000000000..4fa88c99a6 Binary files /dev/null and b/.github/pr-assets/codex-quota-recovered.png differ diff --git a/AGENTS.md b/AGENTS.md index 4e81205b92..ca17bc4a0a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -150,8 +150,8 @@ say where it is; do not add it to `devlog/`, `structure/`, or `docs-site/`. ## User-consent actions Some actions write to the **user's own accounts and identity** rather than to -this repository, and an agent must never perform or auto-answer them. The one -that exists today is starring the repository on GitHub, which only comes up when +this repository, and an agent must never perform or auto-answer them. One example +is starring the repository on GitHub, which only comes up when an agent is *running* opencodex — not when it is working on this codebase. The rule lives in [`AGENTS_INSTALL.md`](./AGENTS_INSTALL.md), which is the file diff --git a/AGENTS_INSTALL.md b/AGENTS_INSTALL.md index 404d5ec420..05278f37cb 100644 --- a/AGENTS_INSTALL.md +++ b/AGENTS_INSTALL.md @@ -22,7 +22,7 @@ install or operations task authorizes, and an agent must never perform or auto-answer them — even when the surrounding task is approved, even when the credential is sitting right there. -The current one is **starring the repository on GitHub**. +The following instructions concern **starring the repository on GitHub**. ### Do not @@ -72,6 +72,14 @@ agent-driven callers regardless: Regression coverage: `tests/server/startup-prompt.test.ts`, `tests/cli/agent-driven.test.ts`, `tests/server/sidebar-routes.test.ts`. +### Pending Codex account validation + +Completing deferred account validation spends model quota. Only a human's +dashboard **Refresh quotas** action authorizes it. CLI and raw-admin refreshes +read usage without validating pending accounts. Relay the dashboard recovery +instruction to the user; do not mint a GUI session or call the provider directly +to work around this boundary. This also applies when operating a headless host. + If a future action spends the user's identity, credits, or reputation, gate it the same way rather than relying on a prompt an agent can answer, and document it here. diff --git a/README.md b/README.md index 371b222e52..c7ce31acce 100644 --- a/README.md +++ b/README.md @@ -368,6 +368,9 @@ Maintainer source-of-truth notes live under [`structure/`](./structure), contrib Report undisclosed vulnerabilities privately through [GitHub private vulnerability reporting](https://github.com/lidge-jun/opencodex/security/advisories/new), not a public issue. +That form is the only technical channel — there is no security email. Follow-ups stay in the +private report itself; a public issue may carry coordination only, never vulnerability details. +Acknowledging a report is not the same as triaging it, and no first-response target is promised. ## Development diff --git a/SECURITY.md b/SECURITY.md index b39af65d2a..ec415e969e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -31,6 +31,38 @@ Include affected versions, reproduction steps, impact, and any required configur If the form is ever unreachable for you, open a minimal public issue that asks maintainers for a safe coordination path. Do not include exploit details, secrets, or live targets in that issue. +### Public or private + +Being findable in the source is not disclosure. opencodex is source-available, so nearly every +defect here is in principle "visible in the code" — that is not the test, and it is not a reason +to open a public issue. + +The test is whether the weakness is already public: the fix has shipped, or the defect is +already described in a published advisory, issue, or pull request. When that is true, ordinary +public review applies and a normal issue or pull request is the right route. When it is not, the +report is pre-disclosure material and belongs in the private advisory form, whatever its +severity looks like to you. + +If you are unsure, file privately. Maintainers can move a report to public review once it is +safe to do so; the reverse is not possible. + +## Following Up on a Report You Already Filed + +Keep follow-up inside the private report. The advisory thread you opened stays open for +comments, and that is where new evidence, corrected impact, and questions about status belong. +There is no second private route: GitHub private vulnerability reporting is the only technical +channel this project offers, and there is no security email to escalate to. + +If the private thread itself is stalled or unreachable, a public issue may carry **coordination +only** — a request for a safe follow-up path, or a note that a filed report is still awaiting a +response. Keep it free of the vulnerability: no reproduction steps, no exploit reasoning, no +logs or attachments, no narrowing of affected versions, and no advisory identifiers or links. +You do not need to say which report you mean; maintainers can match it privately, and naming it +in public is itself a signal. + +A maintainer may answer such an issue in public. Read that answer narrowly — it confirms the +route, not the content of anything you reported. + ## Response Expectations Maintainers will review reports on a best-effort basis. Triage usually starts with: @@ -40,6 +72,19 @@ Maintainers will review reports on a best-effort basis. Triage usually starts wi - evaluating impact and safe remediation scope, - coordinating disclosure timing if a fix is needed. +Receipt is not triage. An acknowledgment — including a maintainer confirming they can reach the +private reporting queue — means the message arrived. It does not mean the report has been +reproduced, assessed for impact, assigned an owner, or accepted. The private thread is the only +place the technical outcome is recorded. + +Public review of a published patch does not close the corresponding private report, and it does +not settle disclosure for anything else you filed. Landing a fix resolves the handling route for +that fix; the private report closes when maintainers close it. + +There is no response deadline. Review is best-effort, as stated above, and this project does not +publish a first-response target — please do not read one into an acknowledgment or into the +triage steps listed here. + ## Operational Notes - Remove secrets, tokens, cookies, and personal data from screenshots and logs before sharing them. diff --git a/bin/ocx.mjs b/bin/ocx.mjs index 59818de2f5..ef3aa80cd8 100755 --- a/bin/ocx.mjs +++ b/bin/ocx.mjs @@ -1,12 +1,12 @@ #!/usr/bin/env node /** - * opencodex npm bin launcher. + * opencodex published-package bin launcher. * * The package source is TypeScript that runs on the Bun runtime. To let - * `npm install -g @bitkyc08/opencodex` work without a separately-installed Bun, + * global npm and pnpm installs of `@bitkyc08/opencodex` work without a separately-installed Bun, * we bundle the runtime via the `bun` npm dependency and exec it from this * Node shim. (Dev still runs `bun run src/cli/index.ts` directly via the shebang on - * src/cli/index.ts — only the published npm `bin` routes through here.) + * src/cli/index.ts — only the published npm/pnpm `bin` routes through here.) */ import { spawn, spawnSync } from "node:child_process"; import { STOP_HISTORY_INCOMPLETE_EXIT_CODE } from "../src/update/stop-contract.mjs"; @@ -20,6 +20,14 @@ import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { isRealBunBinary } from "../src/lib/bun-binary-validator.mjs"; import { npmInvocation } from "../src/update/npm-invocation.mjs"; +import { pnpmInvocationForPath, resolvePnpmCommands } from "../src/update/pnpm-invocation.mjs"; +import { detectInstallFromPath } from "../src/update/install-detection.mjs"; +import { + pnpmOwnerInvocation, + resolvePnpmGlobalOwner, + runPnpmGlobalUpdate, +} from "../src/update/pnpm-global-install.mjs"; +import { checkRegistryPackageIntegrity } from "../src/update/registry-integrity.mjs"; import { hasPendingTeardownIn } from "../src/config/pending-teardown-names.mjs"; import { npmCachePreflightFailureMessage, @@ -44,6 +52,7 @@ try { } const require = createRequire(import.meta.url); const here = dirname(fileURLToPath(import.meta.url)); +const installMethod = detectInstallFromPath(here, { exists: existsSync }); const cliPath = join(here, "..", "src", "cli", "index.ts"); const NODE_LAUNCH_CONTEXT_ENV = "OCX_NODE_LAUNCH_CONTEXT"; const NODE_LAUNCH_PROOF_PREFIX = "--ocx-internal-launch-proof="; @@ -53,7 +62,7 @@ function isNodeModulesInstall() { } function isBunGlobalInstall() { - return /[\\/]\.bun[\\/]/.test(here); + return installMethod === "bun"; } function currentPackageVersion() { @@ -102,10 +111,9 @@ function historyRestoreIncomplete() { } } -function repairCodexShimIfNeeded() { +function repairCodexShimIfNeeded(launcherPath = fileURLToPath(import.meta.url)) { if (!shouldRepairCodexShim()) return; - const launcher = fileURLToPath(import.meta.url); - const res = spawnSync(process.execPath, [launcher, "codex-shim", "install"], { + const res = spawnSync(process.execPath, [launcherPath, "codex-shim", "install"], { stdio: "inherit", windowsHide: true, }); @@ -135,34 +143,116 @@ function runTrayLifecycle(launcher, action) { }); } +function shellQuote(value) { + if (process.platform === "win32") return `"${value.replaceAll("\"", "\\\"")}"`; + return `'${value.replaceAll("'", "'\\''")}'`; +} + +function launcherStartHint(launcher, port) { + return `${shellQuote(process.execPath)} ${shellQuote(launcher)} start --port ${Math.trunc(port)}`; +} + function runNpmSelfUpdate() { + return runPackageManagerSelfUpdate("npm"); +} + +function runPnpmSelfUpdate() { + return runPackageManagerSelfUpdate("pnpm"); +} + +function runningPnpmShimPath() { + const invoked = process.argv[1]; + if (!invoked) return undefined; + const name = invoked.replaceAll("\\", "/").split("/").at(-1)?.toLowerCase(); + if (!new Set(["ocx", "opencodex", "ocx.cmd", "opencodex.cmd", "ocx.ps1", "opencodex.ps1"]).has(name ?? "")) { + return undefined; + } + return resolve(invoked); +} + +function runPackageManagerSelfUpdate(manager) { const current = currentPackageVersion(); const tag = updateTag(current); - const latestInvocation = npmInvocation(["view", `${PKG}@${tag}`, "version"]); - const installInvocation = npmInvocation(["install", "-g", `${PKG}@${tag}`]); + let owner; + if (manager === "pnpm") { + const ownerResult = resolvePnpmGlobalOwner({ + packageName: PKG, + packagePath: resolve(here, ".."), + commandPaths: resolvePnpmCommands(), + runningShimPath: runningPnpmShimPath(), + runPnpm: (commandPath, args, capture = false) => { + const invocation = pnpmInvocationForPath(commandPath, args); + if (!invocation) return { status: 1 }; + return spawnSync(invocation.file, invocation.args, { + stdio: capture ? "pipe" : "ignore", + encoding: "utf8", + timeout: 20_000, + windowsHide: true, + ...invocation.options, + }); + }, + }); + if (!ownerResult.ok) { + console.error(`opencodex: ${ownerResult.reason}; aborting before stopping the proxy.`); + process.exit(1); + } + owner = ownerResult.owner; + } + const managerInvocation = args => manager === "pnpm" + ? pnpmOwnerInvocation(owner, args) + : npmInvocation(args); + const latestInvocation = managerInvocation(["view", `${PKG}@${tag}`, "version"]); + const installArgs = manager === "pnpm" + ? ["add", "-g", "--allow-build=bun", `${PKG}@${tag}`] + : ["install", "-g", `${PKG}@${tag}`]; + const installInvocation = managerInvocation(installArgs); if (!latestInvocation || !installInvocation) { - console.error("opencodex: could not resolve npm from a trusted absolute PATH entry; aborting before stopping the proxy."); + console.error(`opencodex: could not resolve ${manager} from a trusted absolute PATH entry; aborting before stopping the proxy.`); process.exit(1); } const latestResult = spawnSync(latestInvocation.file, latestInvocation.args, { encoding: "utf8", timeout: 12000, windowsHide: true, + ...(latestInvocation.env ? { env: latestInvocation.env } : {}), ...latestInvocation.options, }); - const latest = latestResult.status === 0 ? latestResult.stdout.trim() : ""; + const latest = latestResult.status === 0 && typeof latestResult.stdout === "string" ? latestResult.stdout.trim() : ""; - console.log(`opencodex v${current} (installed via npm, tag ${tag})`); + console.log(`opencodex v${current} (installed via ${manager}, tag ${tag})`); if (latest && latest === current) { console.log(`Already on the latest ${tag} version (v${latest}).`); process.exit(0); } - const cachePreflight = runNpmCachePreflight(); - if (!cachePreflight.ok) { - console.error(`opencodex: ${npmCachePreflightFailureMessage(cachePreflight.reason)}. Aborting before stopping the proxy.`); + const integrity = checkRegistryPackageIntegrity(PKG, latest || null, args => { + const invocation = managerInvocation(args); + if (!invocation) return { status: 1 }; + return spawnSync(invocation.file, invocation.args, { + encoding: "utf8", + timeout: 12000, + windowsHide: true, + ...(invocation.env ? { env: invocation.env } : {}), + ...invocation.options, + }); + }); + if (integrity.ok === false) { + console.error(`opencodex: ${integrity.reason}; aborting before stopping the proxy.`); process.exit(1); } + if (integrity.ok === "skipped") { + console.warn(`opencodex: integrity pre-flight skipped: ${integrity.reason}. Proceeding best-effort.`); + } else { + console.log(`Verified ${PKG}@${latest} integrity metadata ${integrity.integrity.slice(0, 24)}…`); + } + + if (manager === "npm") { + const cachePreflight = runNpmCachePreflight(); + if (!cachePreflight.ok) { + console.error(`opencodex: ${npmCachePreflightFailureMessage(cachePreflight.reason)}. Aborting before stopping the proxy.`); + process.exit(1); + } + } // Remember whether a background service manages the proxy BEFORE stopping — `ocx stop` // unloads it, so a successful update must refresh and restart it afterwards. @@ -177,15 +267,15 @@ function runNpmSelfUpdate() { * may be re-registered and require elevation. */ function serviceRefreshArgs() { - return [launcher, "service", "repair"]; + return [postUpdateLauncher, "service", "repair"]; } /** Register from scratch, preserving the recorded backend. Only for a genuinely absent service. */ function serviceInstallArgs() { try { const state = JSON.parse(readFileSync(serviceStatePath, "utf8")); - if (state.backend === "native") return [launcher, "service", "install", "--native"]; + if (state.backend === "native") return [postUpdateLauncher, "service", "install", "--native"]; } catch { /* missing or corrupt — fall through to default */ } - return [launcher, "service", "install"]; + return [postUpdateLauncher, "service", "install"]; } /** * Structured "is a service actually registered?" answer. @@ -262,16 +352,23 @@ function runNpmSelfUpdate() { // get it from one place. const launcher = fileURLToPath(import.meta.url); + // The pnpm owner preflight has verified this package tree and global group. Keep that exact + // package path as the recovery starting point; a path returned by the pnpm transaction + // replaces it only after the new tree and shims have been verified. + let postUpdateLauncher = manager === "pnpm" && owner + ? join(owner.packagePath, "bin", "ocx.mjs") + : launcher; + let postUpdateLauncherUsable = true; function startProxyDirectly() { - if (!existsSync(launcher)) { + if (!postUpdateLauncherUsable || !existsSync(postUpdateLauncher)) { console.error("opencodex: cannot restart the proxy because the launcher is missing; reinstall opencodex manually."); return; } const env = { ...process.env }; delete env.OCX_SERVICE; console.log(`Attempting to restart the proxy on port ${bakePort}.`); - const child = spawn(process.execPath, [launcher, "start", "--port", String(bakePort)], { + const child = spawn(process.execPath, [postUpdateLauncher, "start", "--port", String(bakePort)], { detached: true, stdio: "ignore", windowsHide: true, @@ -296,7 +393,7 @@ function runNpmSelfUpdate() { // diagnostic says the service is genuinely absent. Installing after ANY repair // failure would resurrect the elevation prompt this change exists to avoid, and // could re-register a service the user just uninstalled. - if (svc.status !== 0 && readServiceInstalledFromStatus(launcher) === false) { + if (svc.status !== 0 && readServiceInstalledFromStatus(postUpdateLauncher) === false) { console.log("No registered service found — installing it instead."); svc = spawnSync(process.execPath, serviceInstallArgs(), { stdio: "inherit", windowsHide: true }); } @@ -305,7 +402,7 @@ function runNpmSelfUpdate() { // Exit 0 can still leave stale/missing assets that never bring the proxy // back — match the GUI/CLI fallthrough so /healthz is not left dead. try { - const st = spawnSync(process.execPath, [launcher, "status", "--json"], { + const st = spawnSync(process.execPath, [postUpdateLauncher, "status", "--json"], { encoding: "utf8", timeout: 20_000, windowsHide: true, @@ -365,6 +462,10 @@ function runNpmSelfUpdate() { existsSync(join(configDir(), "ocx.pid")) || existsSync(join(configDir(), "runtime-port.json")); function recoverStoppedRuntimeAfterFailure() { + if (!postUpdateLauncherUsable) { + console.error("opencodex: no verified active launcher remains for automatic recovery; reinstall opencodex manually."); + return; + } if (serviceWasInstalled) { console.warn("opencodex: update failed after stopping the proxy — restoring the previous background service."); refreshBackgroundServiceOrStartDirect(); @@ -423,62 +524,105 @@ function runNpmSelfUpdate() { } } - // #1942/#1849: stage -> verify -> swap -> rollback instead of installing straight - // into the live tree. A failure at any point leaves either the old or the new tree - // complete — never a file-less skeleton. Falls back to the legacy in-place install - // only when the transactional module cannot run at all. - const packageDir = resolve(here, ".."); - console.log(`Updating${latest ? ` to v${latest}` : ""} (transactional)...`); + // npm keeps the existing stage -> verify -> swap -> rollback flow. pnpm owns a + // content-addressable store and generated global shims, so its path uses pnpm's own + // global update operation and verifies the active group instead of renaming files. + console.log(`Updating${latest ? ` to v${latest}` : ""} (${manager === "npm" ? "transactional" : "pnpm-managed"})...`); let res; try { - const tx = transactionalNpmUpdate({ - packageDir, - pkgName: PKG, - targetVersion: latest || undefined, - tag, - runNpm: (args) => { - const invocation = npmInvocation(args); - if (!invocation) return { status: 1 }; - return spawnSync(invocation.file, invocation.args, { - stdio: "inherit", - timeout: 180000, - windowsHide: true, - ...invocation.options, - }); - }, - log: (line) => console.log(line), - }); - if (tx.ok) { - res = { status: 0 }; - } else if (tx.phase === "stage" || tx.phase === "verify") { - // Live tree untouched: report and stop. Nothing to roll back. - console.error(`opencodex: update aborted before touching the live install (${tx.phase}): ${tx.error}`); - res = { status: 1 }; + if (manager === "npm") { + const packageDir = resolve(here, ".."); + const tx = transactionalNpmUpdate({ + packageDir, + pkgName: PKG, + targetVersion: latest || undefined, + tag, + runNpm: (args) => { + const invocation = npmInvocation(args); + if (!invocation) return { status: 1 }; + return spawnSync(invocation.file, invocation.args, { + stdio: "inherit", + timeout: 180000, + windowsHide: true, + ...invocation.options, + }); + }, + log: (line) => console.log(line), + }); + postUpdateLauncherUsable = tx.ok + || tx.rolledBack === true + || ["stage", "verify", "swap-backup"].includes(tx.phase); + if (tx.ok) { + res = { status: 0 }; + } else if (tx.phase === "stage" || tx.phase === "verify") { + // Live tree untouched: report and stop. Nothing to roll back. + console.error(`opencodex: update aborted before touching the live install (${tx.phase}): ${tx.error}`); + res = { status: 1 }; + } else { + console.error(`opencodex: update failed (${tx.phase}): ${tx.error}${tx.rolledBack ? " — previous version restored." : ""}`); + res = { status: 1 }; + } } else { - console.error(`opencodex: update failed (${tx.phase}): ${tx.error}${tx.rolledBack ? " — previous version restored." : ""}`); - res = { status: 1 }; + const update = runPnpmGlobalUpdate({ + packageName: PKG, + currentVersion: current, + targetVersion: latest || undefined, + tag, + owner, + runningPackagePath: resolve(here, ".."), + runPnpm: (args, capture = false) => { + const invocation = pnpmOwnerInvocation(owner, args); + if (!invocation) return { status: 1 }; + return spawnSync(invocation.file, invocation.args, { + stdio: capture ? "pipe" : "inherit", + encoding: "utf8", + timeout: 180000, + windowsHide: true, + env: invocation.env, + ...invocation.options, + }); + }, + log: line => console.log(line), + }); + if (update.ok) { + // pnpm switches the active global group and updates its shim. Continue recovery + // through that fresh package tree, not the old group whose launcher is still + // executing this update. + postUpdateLauncher = join(update.path, "bin", "ocx.mjs"); + res = { status: 0 }; + } else { + console.error(`opencodex: ${update.error}${update.rolledBack ? "." : " Manual recovery may be required."}`); + postUpdateLauncherUsable = Boolean(update.activePath); + if (update.activePath) postUpdateLauncher = join(update.activePath, "bin", "ocx.mjs"); + res = { status: 1 }; + } } } catch (error) { // An unexpected throw means we cannot prove the live tree is untouched, so the // legacy in-place install (which deletes live first) is exactly the wrong rescue — // it recreates the #1849 destruction path. Report and stop; the boot probe and the // recovery marker cover the swap-window states. - console.error(`opencodex: transactional update failed unexpectedly (${error?.message ?? error}). ` + - `The live install was not knowingly modified; run 'ocx update' again or reinstall with ` + - `npm install -g --allow-scripts=bun ${PKG}@${tag}.`); + const manual = manager === "pnpm" + ? `pnpm add -g --allow-build=bun ${PKG}@${tag}` + : `npm install -g --allow-scripts=bun ${PKG}@${tag}`; + // An unexpected exception leaves the active package path unproven for either manager. + // Do not run service/tray/proxy recovery through a possibly half-swapped tree. + postUpdateLauncherUsable = false; + console.error(`opencodex: ${manager} update failed unexpectedly (${error?.message ?? error}). ` + + `The live install was not knowingly modified; run 'ocx update' again or reinstall with ${manual}.`); res = { status: 1 }; } if (res.status === 0) { console.log(`\nUpdated${latest ? ` to v${latest}` : ""}.`); - repairCodexShimIfNeeded(); + repairCodexShimIfNeeded(postUpdateLauncher); if (trayBeforeUpdate.refreshAfterReplacement) { - const tray = spawnSync(process.execPath, [launcher, ...trayBeforeUpdate.installArgs], { + const tray = spawnSync(process.execPath, [postUpdateLauncher, ...trayBeforeUpdate.installArgs], { stdio: "inherit", windowsHide: true, }); if (tray.status !== 0) { console.warn("opencodex: Windows tray refresh failed. Run: ocx tray install"); - if (trayBeforeUpdate.restoreOnFailure) runTrayLifecycle(launcher, "start"); + if (trayBeforeUpdate.restoreOnFailure && postUpdateLauncherUsable) runTrayLifecycle(postUpdateLauncher, "start"); } } // The stop above unloaded any managed service; refresh via the freshly-installed @@ -487,19 +631,22 @@ function runNpmSelfUpdate() { console.log("Refreshing the background service with the updated files..."); refreshBackgroundServiceOrStartDirect(); } else { - console.log("Restart the proxy: ocx start"); + console.log(`Restart the proxy: ${launcherStartHint(postUpdateLauncher, bakePort)}`); } process.exit(0); } - if (trayBeforeUpdate.restoreOnFailure) runTrayLifecycle(launcher, "start"); + if (trayBeforeUpdate.restoreOnFailure && postUpdateLauncherUsable) runTrayLifecycle(postUpdateLauncher, "start"); recoverStoppedRuntimeAfterFailure(); - console.error(`\nUpdate failed (npm exit ${res.status ?? "?"}). Try manually: npm install -g --allow-scripts=bun ${PKG}@${tag}`); + const manual = manager === "pnpm" + ? `pnpm add -g --allow-build=bun ${PKG}@${tag}` + : `npm install -g --allow-scripts=bun ${PKG}@${tag}`; + console.error(`\nUpdate failed (${manager} exit ${res.status ?? "?"}). Try manually: ${manual}`); process.exit(1); } function bunBinDir() { // Resolve the `bun` dependency's directory without hardcoding the platform - // package — npm's os/cpu/libc resolution already picked the right @oven/bun-*. + // package — the package manager's os/cpu/libc resolution already picked the right @oven/bun-*. return dirname(require.resolve("bun/package.json")); } @@ -511,7 +658,7 @@ const BUN_RUNTIME_SOURCE_ENV = "OCX_BUN_RUNTIME_SOURCE"; const BUN_RUNTIME_PATH_ENV = "OCX_BUN_RUNTIME_PATH"; function findBunBinary(bunDir) { - // The npm `bun` package ships the binary as bin/bun.exe on every platform; + // The bundled `bun` package ships the binary as bin/bun.exe on every platform; // probe bin/bun too for forward compatibility. for (const name of ["bun.exe", "bun"]) { const p = join(bunDir, "bin", name); @@ -521,12 +668,15 @@ function findBunBinary(bunDir) { } function fail(msg) { + const reinstall = installMethod === "pnpm" + ? "pnpm add -g --allow-build=bun @bitkyc08/opencodex" + : "npm install -g --allow-scripts=bun @bitkyc08/opencodex"; console.error( `opencodex: ${msg}\n` + "The bundled Bun runtime could not be prepared. This usually means the\n" + - "install skipped lifecycle scripts (e.g. npm blocked bun's postinstall\n" + - "under allowScripts) or optional dependencies. Reinstall with:\n" + - " npm install -g --allow-scripts=bun @bitkyc08/opencodex\n" + + "install skipped lifecycle scripts (for example npm blocked bun's postinstall\n" + + "or pnpm did not approve bun's build) or optional dependencies. Reinstall with:\n" + + ` ${reinstall}\n` + "(use sudo if the original install used sudo; without --ignore-scripts\n" + "and without --omit=optional / optional=false)" ); @@ -534,7 +684,7 @@ function fail(msg) { } function resolveBun({ allowInstall = true } = {}) { - // Keep direct npm-launcher starts aligned with durable service/shim installs: + // Keep direct package-launcher starts aligned with durable service/shim installs: // a valid explicit runtime must win even when the bundled dependency exists. const override = process.env[BUN_OVERRIDE_ENV]?.trim(); if (override) { @@ -566,7 +716,7 @@ function resolveBun({ allowInstall = true } = {}) { return { path: bin, source: "bundled" }; } -// `ocx update --help` prints usage and exits WITHOUT side effects. The npm launcher +// `ocx update --help` prints usage and exits WITHOUT side effects. The Node launcher // intercepts `update` before the Bun CLI starts, so the help short-circuit must live // here too — otherwise --help runs the real self-update, stops the proxy, and drops // in-flight routed streams (issue #168). @@ -584,13 +734,14 @@ if (codexCliUpdateInspection && typeof process.versions.bun === "string") { } if (process.argv[2] === "update" && isNodeModulesInstall() && !isBunGlobalInstall()) { - runNpmSelfUpdate(); + if (installMethod === "npm") runNpmSelfUpdate(); + if (installMethod === "pnpm") runPnpmSelfUpdate(); } // #1849 boot probe: a prior update that lost power (or double-faulted) mid-swap leaves a // backup sibling and a broken live tree. Restore before anything tries to run from the // broken tree; reap stale backups once the live tree verifies healthy. -if (!codexCliUpdateInspection && isNodeModulesInstall() && !isBunGlobalInstall()) { +if (!codexCliUpdateInspection && installMethod === "npm" && isNodeModulesInstall() && !isBunGlobalInstall()) { try { const probe = bootRestoreProbe(resolve(here, "..")); if (probe.action === "restored") { diff --git a/devlog/_plan/260724_gpt_live_hotfix/010_session_header_fix.md b/devlog/_fin/260724_gpt_live_hotfix/010_session_header_fix.md similarity index 100% rename from devlog/_plan/260724_gpt_live_hotfix/010_session_header_fix.md rename to devlog/_fin/260724_gpt_live_hotfix/010_session_header_fix.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/000_plan.md b/devlog/_fin/260802_codex_set_prompt_composer/000_plan.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/000_plan.md rename to devlog/_fin/260802_codex_set_prompt_composer/000_plan.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/001_prompt_layer_inventory.md b/devlog/_fin/260802_codex_set_prompt_composer/001_prompt_layer_inventory.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/001_prompt_layer_inventory.md rename to devlog/_fin/260802_codex_set_prompt_composer/001_prompt_layer_inventory.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/002_injection_paths_and_presets.md b/devlog/_fin/260802_codex_set_prompt_composer/002_injection_paths_and_presets.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/002_injection_paths_and_presets.md rename to devlog/_fin/260802_codex_set_prompt_composer/002_injection_paths_and_presets.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/003_config_write_semantics.md b/devlog/_fin/260802_codex_set_prompt_composer/003_config_write_semantics.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/003_config_write_semantics.md rename to devlog/_fin/260802_codex_set_prompt_composer/003_config_write_semantics.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/004_surface_inventory.md b/devlog/_fin/260802_codex_set_prompt_composer/004_surface_inventory.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/004_surface_inventory.md rename to devlog/_fin/260802_codex_set_prompt_composer/004_surface_inventory.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/005_ux_design.md b/devlog/_fin/260802_codex_set_prompt_composer/005_ux_design.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/005_ux_design.md rename to devlog/_fin/260802_codex_set_prompt_composer/005_ux_design.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/010_wp1_prompt_layers_core.md b/devlog/_fin/260802_codex_set_prompt_composer/010_wp1_prompt_layers_core.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/010_wp1_prompt_layers_core.md rename to devlog/_fin/260802_codex_set_prompt_composer/010_wp1_prompt_layers_core.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/020_wp2_management_route.md b/devlog/_fin/260802_codex_set_prompt_composer/020_wp2_management_route.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/020_wp2_management_route.md rename to devlog/_fin/260802_codex_set_prompt_composer/020_wp2_management_route.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/021_wp2_amendments.md b/devlog/_fin/260802_codex_set_prompt_composer/021_wp2_amendments.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/021_wp2_amendments.md rename to devlog/_fin/260802_codex_set_prompt_composer/021_wp2_amendments.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/022_empirical_gate_findings.md b/devlog/_fin/260802_codex_set_prompt_composer/022_empirical_gate_findings.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/022_empirical_gate_findings.md rename to devlog/_fin/260802_codex_set_prompt_composer/022_empirical_gate_findings.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/023_stack_scope_presets.md b/devlog/_fin/260802_codex_set_prompt_composer/023_stack_scope_presets.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/023_stack_scope_presets.md rename to devlog/_fin/260802_codex_set_prompt_composer/023_stack_scope_presets.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/030_wp3_page_shell.md b/devlog/_fin/260802_codex_set_prompt_composer/030_wp3_page_shell.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/030_wp3_page_shell.md rename to devlog/_fin/260802_codex_set_prompt_composer/030_wp3_page_shell.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/040_wp4_layer_rows.md b/devlog/_fin/260802_codex_set_prompt_composer/040_wp4_layer_rows.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/040_wp4_layer_rows.md rename to devlog/_fin/260802_codex_set_prompt_composer/040_wp4_layer_rows.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/050_wp5_custom_layers.md b/devlog/_fin/260802_codex_set_prompt_composer/050_wp5_custom_layers.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/050_wp5_custom_layers.md rename to devlog/_fin/260802_codex_set_prompt_composer/050_wp5_custom_layers.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/060_wp6_presets_and_linter.md b/devlog/_fin/260802_codex_set_prompt_composer/060_wp6_presets_and_linter.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/060_wp6_presets_and_linter.md rename to devlog/_fin/260802_codex_set_prompt_composer/060_wp6_presets_and_linter.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/070_wp7_docs_and_verification.md b/devlog/_fin/260802_codex_set_prompt_composer/070_wp7_docs_and_verification.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/070_wp7_docs_and_verification.md rename to devlog/_fin/260802_codex_set_prompt_composer/070_wp7_docs_and_verification.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/080_wp8_stack_publication.md b/devlog/_fin/260802_codex_set_prompt_composer/080_wp8_stack_publication.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/080_wp8_stack_publication.md rename to devlog/_fin/260802_codex_set_prompt_composer/080_wp8_stack_publication.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/090_theme_deferred.md b/devlog/_fin/260802_codex_set_prompt_composer/090_theme_deferred.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/090_theme_deferred.md rename to devlog/_fin/260802_codex_set_prompt_composer/090_theme_deferred.md diff --git a/devlog/_plan/260802_codex_set_prompt_composer/091_stack_gate_findings.md b/devlog/_fin/260802_codex_set_prompt_composer/091_stack_gate_findings.md similarity index 100% rename from devlog/_plan/260802_codex_set_prompt_composer/091_stack_gate_findings.md rename to devlog/_fin/260802_codex_set_prompt_composer/091_stack_gate_findings.md diff --git a/devlog/_plan/260805_ssh_app_server_catalog_race/000_plan.md b/devlog/_fin/260805_ssh_app_server_catalog_race/000_plan.md similarity index 100% rename from devlog/_plan/260805_ssh_app_server_catalog_race/000_plan.md rename to devlog/_fin/260805_ssh_app_server_catalog_race/000_plan.md diff --git a/devlog/_plan/260805_ssh_app_server_catalog_race/001_two_host_comparison.md b/devlog/_fin/260805_ssh_app_server_catalog_race/001_two_host_comparison.md similarity index 100% rename from devlog/_plan/260805_ssh_app_server_catalog_race/001_two_host_comparison.md rename to devlog/_fin/260805_ssh_app_server_catalog_race/001_two_host_comparison.md diff --git a/devlog/_plan/260805_ssh_app_server_catalog_race/002_startup_vs_sync_asymmetry.md b/devlog/_fin/260805_ssh_app_server_catalog_race/002_startup_vs_sync_asymmetry.md similarity index 100% rename from devlog/_plan/260805_ssh_app_server_catalog_race/002_startup_vs_sync_asymmetry.md rename to devlog/_fin/260805_ssh_app_server_catalog_race/002_startup_vs_sync_asymmetry.md diff --git a/devlog/_plan/260813_apply_patch_tool_visibility/000_plan.md b/devlog/_fin/260813_apply_patch_tool_visibility/000_plan.md similarity index 100% rename from devlog/_plan/260813_apply_patch_tool_visibility/000_plan.md rename to devlog/_fin/260813_apply_patch_tool_visibility/000_plan.md diff --git a/devlog/_plan/260813_apply_patch_tool_visibility/001_codex_opencodex_survey.md b/devlog/_fin/260813_apply_patch_tool_visibility/001_codex_opencodex_survey.md similarity index 100% rename from devlog/_plan/260813_apply_patch_tool_visibility/001_codex_opencodex_survey.md rename to devlog/_fin/260813_apply_patch_tool_visibility/001_codex_opencodex_survey.md diff --git a/devlog/_plan/260813_apply_patch_tool_visibility/010_decision_and_noop_or_fix.md b/devlog/_fin/260813_apply_patch_tool_visibility/010_decision_and_noop_or_fix.md similarity index 100% rename from devlog/_plan/260813_apply_patch_tool_visibility/010_decision_and_noop_or_fix.md rename to devlog/_fin/260813_apply_patch_tool_visibility/010_decision_and_noop_or_fix.md diff --git a/devlog/_plan/260813_apply_patch_tool_visibility/011_decision_record.md b/devlog/_fin/260813_apply_patch_tool_visibility/011_decision_record.md similarity index 100% rename from devlog/_plan/260813_apply_patch_tool_visibility/011_decision_record.md rename to devlog/_fin/260813_apply_patch_tool_visibility/011_decision_record.md diff --git a/devlog/_plan/260813_apply_patch_tool_visibility/020_push_and_close.md b/devlog/_fin/260813_apply_patch_tool_visibility/020_push_and_close.md similarity index 100% rename from devlog/_plan/260813_apply_patch_tool_visibility/020_push_and_close.md rename to devlog/_fin/260813_apply_patch_tool_visibility/020_push_and_close.md diff --git a/devlog/_plan/260813_bun_canary_dogfood/000_plan.md b/devlog/_fin/260813_bun_canary_dogfood/000_plan.md similarity index 100% rename from devlog/_plan/260813_bun_canary_dogfood/000_plan.md rename to devlog/_fin/260813_bun_canary_dogfood/000_plan.md diff --git a/devlog/_plan/260813_bun_canary_dogfood/010_git_cleanup.md b/devlog/_fin/260813_bun_canary_dogfood/010_git_cleanup.md similarity index 100% rename from devlog/_plan/260813_bun_canary_dogfood/010_git_cleanup.md rename to devlog/_fin/260813_bun_canary_dogfood/010_git_cleanup.md diff --git a/devlog/_plan/260813_bun_canary_dogfood/020_bun_canary.md b/devlog/_fin/260813_bun_canary_dogfood/020_bun_canary.md similarity index 100% rename from devlog/_plan/260813_bun_canary_dogfood/020_bun_canary.md rename to devlog/_fin/260813_bun_canary_dogfood/020_bun_canary.md diff --git a/devlog/_plan/260813_bun_canary_dogfood/030_memory_patches.md b/devlog/_fin/260813_bun_canary_dogfood/030_memory_patches.md similarity index 100% rename from devlog/_plan/260813_bun_canary_dogfood/030_memory_patches.md rename to devlog/_fin/260813_bun_canary_dogfood/030_memory_patches.md diff --git a/devlog/_plan/260813_bun_canary_dogfood/040_verification_deploy.md b/devlog/_fin/260813_bun_canary_dogfood/040_verification_deploy.md similarity index 100% rename from devlog/_plan/260813_bun_canary_dogfood/040_verification_deploy.md rename to devlog/_fin/260813_bun_canary_dogfood/040_verification_deploy.md diff --git a/devlog/_fin/260813_bun_canary_dogfood/090_terminal.md b/devlog/_fin/260813_bun_canary_dogfood/090_terminal.md new file mode 100644 index 0000000000..2951fdea1e --- /dev/null +++ b/devlog/_fin/260813_bun_canary_dogfood/090_terminal.md @@ -0,0 +1,3 @@ +# terminal outcome — abandoned + +ABANDONED: superseded. The canary channel was retired (`27764f342`); the runtime moved to stable Bun 1.4.x. No dogfood work landed. diff --git a/devlog/_plan/260813_openai_chat_baseurl_normalize/000_plan.md b/devlog/_fin/260813_openai_chat_baseurl_normalize/000_plan.md similarity index 100% rename from devlog/_plan/260813_openai_chat_baseurl_normalize/000_plan.md rename to devlog/_fin/260813_openai_chat_baseurl_normalize/000_plan.md diff --git a/devlog/_plan/260813_openai_chat_baseurl_normalize/010_normalize_chat_baseurl.md b/devlog/_fin/260813_openai_chat_baseurl_normalize/010_normalize_chat_baseurl.md similarity index 100% rename from devlog/_plan/260813_openai_chat_baseurl_normalize/010_normalize_chat_baseurl.md rename to devlog/_fin/260813_openai_chat_baseurl_normalize/010_normalize_chat_baseurl.md diff --git a/devlog/_plan/260814_bug_resolution_campaign/000_plan.md b/devlog/_fin/260814_bug_resolution_campaign/000_plan.md similarity index 100% rename from devlog/_plan/260814_bug_resolution_campaign/000_plan.md rename to devlog/_fin/260814_bug_resolution_campaign/000_plan.md diff --git a/devlog/_plan/260814_bug_resolution_campaign/001_audit_inventory.md b/devlog/_fin/260814_bug_resolution_campaign/001_audit_inventory.md similarity index 100% rename from devlog/_plan/260814_bug_resolution_campaign/001_audit_inventory.md rename to devlog/_fin/260814_bug_resolution_campaign/001_audit_inventory.md diff --git a/devlog/_plan/260814_bug_resolution_campaign/002_merge_train_analysis.md b/devlog/_fin/260814_bug_resolution_campaign/002_merge_train_analysis.md similarity index 100% rename from devlog/_plan/260814_bug_resolution_campaign/002_merge_train_analysis.md rename to devlog/_fin/260814_bug_resolution_campaign/002_merge_train_analysis.md diff --git a/devlog/_plan/260814_bug_resolution_campaign/003_pr_split_decisions.md b/devlog/_fin/260814_bug_resolution_campaign/003_pr_split_decisions.md similarity index 100% rename from devlog/_plan/260814_bug_resolution_campaign/003_pr_split_decisions.md rename to devlog/_fin/260814_bug_resolution_campaign/003_pr_split_decisions.md diff --git a/devlog/_plan/260814_bug_resolution_campaign/010_wave1_small_p0_invariants.md b/devlog/_fin/260814_bug_resolution_campaign/010_wave1_small_p0_invariants.md similarity index 100% rename from devlog/_plan/260814_bug_resolution_campaign/010_wave1_small_p0_invariants.md rename to devlog/_fin/260814_bug_resolution_campaign/010_wave1_small_p0_invariants.md diff --git a/devlog/_plan/260814_bug_resolution_campaign/020_wave2_windows_service.md b/devlog/_fin/260814_bug_resolution_campaign/020_wave2_windows_service.md similarity index 100% rename from devlog/_plan/260814_bug_resolution_campaign/020_wave2_windows_service.md rename to devlog/_fin/260814_bug_resolution_campaign/020_wave2_windows_service.md diff --git a/devlog/_plan/260814_bug_resolution_campaign/030_wave3_cursor.md b/devlog/_fin/260814_bug_resolution_campaign/030_wave3_cursor.md similarity index 100% rename from devlog/_plan/260814_bug_resolution_campaign/030_wave3_cursor.md rename to devlog/_fin/260814_bug_resolution_campaign/030_wave3_cursor.md diff --git a/devlog/_plan/260814_bug_resolution_campaign/040_wave4_oauth_config_security.md b/devlog/_fin/260814_bug_resolution_campaign/040_wave4_oauth_config_security.md similarity index 100% rename from devlog/_plan/260814_bug_resolution_campaign/040_wave4_oauth_config_security.md rename to devlog/_fin/260814_bug_resolution_campaign/040_wave4_oauth_config_security.md diff --git a/devlog/_plan/260814_bug_resolution_campaign/050_wave5_provider_capability.md b/devlog/_fin/260814_bug_resolution_campaign/050_wave5_provider_capability.md similarity index 100% rename from devlog/_plan/260814_bug_resolution_campaign/050_wave5_provider_capability.md rename to devlog/_fin/260814_bug_resolution_campaign/050_wave5_provider_capability.md diff --git a/devlog/_plan/260814_bug_resolution_campaign/060_wave6_ci_bun14.md b/devlog/_fin/260814_bug_resolution_campaign/060_wave6_ci_bun14.md similarity index 100% rename from devlog/_plan/260814_bug_resolution_campaign/060_wave6_ci_bun14.md rename to devlog/_fin/260814_bug_resolution_campaign/060_wave6_ci_bun14.md diff --git a/devlog/_fin/260814_bug_resolution_campaign/090_terminal.md b/devlog/_fin/260814_bug_resolution_campaign/090_terminal.md new file mode 100644 index 0000000000..de5ec0216c --- /dev/null +++ b/devlog/_fin/260814_bug_resolution_campaign/090_terminal.md @@ -0,0 +1,3 @@ +# terminal outcome — abandoned + +ABANDONED: superseded. The six-wave campaign was never executed; later 260819/260821 merge trains covered the backlog in a different shape. diff --git a/devlog/_plan/260814_usage_memory_roadmap/000_plan.md b/devlog/_fin/260814_usage_memory_roadmap/000_plan.md similarity index 100% rename from devlog/_plan/260814_usage_memory_roadmap/000_plan.md rename to devlog/_fin/260814_usage_memory_roadmap/000_plan.md diff --git a/devlog/_plan/260814_usage_memory_roadmap/010_m0_1_input_admission.md b/devlog/_fin/260814_usage_memory_roadmap/010_m0_1_input_admission.md similarity index 100% rename from devlog/_plan/260814_usage_memory_roadmap/010_m0_1_input_admission.md rename to devlog/_fin/260814_usage_memory_roadmap/010_m0_1_input_admission.md diff --git a/devlog/_plan/260814_usage_memory_roadmap/020_m0_2_continuation_dedup.md b/devlog/_fin/260814_usage_memory_roadmap/020_m0_2_continuation_dedup.md similarity index 100% rename from devlog/_plan/260814_usage_memory_roadmap/020_m0_2_continuation_dedup.md rename to devlog/_fin/260814_usage_memory_roadmap/020_m0_2_continuation_dedup.md diff --git a/devlog/_plan/260814_usage_memory_roadmap/030_m0_3_provider_delivery.md b/devlog/_fin/260814_usage_memory_roadmap/030_m0_3_provider_delivery.md similarity index 100% rename from devlog/_plan/260814_usage_memory_roadmap/030_m0_3_provider_delivery.md rename to devlog/_fin/260814_usage_memory_roadmap/030_m0_3_provider_delivery.md diff --git a/devlog/_plan/260814_usage_memory_roadmap/040_m0_4_memory_recovery.md b/devlog/_fin/260814_usage_memory_roadmap/040_m0_4_memory_recovery.md similarity index 100% rename from devlog/_plan/260814_usage_memory_roadmap/040_m0_4_memory_recovery.md rename to devlog/_fin/260814_usage_memory_roadmap/040_m0_4_memory_recovery.md diff --git a/devlog/_plan/260814_usage_memory_roadmap/050_m0_5_nonjson_depth_cap.md b/devlog/_fin/260814_usage_memory_roadmap/050_m0_5_nonjson_depth_cap.md similarity index 100% rename from devlog/_plan/260814_usage_memory_roadmap/050_m0_5_nonjson_depth_cap.md rename to devlog/_fin/260814_usage_memory_roadmap/050_m0_5_nonjson_depth_cap.md diff --git a/devlog/_plan/260814_usage_memory_roadmap/060_u1_segmented_writer.md b/devlog/_fin/260814_usage_memory_roadmap/060_u1_segmented_writer.md similarity index 100% rename from devlog/_plan/260814_usage_memory_roadmap/060_u1_segmented_writer.md rename to devlog/_fin/260814_usage_memory_roadmap/060_u1_segmented_writer.md diff --git a/devlog/_plan/260814_usage_memory_roadmap/070_u2_sqlite_projector.md b/devlog/_fin/260814_usage_memory_roadmap/070_u2_sqlite_projector.md similarity index 100% rename from devlog/_plan/260814_usage_memory_roadmap/070_u2_sqlite_projector.md rename to devlog/_fin/260814_usage_memory_roadmap/070_u2_sqlite_projector.md diff --git a/devlog/_plan/260814_usage_memory_roadmap/080_u3_projection_api.md b/devlog/_fin/260814_usage_memory_roadmap/080_u3_projection_api.md similarity index 100% rename from devlog/_plan/260814_usage_memory_roadmap/080_u3_projection_api.md rename to devlog/_fin/260814_usage_memory_roadmap/080_u3_projection_api.md diff --git a/devlog/_fin/260814_usage_memory_roadmap/090_terminal.md b/devlog/_fin/260814_usage_memory_roadmap/090_terminal.md new file mode 100644 index 0000000000..c87252e40a --- /dev/null +++ b/devlog/_fin/260814_usage_memory_roadmap/090_terminal.md @@ -0,0 +1,3 @@ +# terminal outcome — abandoned + +ABANDONED: superseded. The M0/U1-U3 stack PRs (#1693/#1696/#1698) closed unmerged; later usage work (#2396, #1864) took a different shape. diff --git a/devlog/_plan/260816_undeclared_tool_invocation_contract/000_research.md b/devlog/_fin/260816_undeclared_tool_invocation_contract/000_research.md similarity index 100% rename from devlog/_plan/260816_undeclared_tool_invocation_contract/000_research.md rename to devlog/_fin/260816_undeclared_tool_invocation_contract/000_research.md diff --git a/devlog/_plan/260818_260818-zcode-client/000_plan.md b/devlog/_fin/260818_260818-zcode-client/000_plan.md similarity index 100% rename from devlog/_plan/260818_260818-zcode-client/000_plan.md rename to devlog/_fin/260818_260818-zcode-client/000_plan.md diff --git a/devlog/_plan/260818_260818-zcode-client/010_phase1.md b/devlog/_fin/260818_260818-zcode-client/010_phase1.md similarity index 100% rename from devlog/_plan/260818_260818-zcode-client/010_phase1.md rename to devlog/_fin/260818_260818-zcode-client/010_phase1.md diff --git a/devlog/_plan/260818_260818-zcode-client/020_gui_zcode_tab.png b/devlog/_fin/260818_260818-zcode-client/020_gui_zcode_tab.png similarity index 100% rename from devlog/_plan/260818_260818-zcode-client/020_gui_zcode_tab.png rename to devlog/_fin/260818_260818-zcode-client/020_gui_zcode_tab.png diff --git a/devlog/_plan/260818_260818-zcode-client/021_zcode_e2e_live.png b/devlog/_fin/260818_260818-zcode-client/021_zcode_e2e_live.png similarity index 100% rename from devlog/_plan/260818_260818-zcode-client/021_zcode_e2e_live.png rename to devlog/_fin/260818_260818-zcode-client/021_zcode_e2e_live.png diff --git a/devlog/_plan/260818_fastwire_b2_openrouter/evidence/010_logs_openrouter_priority_lower_bound.png b/devlog/_fin/260818_fastwire_b2_openrouter/evidence/010_logs_openrouter_priority_lower_bound.png similarity index 100% rename from devlog/_plan/260818_fastwire_b2_openrouter/evidence/010_logs_openrouter_priority_lower_bound.png rename to devlog/_fin/260818_fastwire_b2_openrouter/evidence/010_logs_openrouter_priority_lower_bound.png diff --git a/devlog/_plan/260818_fastwire_b2_openrouter/evidence/README.md b/devlog/_fin/260818_fastwire_b2_openrouter/evidence/README.md similarity index 100% rename from devlog/_plan/260818_fastwire_b2_openrouter/evidence/README.md rename to devlog/_fin/260818_fastwire_b2_openrouter/evidence/README.md diff --git a/devlog/_plan/260818_fastwire_b2_xai/evidence/010_logs_priority_lower_bound.png b/devlog/_fin/260818_fastwire_b2_xai/evidence/010_logs_priority_lower_bound.png similarity index 100% rename from devlog/_plan/260818_fastwire_b2_xai/evidence/010_logs_priority_lower_bound.png rename to devlog/_fin/260818_fastwire_b2_xai/evidence/010_logs_priority_lower_bound.png diff --git a/devlog/_plan/260818_fastwire_b2_xai/evidence/README.md b/devlog/_fin/260818_fastwire_b2_xai/evidence/README.md similarity index 100% rename from devlog/_plan/260818_fastwire_b2_xai/evidence/README.md rename to devlog/_fin/260818_fastwire_b2_xai/evidence/README.md diff --git a/devlog/_plan/260819_response_state_temp_reclaim/000_plan.md b/devlog/_fin/260819_response_state_temp_reclaim/000_plan.md similarity index 100% rename from devlog/_plan/260819_response_state_temp_reclaim/000_plan.md rename to devlog/_fin/260819_response_state_temp_reclaim/000_plan.md diff --git a/devlog/_plan/260819_response_state_temp_reclaim/001_audit_round1.md b/devlog/_fin/260819_response_state_temp_reclaim/001_audit_round1.md similarity index 100% rename from devlog/_plan/260819_response_state_temp_reclaim/001_audit_round1.md rename to devlog/_fin/260819_response_state_temp_reclaim/001_audit_round1.md diff --git a/devlog/_plan/260819_response_state_temp_reclaim/002_audit_round1_late.md b/devlog/_fin/260819_response_state_temp_reclaim/002_audit_round1_late.md similarity index 100% rename from devlog/_plan/260819_response_state_temp_reclaim/002_audit_round1_late.md rename to devlog/_fin/260819_response_state_temp_reclaim/002_audit_round1_late.md diff --git a/devlog/_plan/260819_response_state_temp_reclaim/010_phase1_periodic_sweeper.md b/devlog/_fin/260819_response_state_temp_reclaim/010_phase1_periodic_sweeper.md similarity index 100% rename from devlog/_plan/260819_response_state_temp_reclaim/010_phase1_periodic_sweeper.md rename to devlog/_fin/260819_response_state_temp_reclaim/010_phase1_periodic_sweeper.md diff --git a/devlog/_plan/260819_response_state_temp_reclaim/011_audit_round2.md b/devlog/_fin/260819_response_state_temp_reclaim/011_audit_round2.md similarity index 100% rename from devlog/_plan/260819_response_state_temp_reclaim/011_audit_round2.md rename to devlog/_fin/260819_response_state_temp_reclaim/011_audit_round2.md diff --git a/devlog/_plan/260819_response_state_temp_reclaim/012_phase1_verification.md b/devlog/_fin/260819_response_state_temp_reclaim/012_phase1_verification.md similarity index 100% rename from devlog/_plan/260819_response_state_temp_reclaim/012_phase1_verification.md rename to devlog/_fin/260819_response_state_temp_reclaim/012_phase1_verification.md diff --git a/devlog/_plan/260819_response_state_temp_reclaim/020_phase2_doctor_reclaim.md b/devlog/_fin/260819_response_state_temp_reclaim/020_phase2_doctor_reclaim.md similarity index 100% rename from devlog/_plan/260819_response_state_temp_reclaim/020_phase2_doctor_reclaim.md rename to devlog/_fin/260819_response_state_temp_reclaim/020_phase2_doctor_reclaim.md diff --git a/devlog/_plan/260819_response_state_temp_reclaim/021_audit_round3.md b/devlog/_fin/260819_response_state_temp_reclaim/021_audit_round3.md similarity index 100% rename from devlog/_plan/260819_response_state_temp_reclaim/021_audit_round3.md rename to devlog/_fin/260819_response_state_temp_reclaim/021_audit_round3.md diff --git a/devlog/_plan/260819_response_state_temp_reclaim/022_phase2_verification.md b/devlog/_fin/260819_response_state_temp_reclaim/022_phase2_verification.md similarity index 100% rename from devlog/_plan/260819_response_state_temp_reclaim/022_phase2_verification.md rename to devlog/_fin/260819_response_state_temp_reclaim/022_phase2_verification.md diff --git a/devlog/_plan/260819_triage_execution/000_plan.md b/devlog/_fin/260819_triage_execution/000_plan.md similarity index 100% rename from devlog/_plan/260819_triage_execution/000_plan.md rename to devlog/_fin/260819_triage_execution/000_plan.md diff --git a/devlog/_plan/260819_triage_execution/010_env_key_contract.md b/devlog/_fin/260819_triage_execution/010_env_key_contract.md similarity index 100% rename from devlog/_plan/260819_triage_execution/010_env_key_contract.md rename to devlog/_fin/260819_triage_execution/010_env_key_contract.md diff --git a/devlog/_plan/260819_triage_execution/020_remote_reasoning_leak_rca.md b/devlog/_fin/260819_triage_execution/020_remote_reasoning_leak_rca.md similarity index 100% rename from devlog/_plan/260819_triage_execution/020_remote_reasoning_leak_rca.md rename to devlog/_fin/260819_triage_execution/020_remote_reasoning_leak_rca.md diff --git a/devlog/_plan/260819_triage_execution/030_outcome.md b/devlog/_fin/260819_triage_execution/030_outcome.md similarity index 100% rename from devlog/_plan/260819_triage_execution/030_outcome.md rename to devlog/_fin/260819_triage_execution/030_outcome.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/000_research_inventory.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/000_research_inventory.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/000_research_inventory.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/000_research_inventory.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/010_layer1_bearer_admission_2132.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/010_layer1_bearer_admission_2132.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/010_layer1_bearer_admission_2132.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/010_layer1_bearer_admission_2132.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/020_layer2_responses_id_backfill_2131.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/020_layer2_responses_id_backfill_2131.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/020_layer2_responses_id_backfill_2131.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/020_layer2_responses_id_backfill_2131.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/030_sibling_prompt_cache_retention.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/030_sibling_prompt_cache_retention.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/030_sibling_prompt_cache_retention.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/030_sibling_prompt_cache_retention.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/040_sibling_routing_capability.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/040_sibling_routing_capability.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/040_sibling_routing_capability.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/040_sibling_routing_capability.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/050_sibling_k12_short_window.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/050_sibling_k12_short_window.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/050_sibling_k12_short_window.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/050_sibling_k12_short_window.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/060_supersede_and_close_operations.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/060_supersede_and_close_operations.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/060_supersede_and_close_operations.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/060_supersede_and_close_operations.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/070_execution_log.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/070_execution_log.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/070_execution_log.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/070_execution_log.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/080_residual_dispositions.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/080_residual_dispositions.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/080_residual_dispositions.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/080_residual_dispositions.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/090_merge_log.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/090_merge_log.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/090_merge_log.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/090_merge_log.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/100_release_audit.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/100_release_audit.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/100_release_audit.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/100_release_audit.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/100_release_safety_audit.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/100_release_safety_audit.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/100_release_safety_audit.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/100_release_safety_audit.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/110_three_bug_issues.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/110_three_bug_issues.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/110_three_bug_issues.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/110_three_bug_issues.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/120_release_2280.md b/devlog/_fin/260820_bug_pr_backlog_consolidation/120_release_2280.md similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/120_release_2280.md rename to devlog/_fin/260820_bug_pr_backlog_consolidation/120_release_2280.md diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/assets/2157-logs-intercepted-badge.png b/devlog/_fin/260820_bug_pr_backlog_consolidation/assets/2157-logs-intercepted-badge.png similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/assets/2157-logs-intercepted-badge.png rename to devlog/_fin/260820_bug_pr_backlog_consolidation/assets/2157-logs-intercepted-badge.png diff --git a/devlog/_plan/260820_bug_pr_backlog_consolidation/assets/2157-logs-intercepted-filtered.png b/devlog/_fin/260820_bug_pr_backlog_consolidation/assets/2157-logs-intercepted-filtered.png similarity index 100% rename from devlog/_plan/260820_bug_pr_backlog_consolidation/assets/2157-logs-intercepted-filtered.png rename to devlog/_fin/260820_bug_pr_backlog_consolidation/assets/2157-logs-intercepted-filtered.png diff --git a/devlog/_plan/260820_sidecar_selection_unification/000_unit_overview.md b/devlog/_fin/260820_sidecar_selection_unification/000_unit_overview.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/000_unit_overview.md rename to devlog/_fin/260820_sidecar_selection_unification/000_unit_overview.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/000_wp0_branch_worktree_cleanup.md b/devlog/_fin/260820_sidecar_selection_unification/000_wp0_branch_worktree_cleanup.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/000_wp0_branch_worktree_cleanup.md rename to devlog/_fin/260820_sidecar_selection_unification/000_wp0_branch_worktree_cleanup.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/001_research_current_state.md b/devlog/_fin/260820_sidecar_selection_unification/001_research_current_state.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/001_research_current_state.md rename to devlog/_fin/260820_sidecar_selection_unification/001_research_current_state.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/002_protocol_research.md b/devlog/_fin/260820_sidecar_selection_unification/002_protocol_research.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/002_protocol_research.md rename to devlog/_fin/260820_sidecar_selection_unification/002_protocol_research.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/003_grok_tools_research.md b/devlog/_fin/260820_sidecar_selection_unification/003_grok_tools_research.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/003_grok_tools_research.md rename to devlog/_fin/260820_sidecar_selection_unification/003_grok_tools_research.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/010_layer1_sidecar_auth.md b/devlog/_fin/260820_sidecar_selection_unification/010_layer1_sidecar_auth.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/010_layer1_sidecar_auth.md rename to devlog/_fin/260820_sidecar_selection_unification/010_layer1_sidecar_auth.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/020_layer2_picker_candidates.md b/devlog/_fin/260820_sidecar_selection_unification/020_layer2_picker_candidates.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/020_layer2_picker_candidates.md rename to devlog/_fin/260820_sidecar_selection_unification/020_layer2_picker_candidates.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/030_layer3_websearch_slots.md b/devlog/_fin/260820_sidecar_selection_unification/030_layer3_websearch_slots.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/030_layer3_websearch_slots.md rename to devlog/_fin/260820_sidecar_selection_unification/030_layer3_websearch_slots.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/031_future_backend_research.md b/devlog/_fin/260820_sidecar_selection_unification/031_future_backend_research.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/031_future_backend_research.md rename to devlog/_fin/260820_sidecar_selection_unification/031_future_backend_research.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/040_layer4_write_gates_gui.md b/devlog/_fin/260820_sidecar_selection_unification/040_layer4_write_gates_gui.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/040_layer4_write_gates_gui.md rename to devlog/_fin/260820_sidecar_selection_unification/040_layer4_write_gates_gui.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/050_layer5_cli_and_final.md b/devlog/_fin/260820_sidecar_selection_unification/050_layer5_cli_and_final.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/050_layer5_cli_and_final.md rename to devlog/_fin/260820_sidecar_selection_unification/050_layer5_cli_and_final.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/060_layer6_backend_union.md b/devlog/_fin/260820_sidecar_selection_unification/060_layer6_backend_union.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/060_layer6_backend_union.md rename to devlog/_fin/260820_sidecar_selection_unification/060_layer6_backend_union.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/070_layer7_xai_executor.md b/devlog/_fin/260820_sidecar_selection_unification/070_layer7_xai_executor.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/070_layer7_xai_executor.md rename to devlog/_fin/260820_sidecar_selection_unification/070_layer7_xai_executor.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/080_layer8_gemini_executor.md b/devlog/_fin/260820_sidecar_selection_unification/080_layer8_gemini_executor.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/080_layer8_gemini_executor.md rename to devlog/_fin/260820_sidecar_selection_unification/080_layer8_gemini_executor.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/090_layer9_exa_executor.md b/devlog/_fin/260820_sidecar_selection_unification/090_layer9_exa_executor.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/090_layer9_exa_executor.md rename to devlog/_fin/260820_sidecar_selection_unification/090_layer9_exa_executor.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/100_chat_default_regression.md b/devlog/_fin/260820_sidecar_selection_unification/100_chat_default_regression.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/100_chat_default_regression.md rename to devlog/_fin/260820_sidecar_selection_unification/100_chat_default_regression.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/110_global_merge_order.md b/devlog/_fin/260820_sidecar_selection_unification/110_global_merge_order.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/110_global_merge_order.md rename to devlog/_fin/260820_sidecar_selection_unification/110_global_merge_order.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/120_sidecar_chain_merge.md b/devlog/_fin/260820_sidecar_selection_unification/120_sidecar_chain_merge.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/120_sidecar_chain_merge.md rename to devlog/_fin/260820_sidecar_selection_unification/120_sidecar_chain_merge.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/130_xai_responses_optin_switch.md b/devlog/_fin/260820_sidecar_selection_unification/130_xai_responses_optin_switch.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/130_xai_responses_optin_switch.md rename to devlog/_fin/260820_sidecar_selection_unification/130_xai_responses_optin_switch.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/140_release_prep.md b/devlog/_fin/260820_sidecar_selection_unification/140_release_prep.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/140_release_prep.md rename to devlog/_fin/260820_sidecar_selection_unification/140_release_prep.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/141_release_prep_artifact.md b/devlog/_fin/260820_sidecar_selection_unification/141_release_prep_artifact.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/141_release_prep_artifact.md rename to devlog/_fin/260820_sidecar_selection_unification/141_release_prep_artifact.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/150_lidge_final_gate.md b/devlog/_fin/260820_sidecar_selection_unification/150_lidge_final_gate.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/150_lidge_final_gate.md rename to devlog/_fin/260820_sidecar_selection_unification/150_lidge_final_gate.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/160_vision_external_research.md b/devlog/_fin/260820_sidecar_selection_unification/160_vision_external_research.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/160_vision_external_research.md rename to devlog/_fin/260820_sidecar_selection_unification/160_vision_external_research.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/170_vision_backend_union.md b/devlog/_fin/260820_sidecar_selection_unification/170_vision_backend_union.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/170_vision_backend_union.md rename to devlog/_fin/260820_sidecar_selection_unification/170_vision_backend_union.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/180_vision_describe_executors.md b/devlog/_fin/260820_sidecar_selection_unification/180_vision_describe_executors.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/180_vision_describe_executors.md rename to devlog/_fin/260820_sidecar_selection_unification/180_vision_describe_executors.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/190_vision_surfaces_and_delivery.md b/devlog/_fin/260820_sidecar_selection_unification/190_vision_surfaces_and_delivery.md similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/190_vision_surfaces_and_delivery.md rename to devlog/_fin/260820_sidecar_selection_unification/190_vision_surfaces_and_delivery.md diff --git a/devlog/_plan/260820_sidecar_selection_unification/assets/l4-websearch-picker-filtered.png b/devlog/_fin/260820_sidecar_selection_unification/assets/l4-websearch-picker-filtered.png similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/assets/l4-websearch-picker-filtered.png rename to devlog/_fin/260820_sidecar_selection_unification/assets/l4-websearch-picker-filtered.png diff --git a/devlog/_plan/260820_sidecar_selection_unification/assets/vision_routed_dropdown.png b/devlog/_fin/260820_sidecar_selection_unification/assets/vision_routed_dropdown.png similarity index 100% rename from devlog/_plan/260820_sidecar_selection_unification/assets/vision_routed_dropdown.png rename to devlog/_fin/260820_sidecar_selection_unification/assets/vision_routed_dropdown.png diff --git a/devlog/_plan/260821_260821-model-catalog-refresh/000_plan.md b/devlog/_fin/260821_260821-model-catalog-refresh/000_plan.md similarity index 100% rename from devlog/_plan/260821_260821-model-catalog-refresh/000_plan.md rename to devlog/_fin/260821_260821-model-catalog-refresh/000_plan.md diff --git a/devlog/_plan/260821_260821-model-catalog-refresh/010_phase1.md b/devlog/_fin/260821_260821-model-catalog-refresh/010_phase1.md similarity index 100% rename from devlog/_plan/260821_260821-model-catalog-refresh/010_phase1.md rename to devlog/_fin/260821_260821-model-catalog-refresh/010_phase1.md diff --git a/devlog/_plan/260821_260821-windows-picker-full-restart/000_plan.md b/devlog/_fin/260821_260821-windows-picker-full-restart/000_plan.md similarity index 100% rename from devlog/_plan/260821_260821-windows-picker-full-restart/000_plan.md rename to devlog/_fin/260821_260821-windows-picker-full-restart/000_plan.md diff --git a/devlog/_plan/260821_260821-windows-picker-full-restart/010_phase1.md b/devlog/_fin/260821_260821-windows-picker-full-restart/010_phase1.md similarity index 100% rename from devlog/_plan/260821_260821-windows-picker-full-restart/010_phase1.md rename to devlog/_fin/260821_260821-windows-picker-full-restart/010_phase1.md diff --git a/devlog/_plan/260822_260822-bun14-followup-memory/000_plan.md b/devlog/_fin/260822_260822-bun14-followup-memory/000_plan.md similarity index 100% rename from devlog/_plan/260822_260822-bun14-followup-memory/000_plan.md rename to devlog/_fin/260822_260822-bun14-followup-memory/000_plan.md diff --git a/devlog/_plan/260822_260822-bun14-followup-memory/000_research.md b/devlog/_fin/260822_260822-bun14-followup-memory/000_research.md similarity index 100% rename from devlog/_plan/260822_260822-bun14-followup-memory/000_research.md rename to devlog/_fin/260822_260822-bun14-followup-memory/000_research.md diff --git a/devlog/_plan/260822_260822-bun14-followup-memory/010_memory_diagnostics.md b/devlog/_fin/260822_260822-bun14-followup-memory/010_memory_diagnostics.md similarity index 100% rename from devlog/_plan/260822_260822-bun14-followup-memory/010_memory_diagnostics.md rename to devlog/_fin/260822_260822-bun14-followup-memory/010_memory_diagnostics.md diff --git a/devlog/_plan/260822_260822-bun14-followup-memory/020_watchdog_gc_relief.md b/devlog/_fin/260822_260822-bun14-followup-memory/020_watchdog_gc_relief.md similarity index 100% rename from devlog/_plan/260822_260822-bun14-followup-memory/020_watchdog_gc_relief.md rename to devlog/_fin/260822_260822-bun14-followup-memory/020_watchdog_gc_relief.md diff --git a/devlog/_plan/260822_260822-bun14-followup-memory/030_smol_workers.md b/devlog/_fin/260822_260822-bun14-followup-memory/030_smol_workers.md similarity index 100% rename from devlog/_plan/260822_260822-bun14-followup-memory/030_smol_workers.md rename to devlog/_fin/260822_260822-bun14-followup-memory/030_smol_workers.md diff --git a/devlog/_plan/260822_260822-bun14-followup-memory/040_macmini_measurement.md b/devlog/_fin/260822_260822-bun14-followup-memory/040_macmini_measurement.md similarity index 100% rename from devlog/_plan/260822_260822-bun14-followup-memory/040_macmini_measurement.md rename to devlog/_fin/260822_260822-bun14-followup-memory/040_macmini_measurement.md diff --git a/devlog/_fin/260822_260822-bun14-followup-memory/090_terminal.md b/devlog/_fin/260822_260822-bun14-followup-memory/090_terminal.md new file mode 100644 index 0000000000..4b1ee418c1 --- /dev/null +++ b/devlog/_fin/260822_260822-bun14-followup-memory/090_terminal.md @@ -0,0 +1,3 @@ +# terminal outcome — abandoned + +ABANDONED: superseded. Measurement protocol never executed; the runtime moved to stable Bun 1.4.x (`27764f342`) making the GC/smol comparison stale. diff --git a/devlog/_plan/260822_260822-gui-sidecar-layout-dvh/000_plan.md b/devlog/_fin/260822_260822-gui-sidecar-layout-dvh/000_plan.md similarity index 100% rename from devlog/_plan/260822_260822-gui-sidecar-layout-dvh/000_plan.md rename to devlog/_fin/260822_260822-gui-sidecar-layout-dvh/000_plan.md diff --git a/devlog/_plan/260822_260822-gui-sidecar-layout-dvh/010_phase1.md b/devlog/_fin/260822_260822-gui-sidecar-layout-dvh/010_phase1.md similarity index 100% rename from devlog/_plan/260822_260822-gui-sidecar-layout-dvh/010_phase1.md rename to devlog/_fin/260822_260822-gui-sidecar-layout-dvh/010_phase1.md diff --git a/devlog/_plan/260822_260822-gui-sidecar-layout-dvh/020_phase2.md b/devlog/_fin/260822_260822-gui-sidecar-layout-dvh/020_phase2.md similarity index 100% rename from devlog/_plan/260822_260822-gui-sidecar-layout-dvh/020_phase2.md rename to devlog/_fin/260822_260822-gui-sidecar-layout-dvh/020_phase2.md diff --git a/devlog/_plan/260822_260822-gui-sidecar-layout-dvh/030_phase3.md b/devlog/_fin/260822_260822-gui-sidecar-layout-dvh/030_phase3.md similarity index 100% rename from devlog/_plan/260822_260822-gui-sidecar-layout-dvh/030_phase3.md rename to devlog/_fin/260822_260822-gui-sidecar-layout-dvh/030_phase3.md diff --git a/devlog/_plan/260822_260822-gui-sidecar-layout-dvh/040_phase4.md b/devlog/_fin/260822_260822-gui-sidecar-layout-dvh/040_phase4.md similarity index 100% rename from devlog/_plan/260822_260822-gui-sidecar-layout-dvh/040_phase4.md rename to devlog/_fin/260822_260822-gui-sidecar-layout-dvh/040_phase4.md diff --git a/devlog/_plan/260822_260822-gui-sidecar-layout-dvh/050_execution.md b/devlog/_fin/260822_260822-gui-sidecar-layout-dvh/050_execution.md similarity index 100% rename from devlog/_plan/260822_260822-gui-sidecar-layout-dvh/050_execution.md rename to devlog/_fin/260822_260822-gui-sidecar-layout-dvh/050_execution.md diff --git a/devlog/_plan/260822_260822-gui-sidecar-layout-dvh/060_container_queries.md b/devlog/_fin/260822_260822-gui-sidecar-layout-dvh/060_container_queries.md similarity index 100% rename from devlog/_plan/260822_260822-gui-sidecar-layout-dvh/060_container_queries.md rename to devlog/_fin/260822_260822-gui-sidecar-layout-dvh/060_container_queries.md diff --git a/devlog/_plan/260822_260822-gui-sidecar-layout-dvh/evidence/after-aligned.jpg b/devlog/_fin/260822_260822-gui-sidecar-layout-dvh/evidence/after-aligned.jpg similarity index 100% rename from devlog/_plan/260822_260822-gui-sidecar-layout-dvh/evidence/after-aligned.jpg rename to devlog/_fin/260822_260822-gui-sidecar-layout-dvh/evidence/after-aligned.jpg diff --git a/devlog/_plan/260822_260822-gui-sidecar-layout-dvh/evidence/before-collapse.jpg b/devlog/_fin/260822_260822-gui-sidecar-layout-dvh/evidence/before-collapse.jpg similarity index 100% rename from devlog/_plan/260822_260822-gui-sidecar-layout-dvh/evidence/before-collapse.jpg rename to devlog/_fin/260822_260822-gui-sidecar-layout-dvh/evidence/before-collapse.jpg diff --git a/devlog/_plan/260822_backlog_disposition_program/000_plan.md b/devlog/_fin/260822_backlog_disposition_program/000_plan.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/000_plan.md rename to devlog/_fin/260822_backlog_disposition_program/000_plan.md diff --git a/devlog/_plan/260822_backlog_disposition_program/001_baseline_verifier_evidence.md b/devlog/_fin/260822_backlog_disposition_program/001_baseline_verifier_evidence.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/001_baseline_verifier_evidence.md rename to devlog/_fin/260822_backlog_disposition_program/001_baseline_verifier_evidence.md diff --git a/devlog/_plan/260822_backlog_disposition_program/002_audit_synthesis.md b/devlog/_fin/260822_backlog_disposition_program/002_audit_synthesis.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/002_audit_synthesis.md rename to devlog/_fin/260822_backlog_disposition_program/002_audit_synthesis.md diff --git a/devlog/_plan/260822_backlog_disposition_program/003_live_drift_and_2360.md b/devlog/_fin/260822_backlog_disposition_program/003_live_drift_and_2360.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/003_live_drift_and_2360.md rename to devlog/_fin/260822_backlog_disposition_program/003_live_drift_and_2360.md diff --git a/devlog/_plan/260822_backlog_disposition_program/010_wp1_green_ready_merges.md b/devlog/_fin/260822_backlog_disposition_program/010_wp1_green_ready_merges.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/010_wp1_green_ready_merges.md rename to devlog/_fin/260822_backlog_disposition_program/010_wp1_green_ready_merges.md diff --git a/devlog/_plan/260822_backlog_disposition_program/011_wp1_execution_record.md b/devlog/_fin/260822_backlog_disposition_program/011_wp1_execution_record.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/011_wp1_execution_record.md rename to devlog/_fin/260822_backlog_disposition_program/011_wp1_execution_record.md diff --git a/devlog/_plan/260822_backlog_disposition_program/020_wp2_changes_requested_rebuilds.md b/devlog/_fin/260822_backlog_disposition_program/020_wp2_changes_requested_rebuilds.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/020_wp2_changes_requested_rebuilds.md rename to devlog/_fin/260822_backlog_disposition_program/020_wp2_changes_requested_rebuilds.md diff --git a/devlog/_plan/260822_backlog_disposition_program/021_wp2_execution_record.md b/devlog/_fin/260822_backlog_disposition_program/021_wp2_execution_record.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/021_wp2_execution_record.md rename to devlog/_fin/260822_backlog_disposition_program/021_wp2_execution_record.md diff --git a/devlog/_plan/260822_backlog_disposition_program/030_wp3_issue_2316_wait_agent_timeout.md b/devlog/_fin/260822_backlog_disposition_program/030_wp3_issue_2316_wait_agent_timeout.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/030_wp3_issue_2316_wait_agent_timeout.md rename to devlog/_fin/260822_backlog_disposition_program/030_wp3_issue_2316_wait_agent_timeout.md diff --git a/devlog/_plan/260822_backlog_disposition_program/040_wp4_issue_2292_windows_picker.md b/devlog/_fin/260822_backlog_disposition_program/040_wp4_issue_2292_windows_picker.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/040_wp4_issue_2292_windows_picker.md rename to devlog/_fin/260822_backlog_disposition_program/040_wp4_issue_2292_windows_picker.md diff --git a/devlog/_plan/260822_backlog_disposition_program/041_wp4_execution_record.md b/devlog/_fin/260822_backlog_disposition_program/041_wp4_execution_record.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/041_wp4_execution_record.md rename to devlog/_fin/260822_backlog_disposition_program/041_wp4_execution_record.md diff --git a/devlog/_plan/260822_backlog_disposition_program/050_wp5_issue_2221_native_main_refresh.md b/devlog/_fin/260822_backlog_disposition_program/050_wp5_issue_2221_native_main_refresh.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/050_wp5_issue_2221_native_main_refresh.md rename to devlog/_fin/260822_backlog_disposition_program/050_wp5_issue_2221_native_main_refresh.md diff --git a/devlog/_plan/260822_backlog_disposition_program/060_wp6_issue_1049_presubstrate_home_adoption.md b/devlog/_fin/260822_backlog_disposition_program/060_wp6_issue_1049_presubstrate_home_adoption.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/060_wp6_issue_1049_presubstrate_home_adoption.md rename to devlog/_fin/260822_backlog_disposition_program/060_wp6_issue_1049_presubstrate_home_adoption.md diff --git a/devlog/_plan/260822_backlog_disposition_program/061_wp6_deferral_record.md b/devlog/_fin/260822_backlog_disposition_program/061_wp6_deferral_record.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/061_wp6_deferral_record.md rename to devlog/_fin/260822_backlog_disposition_program/061_wp6_deferral_record.md diff --git a/devlog/_plan/260822_backlog_disposition_program/070_wp7_bun14_memory_stack_retarget.md b/devlog/_fin/260822_backlog_disposition_program/070_wp7_bun14_memory_stack_retarget.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/070_wp7_bun14_memory_stack_retarget.md rename to devlog/_fin/260822_backlog_disposition_program/070_wp7_bun14_memory_stack_retarget.md diff --git a/devlog/_plan/260822_backlog_disposition_program/071_wp7_execution_record.md b/devlog/_fin/260822_backlog_disposition_program/071_wp7_execution_record.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/071_wp7_execution_record.md rename to devlog/_fin/260822_backlog_disposition_program/071_wp7_execution_record.md diff --git a/devlog/_plan/260822_backlog_disposition_program/080_wp8_conflicting_and_remaining.md b/devlog/_fin/260822_backlog_disposition_program/080_wp8_conflicting_and_remaining.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/080_wp8_conflicting_and_remaining.md rename to devlog/_fin/260822_backlog_disposition_program/080_wp8_conflicting_and_remaining.md diff --git a/devlog/_plan/260822_backlog_disposition_program/081_wp8_execution_and_reconciliation.md b/devlog/_fin/260822_backlog_disposition_program/081_wp8_execution_and_reconciliation.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/081_wp8_execution_and_reconciliation.md rename to devlog/_fin/260822_backlog_disposition_program/081_wp8_execution_and_reconciliation.md diff --git a/devlog/_plan/260822_backlog_disposition_program/090_wp9_new_pr_disposition.md b/devlog/_fin/260822_backlog_disposition_program/090_wp9_new_pr_disposition.md similarity index 100% rename from devlog/_plan/260822_backlog_disposition_program/090_wp9_new_pr_disposition.md rename to devlog/_fin/260822_backlog_disposition_program/090_wp9_new_pr_disposition.md diff --git a/devlog/_plan/260822_cli_usage_cost_query/000_unit_overview.md b/devlog/_fin/260822_cli_usage_cost_query/000_unit_overview.md similarity index 100% rename from devlog/_plan/260822_cli_usage_cost_query/000_unit_overview.md rename to devlog/_fin/260822_cli_usage_cost_query/000_unit_overview.md diff --git a/devlog/_plan/260822_cli_usage_cost_query/001_current_state_inventory.md b/devlog/_fin/260822_cli_usage_cost_query/001_current_state_inventory.md similarity index 100% rename from devlog/_plan/260822_cli_usage_cost_query/001_current_state_inventory.md rename to devlog/_fin/260822_cli_usage_cost_query/001_current_state_inventory.md diff --git a/devlog/_plan/260822_cli_usage_cost_query/010_data_layer.md b/devlog/_fin/260822_cli_usage_cost_query/010_data_layer.md similarity index 100% rename from devlog/_plan/260822_cli_usage_cost_query/010_data_layer.md rename to devlog/_fin/260822_cli_usage_cost_query/010_data_layer.md diff --git a/devlog/_plan/260822_cli_usage_cost_query/020_api_layer.md b/devlog/_fin/260822_cli_usage_cost_query/020_api_layer.md similarity index 100% rename from devlog/_plan/260822_cli_usage_cost_query/020_api_layer.md rename to devlog/_fin/260822_cli_usage_cost_query/020_api_layer.md diff --git a/devlog/_plan/260822_cli_usage_cost_query/021_audit_amendments.md b/devlog/_fin/260822_cli_usage_cost_query/021_audit_amendments.md similarity index 100% rename from devlog/_plan/260822_cli_usage_cost_query/021_audit_amendments.md rename to devlog/_fin/260822_cli_usage_cost_query/021_audit_amendments.md diff --git a/devlog/_plan/260822_cli_usage_cost_query/030_cli_layer.md b/devlog/_fin/260822_cli_usage_cost_query/030_cli_layer.md similarity index 100% rename from devlog/_plan/260822_cli_usage_cost_query/030_cli_layer.md rename to devlog/_fin/260822_cli_usage_cost_query/030_cli_layer.md diff --git a/devlog/_plan/260822_cli_usage_cost_query/031_live_evidence.md b/devlog/_fin/260822_cli_usage_cost_query/031_live_evidence.md similarity index 100% rename from devlog/_plan/260822_cli_usage_cost_query/031_live_evidence.md rename to devlog/_fin/260822_cli_usage_cost_query/031_live_evidence.md diff --git a/devlog/_plan/260822_cli_usage_cost_query/040_verification_and_pr.md b/devlog/_fin/260822_cli_usage_cost_query/040_verification_and_pr.md similarity index 100% rename from devlog/_plan/260822_cli_usage_cost_query/040_verification_and_pr.md rename to devlog/_fin/260822_cli_usage_cost_query/040_verification_and_pr.md diff --git a/devlog/_plan/260822_dev_release_readiness/000_plan.md b/devlog/_fin/260822_dev_release_readiness/000_plan.md similarity index 100% rename from devlog/_plan/260822_dev_release_readiness/000_plan.md rename to devlog/_fin/260822_dev_release_readiness/000_plan.md diff --git a/devlog/_plan/260822_dev_release_readiness/001_delta_inventory.md b/devlog/_fin/260822_dev_release_readiness/001_delta_inventory.md similarity index 100% rename from devlog/_plan/260822_dev_release_readiness/001_delta_inventory.md rename to devlog/_fin/260822_dev_release_readiness/001_delta_inventory.md diff --git a/devlog/_plan/260822_dev_release_readiness/002_risk_matrix.md b/devlog/_fin/260822_dev_release_readiness/002_risk_matrix.md similarity index 100% rename from devlog/_plan/260822_dev_release_readiness/002_risk_matrix.md rename to devlog/_fin/260822_dev_release_readiness/002_risk_matrix.md diff --git a/devlog/_plan/260822_dev_release_readiness/009_roadmap_lock.md b/devlog/_fin/260822_dev_release_readiness/009_roadmap_lock.md similarity index 100% rename from devlog/_plan/260822_dev_release_readiness/009_roadmap_lock.md rename to devlog/_fin/260822_dev_release_readiness/009_roadmap_lock.md diff --git a/devlog/_plan/260822_dev_release_readiness/010_wp4_findings.md b/devlog/_fin/260822_dev_release_readiness/010_wp4_findings.md similarity index 100% rename from devlog/_plan/260822_dev_release_readiness/010_wp4_findings.md rename to devlog/_fin/260822_dev_release_readiness/010_wp4_findings.md diff --git a/devlog/_plan/260822_dev_release_readiness/090_go_verdict.md b/devlog/_fin/260822_dev_release_readiness/090_go_verdict.md similarity index 100% rename from devlog/_plan/260822_dev_release_readiness/090_go_verdict.md rename to devlog/_fin/260822_dev_release_readiness/090_go_verdict.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/000_inventory_and_roadmap.md b/devlog/_fin/260823_owner_backlog_closeout/000_inventory_and_roadmap.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/000_inventory_and_roadmap.md rename to devlog/_fin/260823_owner_backlog_closeout/000_inventory_and_roadmap.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/001_reviewer_evidence.md b/devlog/_fin/260823_owner_backlog_closeout/001_reviewer_evidence.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/001_reviewer_evidence.md rename to devlog/_fin/260823_owner_backlog_closeout/001_reviewer_evidence.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/010_wp1_pr2439_contract_manifest.md b/devlog/_fin/260823_owner_backlog_closeout/010_wp1_pr2439_contract_manifest.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/010_wp1_pr2439_contract_manifest.md rename to devlog/_fin/260823_owner_backlog_closeout/010_wp1_pr2439_contract_manifest.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/020_wp2_pr2437_history_manifest.md b/devlog/_fin/260823_owner_backlog_closeout/020_wp2_pr2437_history_manifest.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/020_wp2_pr2437_history_manifest.md rename to devlog/_fin/260823_owner_backlog_closeout/020_wp2_pr2437_history_manifest.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/030_wp3_pr2435_fetch_helper_boundary.md b/devlog/_fin/260823_owner_backlog_closeout/030_wp3_pr2435_fetch_helper_boundary.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/030_wp3_pr2435_fetch_helper_boundary.md rename to devlog/_fin/260823_owner_backlog_closeout/030_wp3_pr2435_fetch_helper_boundary.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/040_wp4_pr2433_combo_failover.md b/devlog/_fin/260823_owner_backlog_closeout/040_wp4_pr2433_combo_failover.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/040_wp4_pr2433_combo_failover.md rename to devlog/_fin/260823_owner_backlog_closeout/040_wp4_pr2433_combo_failover.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/050_wp5_pr2387_process_state.md b/devlog/_fin/260823_owner_backlog_closeout/050_wp5_pr2387_process_state.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/050_wp5_pr2387_process_state.md rename to devlog/_fin/260823_owner_backlog_closeout/050_wp5_pr2387_process_state.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/060_wp6_pr2380_provider_validation.md b/devlog/_fin/260823_owner_backlog_closeout/060_wp6_pr2380_provider_validation.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/060_wp6_pr2380_provider_validation.md rename to devlog/_fin/260823_owner_backlog_closeout/060_wp6_pr2380_provider_validation.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/070_wp4b_exactly_once_terminal.md b/devlog/_fin/260823_owner_backlog_closeout/070_wp4b_exactly_once_terminal.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/070_wp4b_exactly_once_terminal.md rename to devlog/_fin/260823_owner_backlog_closeout/070_wp4b_exactly_once_terminal.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/080_wp7_issue2392_auth_error_mapping.md b/devlog/_fin/260823_owner_backlog_closeout/080_wp7_issue2392_auth_error_mapping.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/080_wp7_issue2392_auth_error_mapping.md rename to devlog/_fin/260823_owner_backlog_closeout/080_wp7_issue2392_auth_error_mapping.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/090_wp8_issue2443_wait_coercion.md b/devlog/_fin/260823_owner_backlog_closeout/090_wp8_issue2443_wait_coercion.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/090_wp8_issue2443_wait_coercion.md rename to devlog/_fin/260823_owner_backlog_closeout/090_wp8_issue2443_wait_coercion.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/100_wp9_verdict_only_issues.md b/devlog/_fin/260823_owner_backlog_closeout/100_wp9_verdict_only_issues.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/100_wp9_verdict_only_issues.md rename to devlog/_fin/260823_owner_backlog_closeout/100_wp9_verdict_only_issues.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/110_wp10_roadmap_issue_dispositions.md b/devlog/_fin/260823_owner_backlog_closeout/110_wp10_roadmap_issue_dispositions.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/110_wp10_roadmap_issue_dispositions.md rename to devlog/_fin/260823_owner_backlog_closeout/110_wp10_roadmap_issue_dispositions.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/120_wp11_issue1702_combo_quota.md b/devlog/_fin/260823_owner_backlog_closeout/120_wp11_issue1702_combo_quota.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/120_wp11_issue1702_combo_quota.md rename to devlog/_fin/260823_owner_backlog_closeout/120_wp11_issue1702_combo_quota.md diff --git a/devlog/_plan/260823_owner_backlog_closeout/900_closing_reconciliation.md b/devlog/_fin/260823_owner_backlog_closeout/900_closing_reconciliation.md similarity index 100% rename from devlog/_plan/260823_owner_backlog_closeout/900_closing_reconciliation.md rename to devlog/_fin/260823_owner_backlog_closeout/900_closing_reconciliation.md diff --git a/devlog/_plan/260824_v2_32_1_hotfix_train/000_baseline_scope_and_roadmap.md b/devlog/_fin/260824_v2_32_1_hotfix_train/000_baseline_scope_and_roadmap.md similarity index 100% rename from devlog/_plan/260824_v2_32_1_hotfix_train/000_baseline_scope_and_roadmap.md rename to devlog/_fin/260824_v2_32_1_hotfix_train/000_baseline_scope_and_roadmap.md diff --git a/devlog/_plan/260824_v2_32_1_hotfix_train/001_reviewer_lane_evidence.md b/devlog/_fin/260824_v2_32_1_hotfix_train/001_reviewer_lane_evidence.md similarity index 100% rename from devlog/_plan/260824_v2_32_1_hotfix_train/001_reviewer_lane_evidence.md rename to devlog/_fin/260824_v2_32_1_hotfix_train/001_reviewer_lane_evidence.md diff --git a/devlog/_plan/260824_v2_32_1_hotfix_train/010_wp1_dev_fastforward_to_release_lineage.md b/devlog/_fin/260824_v2_32_1_hotfix_train/010_wp1_dev_fastforward_to_release_lineage.md similarity index 100% rename from devlog/_plan/260824_v2_32_1_hotfix_train/010_wp1_dev_fastforward_to_release_lineage.md rename to devlog/_fin/260824_v2_32_1_hotfix_train/010_wp1_dev_fastforward_to_release_lineage.md diff --git a/devlog/_plan/260824_v2_32_1_hotfix_train/020_wp3_pr2483_anthropic_id_classification.md b/devlog/_fin/260824_v2_32_1_hotfix_train/020_wp3_pr2483_anthropic_id_classification.md similarity index 100% rename from devlog/_plan/260824_v2_32_1_hotfix_train/020_wp3_pr2483_anthropic_id_classification.md rename to devlog/_fin/260824_v2_32_1_hotfix_train/020_wp3_pr2483_anthropic_id_classification.md diff --git a/devlog/_plan/260824_v2_32_1_hotfix_train/030_wp4_pr2481_selectedmodels_slug_equivalence.md b/devlog/_fin/260824_v2_32_1_hotfix_train/030_wp4_pr2481_selectedmodels_slug_equivalence.md similarity index 100% rename from devlog/_plan/260824_v2_32_1_hotfix_train/030_wp4_pr2481_selectedmodels_slug_equivalence.md rename to devlog/_fin/260824_v2_32_1_hotfix_train/030_wp4_pr2481_selectedmodels_slug_equivalence.md diff --git a/devlog/_plan/260824_v2_32_1_hotfix_train/040_wp5_pr2473_oversized_ws_transport.md b/devlog/_fin/260824_v2_32_1_hotfix_train/040_wp5_pr2473_oversized_ws_transport.md similarity index 100% rename from devlog/_plan/260824_v2_32_1_hotfix_train/040_wp5_pr2473_oversized_ws_transport.md rename to devlog/_fin/260824_v2_32_1_hotfix_train/040_wp5_pr2473_oversized_ws_transport.md diff --git a/devlog/_plan/260824_v2_32_1_hotfix_train/050_wp6_pr2477_namespace_alias_authorization.md b/devlog/_fin/260824_v2_32_1_hotfix_train/050_wp6_pr2477_namespace_alias_authorization.md similarity index 100% rename from devlog/_plan/260824_v2_32_1_hotfix_train/050_wp6_pr2477_namespace_alias_authorization.md rename to devlog/_fin/260824_v2_32_1_hotfix_train/050_wp6_pr2477_namespace_alias_authorization.md diff --git a/devlog/_plan/260824_v2_32_1_hotfix_train/060_wp7_pr2476_snapshot_write_amplification.md b/devlog/_fin/260824_v2_32_1_hotfix_train/060_wp7_pr2476_snapshot_write_amplification.md similarity index 100% rename from devlog/_plan/260824_v2_32_1_hotfix_train/060_wp7_pr2476_snapshot_write_amplification.md rename to devlog/_fin/260824_v2_32_1_hotfix_train/060_wp7_pr2476_snapshot_write_amplification.md diff --git a/devlog/_plan/260824_v2_32_1_hotfix_train/070_wp2_pr2427_parallel_test_runner.md b/devlog/_fin/260824_v2_32_1_hotfix_train/070_wp2_pr2427_parallel_test_runner.md similarity index 100% rename from devlog/_plan/260824_v2_32_1_hotfix_train/070_wp2_pr2427_parallel_test_runner.md rename to devlog/_fin/260824_v2_32_1_hotfix_train/070_wp2_pr2427_parallel_test_runner.md diff --git a/devlog/_plan/260824_v2_32_1_hotfix_train/080_wp8_freeze_verification_and_go_nogo.md b/devlog/_fin/260824_v2_32_1_hotfix_train/080_wp8_freeze_verification_and_go_nogo.md similarity index 100% rename from devlog/_plan/260824_v2_32_1_hotfix_train/080_wp8_freeze_verification_and_go_nogo.md rename to devlog/_fin/260824_v2_32_1_hotfix_train/080_wp8_freeze_verification_and_go_nogo.md diff --git a/devlog/_plan/260824_v2_32_1_hotfix_train/090_wp9_issue2472_mixed_sequence_regression.md b/devlog/_fin/260824_v2_32_1_hotfix_train/090_wp9_issue2472_mixed_sequence_regression.md similarity index 100% rename from devlog/_plan/260824_v2_32_1_hotfix_train/090_wp9_issue2472_mixed_sequence_regression.md rename to devlog/_fin/260824_v2_32_1_hotfix_train/090_wp9_issue2472_mixed_sequence_regression.md diff --git a/devlog/_plan/260824_v2_32_1_hotfix_train/900_go_nogo_readiness_report.md b/devlog/_fin/260824_v2_32_1_hotfix_train/900_go_nogo_readiness_report.md similarity index 100% rename from devlog/_plan/260824_v2_32_1_hotfix_train/900_go_nogo_readiness_report.md rename to devlog/_fin/260824_v2_32_1_hotfix_train/900_go_nogo_readiness_report.md diff --git a/devlog/_plan/260825_oauth_login_ux/000_baseline_and_scope.md b/devlog/_fin/260825_oauth_login_ux/000_baseline_and_scope.md similarity index 100% rename from devlog/_plan/260825_oauth_login_ux/000_baseline_and_scope.md rename to devlog/_fin/260825_oauth_login_ux/000_baseline_and_scope.md diff --git a/devlog/_plan/260825_oauth_login_ux/001_current_state_inventory.md b/devlog/_fin/260825_oauth_login_ux/001_current_state_inventory.md similarity index 100% rename from devlog/_plan/260825_oauth_login_ux/001_current_state_inventory.md rename to devlog/_fin/260825_oauth_login_ux/001_current_state_inventory.md diff --git a/devlog/_plan/260825_oauth_login_ux/002_plan_audit.md b/devlog/_fin/260825_oauth_login_ux/002_plan_audit.md similarity index 100% rename from devlog/_plan/260825_oauth_login_ux/002_plan_audit.md rename to devlog/_fin/260825_oauth_login_ux/002_plan_audit.md diff --git a/devlog/_plan/260825_oauth_login_ux/003_delivery_map.md b/devlog/_fin/260825_oauth_login_ux/003_delivery_map.md similarity index 100% rename from devlog/_plan/260825_oauth_login_ux/003_delivery_map.md rename to devlog/_fin/260825_oauth_login_ux/003_delivery_map.md diff --git a/devlog/_plan/260825_oauth_login_ux/010_wp2_shared_login_hint.md b/devlog/_fin/260825_oauth_login_ux/010_wp2_shared_login_hint.md similarity index 100% rename from devlog/_plan/260825_oauth_login_ux/010_wp2_shared_login_hint.md rename to devlog/_fin/260825_oauth_login_ux/010_wp2_shared_login_hint.md diff --git a/devlog/_plan/260825_oauth_login_ux/020_wp3_first_add_parity.md b/devlog/_fin/260825_oauth_login_ux/020_wp3_first_add_parity.md similarity index 100% rename from devlog/_plan/260825_oauth_login_ux/020_wp3_first_add_parity.md rename to devlog/_fin/260825_oauth_login_ux/020_wp3_first_add_parity.md diff --git a/devlog/_plan/260825_oauth_login_ux/030_wp4_browser_open_control.md b/devlog/_fin/260825_oauth_login_ux/030_wp4_browser_open_control.md similarity index 100% rename from devlog/_plan/260825_oauth_login_ux/030_wp4_browser_open_control.md rename to devlog/_fin/260825_oauth_login_ux/030_wp4_browser_open_control.md diff --git a/devlog/_plan/260825_oauth_login_ux/040_wp5_paste_normalization.md b/devlog/_fin/260825_oauth_login_ux/040_wp5_paste_normalization.md similarity index 100% rename from devlog/_plan/260825_oauth_login_ux/040_wp5_paste_normalization.md rename to devlog/_fin/260825_oauth_login_ux/040_wp5_paste_normalization.md diff --git a/devlog/_plan/260825_oauth_login_ux/090_merge_train_closeout.md b/devlog/_fin/260825_oauth_login_ux/090_merge_train_closeout.md similarity index 100% rename from devlog/_plan/260825_oauth_login_ux/090_merge_train_closeout.md rename to devlog/_fin/260825_oauth_login_ux/090_merge_train_closeout.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/000_research_snapshot.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/000_research_snapshot.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/000_research_snapshot.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/000_research_snapshot.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/001_audit_response.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/001_audit_response.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/001_audit_response.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/001_audit_response.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/010_wp2_merge_train_remainder.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/010_wp2_merge_train_remainder.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/010_wp2_merge_train_remainder.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/010_wp2_merge_train_remainder.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/020_wp4_google_lane.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/020_wp4_google_lane.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/020_wp4_google_lane.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/020_wp4_google_lane.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/030_wp5_hygiene_and_drafts.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/030_wp5_hygiene_and_drafts.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/030_wp5_hygiene_and_drafts.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/030_wp5_hygiene_and_drafts.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/040_wp6_cursor_catalog.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/040_wp6_cursor_catalog.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/040_wp6_cursor_catalog.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/040_wp6_cursor_catalog.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/050_wp7_oauth_failover.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/050_wp7_oauth_failover.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/050_wp7_oauth_failover.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/050_wp7_oauth_failover.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/060_wp8_wp9_cli_and_platform.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/060_wp8_wp9_cli_and_platform.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/060_wp8_wp9_cli_and_platform.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/060_wp8_wp9_cli_and_platform.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/070_wp10_wp11_slug_and_exec.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/070_wp10_wp11_slug_and_exec.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/070_wp10_wp11_slug_and_exec.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/070_wp10_wp11_slug_and_exec.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/080_wp12_catalog_ux.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/080_wp12_catalog_ux.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/080_wp12_catalog_ux.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/080_wp12_catalog_ux.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/090_wp13_architecture.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/090_wp13_architecture.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/090_wp13_architecture.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/090_wp13_architecture.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/100_wp6_wp8_execution.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/100_wp6_wp8_execution.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/100_wp6_wp8_execution.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/100_wp6_wp8_execution.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/110_wp7_oauth_failover.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/110_wp7_oauth_failover.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/110_wp7_oauth_failover.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/110_wp7_oauth_failover.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/111_wp7_audit_response.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/111_wp7_audit_response.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/111_wp7_audit_response.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/111_wp7_audit_response.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/120_wp5_2497_security_review.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/120_wp5_2497_security_review.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/120_wp5_2497_security_review.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/120_wp5_2497_security_review.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/130_wp16_late_bug_prs.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/130_wp16_late_bug_prs.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/130_wp16_late_bug_prs.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/130_wp16_late_bug_prs.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/140_closeout.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/140_closeout.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/140_closeout.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/140_closeout.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/150_community_bug_sweep.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/150_community_bug_sweep.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/150_community_bug_sweep.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/150_community_bug_sweep.md diff --git a/devlog/_plan/260825_owner_backlog_and_bugpr_closeout/160_terminal_record.md b/devlog/_fin/260825_owner_backlog_and_bugpr_closeout/160_terminal_record.md similarity index 100% rename from devlog/_plan/260825_owner_backlog_and_bugpr_closeout/160_terminal_record.md rename to devlog/_fin/260825_owner_backlog_and_bugpr_closeout/160_terminal_record.md diff --git a/devlog/_plan/260826_backlog_triage/000_snapshot.md b/devlog/_fin/260826_backlog_triage/000_snapshot.md similarity index 100% rename from devlog/_plan/260826_backlog_triage/000_snapshot.md rename to devlog/_fin/260826_backlog_triage/000_snapshot.md diff --git a/devlog/_plan/260826_backlog_triage/010_stale_prs.md b/devlog/_fin/260826_backlog_triage/010_stale_prs.md similarity index 100% rename from devlog/_plan/260826_backlog_triage/010_stale_prs.md rename to devlog/_fin/260826_backlog_triage/010_stale_prs.md diff --git a/devlog/_plan/260826_backlog_triage/020_issue_quick_wins.md b/devlog/_fin/260826_backlog_triage/020_issue_quick_wins.md similarity index 100% rename from devlog/_plan/260826_backlog_triage/020_issue_quick_wins.md rename to devlog/_fin/260826_backlog_triage/020_issue_quick_wins.md diff --git a/devlog/_plan/260826_backlog_triage/030_recommendations.md b/devlog/_fin/260826_backlog_triage/030_recommendations.md similarity index 100% rename from devlog/_plan/260826_backlog_triage/030_recommendations.md rename to devlog/_fin/260826_backlog_triage/030_recommendations.md diff --git a/devlog/_plan/260826_glm53_flash_preseed/000_plan.md b/devlog/_fin/260826_glm53_flash_preseed/000_plan.md similarity index 100% rename from devlog/_plan/260826_glm53_flash_preseed/000_plan.md rename to devlog/_fin/260826_glm53_flash_preseed/000_plan.md diff --git a/devlog/_plan/260826_quota_window_and_backlog/000_plan.md b/devlog/_fin/260826_quota_window_and_backlog/000_plan.md similarity index 100% rename from devlog/_plan/260826_quota_window_and_backlog/000_plan.md rename to devlog/_fin/260826_quota_window_and_backlog/000_plan.md diff --git a/devlog/_plan/260826_quota_window_and_backlog/001_audit_response.md b/devlog/_fin/260826_quota_window_and_backlog/001_audit_response.md similarity index 100% rename from devlog/_plan/260826_quota_window_and_backlog/001_audit_response.md rename to devlog/_fin/260826_quota_window_and_backlog/001_audit_response.md diff --git a/devlog/_plan/260826_quota_window_and_backlog/010_phase1.md b/devlog/_fin/260826_quota_window_and_backlog/010_phase1.md similarity index 100% rename from devlog/_plan/260826_quota_window_and_backlog/010_phase1.md rename to devlog/_fin/260826_quota_window_and_backlog/010_phase1.md diff --git a/devlog/_plan/260826_quota_window_and_backlog/020_phase2.md b/devlog/_fin/260826_quota_window_and_backlog/020_phase2.md similarity index 100% rename from devlog/_plan/260826_quota_window_and_backlog/020_phase2.md rename to devlog/_fin/260826_quota_window_and_backlog/020_phase2.md diff --git a/devlog/_plan/260826_quota_window_and_backlog/030_phase3.md b/devlog/_fin/260826_quota_window_and_backlog/030_phase3.md similarity index 100% rename from devlog/_plan/260826_quota_window_and_backlog/030_phase3.md rename to devlog/_fin/260826_quota_window_and_backlog/030_phase3.md diff --git a/devlog/_plan/260826_quota_window_and_backlog/040_phase4.md b/devlog/_fin/260826_quota_window_and_backlog/040_phase4.md similarity index 100% rename from devlog/_plan/260826_quota_window_and_backlog/040_phase4.md rename to devlog/_fin/260826_quota_window_and_backlog/040_phase4.md diff --git a/devlog/_plan/260826_quota_window_and_backlog/050_phase5.md b/devlog/_fin/260826_quota_window_and_backlog/050_phase5.md similarity index 100% rename from devlog/_plan/260826_quota_window_and_backlog/050_phase5.md rename to devlog/_fin/260826_quota_window_and_backlog/050_phase5.md diff --git a/devlog/_plan/260826_quota_window_and_backlog/060_phase6.md b/devlog/_fin/260826_quota_window_and_backlog/060_phase6.md similarity index 100% rename from devlog/_plan/260826_quota_window_and_backlog/060_phase6.md rename to devlog/_fin/260826_quota_window_and_backlog/060_phase6.md diff --git a/devlog/_plan/260826_quota_window_and_backlog/070_phase7.md b/devlog/_fin/260826_quota_window_and_backlog/070_phase7.md similarity index 100% rename from devlog/_plan/260826_quota_window_and_backlog/070_phase7.md rename to devlog/_fin/260826_quota_window_and_backlog/070_phase7.md diff --git a/devlog/_plan/260826_wp13_disposable_host/010_census.md b/devlog/_fin/260826_wp13_disposable_host/010_census.md similarity index 100% rename from devlog/_plan/260826_wp13_disposable_host/010_census.md rename to devlog/_fin/260826_wp13_disposable_host/010_census.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/000_intake.md b/devlog/_fin/260827_bug_pr_merge_round/000_intake.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/000_intake.md rename to devlog/_fin/260827_bug_pr_merge_round/000_intake.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/001_pr2694_compile_break.md b/devlog/_fin/260827_bug_pr_merge_round/001_pr2694_compile_break.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/001_pr2694_compile_break.md rename to devlog/_fin/260827_bug_pr_merge_round/001_pr2694_compile_break.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/002_pr2639_regression.md b/devlog/_fin/260827_bug_pr_merge_round/002_pr2639_regression.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/002_pr2639_regression.md rename to devlog/_fin/260827_bug_pr_merge_round/002_pr2639_regression.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/003_disposition_matrix.md b/devlog/_fin/260827_bug_pr_merge_round/003_disposition_matrix.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/003_disposition_matrix.md rename to devlog/_fin/260827_bug_pr_merge_round/003_disposition_matrix.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/004_pr2693_missing_implementation.md b/devlog/_fin/260827_bug_pr_merge_round/004_pr2693_missing_implementation.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/004_pr2693_missing_implementation.md rename to devlog/_fin/260827_bug_pr_merge_round/004_pr2693_missing_implementation.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/005_audit_corrections.md b/devlog/_fin/260827_bug_pr_merge_round/005_audit_corrections.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/005_audit_corrections.md rename to devlog/_fin/260827_bug_pr_merge_round/005_audit_corrections.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/006_execution_order.md b/devlog/_fin/260827_bug_pr_merge_round/006_execution_order.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/006_execution_order.md rename to devlog/_fin/260827_bug_pr_merge_round/006_execution_order.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/007_audit_round2.md b/devlog/_fin/260827_bug_pr_merge_round/007_audit_round2.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/007_audit_round2.md rename to devlog/_fin/260827_bug_pr_merge_round/007_audit_round2.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/010_lane_l1_commit_then_merge.md b/devlog/_fin/260827_bug_pr_merge_round/010_lane_l1_commit_then_merge.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/010_lane_l1_commit_then_merge.md rename to devlog/_fin/260827_bug_pr_merge_round/010_lane_l1_commit_then_merge.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/011_wp2_l1_outcome.md b/devlog/_fin/260827_bug_pr_merge_round/011_wp2_l1_outcome.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/011_wp2_l1_outcome.md rename to devlog/_fin/260827_bug_pr_merge_round/011_wp2_l1_outcome.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/020_lane_l3_cherry_pick.md b/devlog/_fin/260827_bug_pr_merge_round/020_lane_l3_cherry_pick.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/020_lane_l3_cherry_pick.md rename to devlog/_fin/260827_bug_pr_merge_round/020_lane_l3_cherry_pick.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/021_status_vs_created_at_asymmetry.md b/devlog/_fin/260827_bug_pr_merge_round/021_status_vs_created_at_asymmetry.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/021_status_vs_created_at_asymmetry.md rename to devlog/_fin/260827_bug_pr_merge_round/021_status_vs_created_at_asymmetry.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/022_l3_audit.md b/devlog/_fin/260827_bug_pr_merge_round/022_l3_audit.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/022_l3_audit.md rename to devlog/_fin/260827_bug_pr_merge_round/022_l3_audit.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/030_lane_l2_close_squash.md b/devlog/_fin/260827_bug_pr_merge_round/030_lane_l2_close_squash.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/030_lane_l2_close_squash.md rename to devlog/_fin/260827_bug_pr_merge_round/030_lane_l2_close_squash.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/031_wp4_l2_outcome.md b/devlog/_fin/260827_bug_pr_merge_round/031_wp4_l2_outcome.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/031_wp4_l2_outcome.md rename to devlog/_fin/260827_bug_pr_merge_round/031_wp4_l2_outcome.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/040_lane_l4_reimplement.md b/devlog/_fin/260827_bug_pr_merge_round/040_lane_l4_reimplement.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/040_lane_l4_reimplement.md rename to devlog/_fin/260827_bug_pr_merge_round/040_lane_l4_reimplement.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/041_wp5_l4_outcome.md b/devlog/_fin/260827_bug_pr_merge_round/041_wp5_l4_outcome.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/041_wp5_l4_outcome.md rename to devlog/_fin/260827_bug_pr_merge_round/041_wp5_l4_outcome.md diff --git a/devlog/_plan/260827_bug_pr_merge_round/050_round_closeout.md b/devlog/_fin/260827_bug_pr_merge_round/050_round_closeout.md similarity index 100% rename from devlog/_plan/260827_bug_pr_merge_round/050_round_closeout.md rename to devlog/_fin/260827_bug_pr_merge_round/050_round_closeout.md diff --git a/devlog/_plan/260827_dev_hardening/000_inventory.md b/devlog/_fin/260827_dev_hardening/000_inventory.md similarity index 100% rename from devlog/_plan/260827_dev_hardening/000_inventory.md rename to devlog/_fin/260827_dev_hardening/000_inventory.md diff --git a/devlog/_plan/260827_dev_hardening/010_wp2_version_line.md b/devlog/_fin/260827_dev_hardening/010_wp2_version_line.md similarity index 100% rename from devlog/_plan/260827_dev_hardening/010_wp2_version_line.md rename to devlog/_fin/260827_dev_hardening/010_wp2_version_line.md diff --git a/devlog/_plan/260827_dev_hardening/020_wp3_failover_identity.md b/devlog/_fin/260827_dev_hardening/020_wp3_failover_identity.md similarity index 100% rename from devlog/_plan/260827_dev_hardening/020_wp3_failover_identity.md rename to devlog/_fin/260827_dev_hardening/020_wp3_failover_identity.md diff --git a/devlog/_plan/260827_dev_hardening/030_wp4_locale_docs.md b/devlog/_fin/260827_dev_hardening/030_wp4_locale_docs.md similarity index 100% rename from devlog/_plan/260827_dev_hardening/030_wp4_locale_docs.md rename to devlog/_fin/260827_dev_hardening/030_wp4_locale_docs.md diff --git a/devlog/_plan/260827_dev_hardening/040_wp5_local_gates.md b/devlog/_fin/260827_dev_hardening/040_wp5_local_gates.md similarity index 100% rename from devlog/_plan/260827_dev_hardening/040_wp5_local_gates.md rename to devlog/_fin/260827_dev_hardening/040_wp5_local_gates.md diff --git a/devlog/_plan/260827_dev_hardening/050_wp6_invariant_locks.md b/devlog/_fin/260827_dev_hardening/050_wp6_invariant_locks.md similarity index 100% rename from devlog/_plan/260827_dev_hardening/050_wp6_invariant_locks.md rename to devlog/_fin/260827_dev_hardening/050_wp6_invariant_locks.md diff --git a/devlog/_plan/260827_dev_hardening/060_wp8_launcher_flake.md b/devlog/_fin/260827_dev_hardening/060_wp8_launcher_flake.md similarity index 100% rename from devlog/_plan/260827_dev_hardening/060_wp8_launcher_flake.md rename to devlog/_fin/260827_dev_hardening/060_wp8_launcher_flake.md diff --git a/devlog/_plan/260827_dev_hardening/070_wp7_promotion_readiness.md b/devlog/_fin/260827_dev_hardening/070_wp7_promotion_readiness.md similarity index 100% rename from devlog/_plan/260827_dev_hardening/070_wp7_promotion_readiness.md rename to devlog/_fin/260827_dev_hardening/070_wp7_promotion_readiness.md diff --git a/devlog/_plan/260827_dev_regression_and_prompt_variants/000_plan.md b/devlog/_fin/260827_dev_regression_and_prompt_variants/000_plan.md similarity index 100% rename from devlog/_plan/260827_dev_regression_and_prompt_variants/000_plan.md rename to devlog/_fin/260827_dev_regression_and_prompt_variants/000_plan.md diff --git a/devlog/_plan/260827_dev_regression_and_prompt_variants/001_regression_review.md b/devlog/_fin/260827_dev_regression_and_prompt_variants/001_regression_review.md similarity index 100% rename from devlog/_plan/260827_dev_regression_and_prompt_variants/001_regression_review.md rename to devlog/_fin/260827_dev_regression_and_prompt_variants/001_regression_review.md diff --git a/devlog/_plan/260827_dev_regression_and_prompt_variants/010_wp2_git_attribution_layer.md b/devlog/_fin/260827_dev_regression_and_prompt_variants/010_wp2_git_attribution_layer.md similarity index 100% rename from devlog/_plan/260827_dev_regression_and_prompt_variants/010_wp2_git_attribution_layer.md rename to devlog/_fin/260827_dev_regression_and_prompt_variants/010_wp2_git_attribution_layer.md diff --git a/devlog/_plan/260827_dev_regression_and_prompt_variants/020_wp3_base_variants.md b/devlog/_fin/260827_dev_regression_and_prompt_variants/020_wp3_base_variants.md similarity index 100% rename from devlog/_plan/260827_dev_regression_and_prompt_variants/020_wp3_base_variants.md rename to devlog/_fin/260827_dev_regression_and_prompt_variants/020_wp3_base_variants.md diff --git a/devlog/_plan/260827_dev_regression_and_prompt_variants/021_audit_blockers.md b/devlog/_fin/260827_dev_regression_and_prompt_variants/021_audit_blockers.md similarity index 100% rename from devlog/_plan/260827_dev_regression_and_prompt_variants/021_audit_blockers.md rename to devlog/_fin/260827_dev_regression_and_prompt_variants/021_audit_blockers.md diff --git a/devlog/_plan/260827_dev_regression_and_prompt_variants/030_wp4_docs_and_stack.md b/devlog/_fin/260827_dev_regression_and_prompt_variants/030_wp4_docs_and_stack.md similarity index 100% rename from devlog/_plan/260827_dev_regression_and_prompt_variants/030_wp4_docs_and_stack.md rename to devlog/_fin/260827_dev_regression_and_prompt_variants/030_wp4_docs_and_stack.md diff --git a/devlog/_plan/260827_dev_regression_and_prompt_variants/040_remote_verification_and_wp3_landing.md b/devlog/_fin/260827_dev_regression_and_prompt_variants/040_remote_verification_and_wp3_landing.md similarity index 100% rename from devlog/_plan/260827_dev_regression_and_prompt_variants/040_remote_verification_and_wp3_landing.md rename to devlog/_fin/260827_dev_regression_and_prompt_variants/040_remote_verification_and_wp3_landing.md diff --git a/devlog/_plan/260827_dev_regression_and_prompt_variants/041_wp3_audit_direct.md b/devlog/_fin/260827_dev_regression_and_prompt_variants/041_wp3_audit_direct.md similarity index 100% rename from devlog/_plan/260827_dev_regression_and_prompt_variants/041_wp3_audit_direct.md rename to devlog/_fin/260827_dev_regression_and_prompt_variants/041_wp3_audit_direct.md diff --git a/devlog/_plan/260827_dev_regression_and_prompt_variants/042_wp3_remote_gate_and_landing.md b/devlog/_fin/260827_dev_regression_and_prompt_variants/042_wp3_remote_gate_and_landing.md similarity index 100% rename from devlog/_plan/260827_dev_regression_and_prompt_variants/042_wp3_remote_gate_and_landing.md rename to devlog/_fin/260827_dev_regression_and_prompt_variants/042_wp3_remote_gate_and_landing.md diff --git a/devlog/_plan/260827_igwanu_bug_pr_merge_round/000_plan.md b/devlog/_fin/260827_igwanu_bug_pr_merge_round/000_plan.md similarity index 100% rename from devlog/_plan/260827_igwanu_bug_pr_merge_round/000_plan.md rename to devlog/_fin/260827_igwanu_bug_pr_merge_round/000_plan.md diff --git a/devlog/_plan/260827_igwanu_bug_pr_merge_round/010_phase1.md b/devlog/_fin/260827_igwanu_bug_pr_merge_round/010_phase1.md similarity index 100% rename from devlog/_plan/260827_igwanu_bug_pr_merge_round/010_phase1.md rename to devlog/_fin/260827_igwanu_bug_pr_merge_round/010_phase1.md diff --git a/devlog/_plan/260827_igwanu_bug_pr_merge_round/011_wp1_outcome.md b/devlog/_fin/260827_igwanu_bug_pr_merge_round/011_wp1_outcome.md similarity index 100% rename from devlog/_plan/260827_igwanu_bug_pr_merge_round/011_wp1_outcome.md rename to devlog/_fin/260827_igwanu_bug_pr_merge_round/011_wp1_outcome.md diff --git a/devlog/_plan/260827_igwanu_bug_pr_merge_round/020_phase2.md b/devlog/_fin/260827_igwanu_bug_pr_merge_round/020_phase2.md similarity index 100% rename from devlog/_plan/260827_igwanu_bug_pr_merge_round/020_phase2.md rename to devlog/_fin/260827_igwanu_bug_pr_merge_round/020_phase2.md diff --git a/devlog/_plan/260827_igwanu_bug_pr_merge_round/021_wp2_outcome.md b/devlog/_fin/260827_igwanu_bug_pr_merge_round/021_wp2_outcome.md similarity index 100% rename from devlog/_plan/260827_igwanu_bug_pr_merge_round/021_wp2_outcome.md rename to devlog/_fin/260827_igwanu_bug_pr_merge_round/021_wp2_outcome.md diff --git a/devlog/_plan/260827_igwanu_bug_pr_merge_round/030_phase3.md b/devlog/_fin/260827_igwanu_bug_pr_merge_round/030_phase3.md similarity index 100% rename from devlog/_plan/260827_igwanu_bug_pr_merge_round/030_phase3.md rename to devlog/_fin/260827_igwanu_bug_pr_merge_round/030_phase3.md diff --git a/devlog/_plan/260827_igwanu_bug_pr_merge_round/040_phase4.md b/devlog/_fin/260827_igwanu_bug_pr_merge_round/040_phase4.md similarity index 100% rename from devlog/_plan/260827_igwanu_bug_pr_merge_round/040_phase4.md rename to devlog/_fin/260827_igwanu_bug_pr_merge_round/040_phase4.md diff --git a/devlog/_plan/260827_igwanu_bug_pr_merge_round/041_wp2b_2729_supersede.md b/devlog/_fin/260827_igwanu_bug_pr_merge_round/041_wp2b_2729_supersede.md similarity index 100% rename from devlog/_plan/260827_igwanu_bug_pr_merge_round/041_wp2b_2729_supersede.md rename to devlog/_fin/260827_igwanu_bug_pr_merge_round/041_wp2b_2729_supersede.md diff --git a/devlog/_plan/260827_igwanu_bug_pr_merge_round/050_phase5.md b/devlog/_fin/260827_igwanu_bug_pr_merge_round/050_phase5.md similarity index 100% rename from devlog/_plan/260827_igwanu_bug_pr_merge_round/050_phase5.md rename to devlog/_fin/260827_igwanu_bug_pr_merge_round/050_phase5.md diff --git a/devlog/_plan/260827_igwanu_bug_pr_merge_round/060_phase6.md b/devlog/_fin/260827_igwanu_bug_pr_merge_round/060_phase6.md similarity index 100% rename from devlog/_plan/260827_igwanu_bug_pr_merge_round/060_phase6.md rename to devlog/_fin/260827_igwanu_bug_pr_merge_round/060_phase6.md diff --git a/devlog/_plan/260827_igwanu_bug_pr_merge_round/070_outcome.md b/devlog/_fin/260827_igwanu_bug_pr_merge_round/070_outcome.md similarity index 100% rename from devlog/_plan/260827_igwanu_bug_pr_merge_round/070_outcome.md rename to devlog/_fin/260827_igwanu_bug_pr_merge_round/070_outcome.md diff --git a/devlog/_plan/260827_kiro_subagent_delegation_unblock/000_plan.md b/devlog/_fin/260827_kiro_subagent_delegation_unblock/000_plan.md similarity index 100% rename from devlog/_plan/260827_kiro_subagent_delegation_unblock/000_plan.md rename to devlog/_fin/260827_kiro_subagent_delegation_unblock/000_plan.md diff --git a/devlog/_plan/260827_kiro_subagent_delegation_unblock/010_exec_discovery_preservation.md b/devlog/_fin/260827_kiro_subagent_delegation_unblock/010_exec_discovery_preservation.md similarity index 100% rename from devlog/_plan/260827_kiro_subagent_delegation_unblock/010_exec_discovery_preservation.md rename to devlog/_fin/260827_kiro_subagent_delegation_unblock/010_exec_discovery_preservation.md diff --git a/devlog/_plan/260827_kiro_subagent_delegation_unblock/011_root_cause_nudge_gap.md b/devlog/_fin/260827_kiro_subagent_delegation_unblock/011_root_cause_nudge_gap.md similarity index 100% rename from devlog/_plan/260827_kiro_subagent_delegation_unblock/011_root_cause_nudge_gap.md rename to devlog/_fin/260827_kiro_subagent_delegation_unblock/011_root_cause_nudge_gap.md diff --git a/devlog/_plan/260827_kiro_subagent_delegation_unblock/012_audit_round1_synthesis.md b/devlog/_fin/260827_kiro_subagent_delegation_unblock/012_audit_round1_synthesis.md similarity index 100% rename from devlog/_plan/260827_kiro_subagent_delegation_unblock/012_audit_round1_synthesis.md rename to devlog/_fin/260827_kiro_subagent_delegation_unblock/012_audit_round1_synthesis.md diff --git a/devlog/_plan/260827_kiro_subagent_delegation_unblock/013_wp1_check_evidence.md b/devlog/_fin/260827_kiro_subagent_delegation_unblock/013_wp1_check_evidence.md similarity index 100% rename from devlog/_plan/260827_kiro_subagent_delegation_unblock/013_wp1_check_evidence.md rename to devlog/_fin/260827_kiro_subagent_delegation_unblock/013_wp1_check_evidence.md diff --git a/devlog/_plan/260827_kiro_subagent_delegation_unblock/020_unbounded_kiro_delegation.md b/devlog/_fin/260827_kiro_subagent_delegation_unblock/020_unbounded_kiro_delegation.md similarity index 100% rename from devlog/_plan/260827_kiro_subagent_delegation_unblock/020_unbounded_kiro_delegation.md rename to devlog/_fin/260827_kiro_subagent_delegation_unblock/020_unbounded_kiro_delegation.md diff --git a/devlog/_plan/260827_kiro_subagent_delegation_unblock/030_verification.md b/devlog/_fin/260827_kiro_subagent_delegation_unblock/030_verification.md similarity index 100% rename from devlog/_plan/260827_kiro_subagent_delegation_unblock/030_verification.md rename to devlog/_fin/260827_kiro_subagent_delegation_unblock/030_verification.md diff --git a/devlog/_plan/260827_kiro_subagent_delegation_unblock/040_exec_catalog_priority.md b/devlog/_fin/260827_kiro_subagent_delegation_unblock/040_exec_catalog_priority.md similarity index 100% rename from devlog/_plan/260827_kiro_subagent_delegation_unblock/040_exec_catalog_priority.md rename to devlog/_fin/260827_kiro_subagent_delegation_unblock/040_exec_catalog_priority.md diff --git a/devlog/_plan/260827_kiro_subagent_delegation_unblock/041_wp2_audit_synthesis.md b/devlog/_fin/260827_kiro_subagent_delegation_unblock/041_wp2_audit_synthesis.md similarity index 100% rename from devlog/_plan/260827_kiro_subagent_delegation_unblock/041_wp2_audit_synthesis.md rename to devlog/_fin/260827_kiro_subagent_delegation_unblock/041_wp2_audit_synthesis.md diff --git a/devlog/_plan/260827_kiro_subagent_delegation_unblock/042_wp2_check_evidence.md b/devlog/_fin/260827_kiro_subagent_delegation_unblock/042_wp2_check_evidence.md similarity index 100% rename from devlog/_plan/260827_kiro_subagent_delegation_unblock/042_wp2_check_evidence.md rename to devlog/_fin/260827_kiro_subagent_delegation_unblock/042_wp2_check_evidence.md diff --git a/devlog/_plan/260827_kiro_subagent_delegation_unblock/050_ship.md b/devlog/_fin/260827_kiro_subagent_delegation_unblock/050_ship.md similarity index 100% rename from devlog/_plan/260827_kiro_subagent_delegation_unblock/050_ship.md rename to devlog/_fin/260827_kiro_subagent_delegation_unblock/050_ship.md diff --git a/devlog/_plan/260827_kiro_subagent_delegation_unblock/051_ci_shard4_triage.md b/devlog/_fin/260827_kiro_subagent_delegation_unblock/051_ci_shard4_triage.md similarity index 100% rename from devlog/_plan/260827_kiro_subagent_delegation_unblock/051_ci_shard4_triage.md rename to devlog/_fin/260827_kiro_subagent_delegation_unblock/051_ci_shard4_triage.md diff --git a/devlog/_plan/260827_release_train/000_state_and_decisions.md b/devlog/_fin/260827_release_train/000_state_and_decisions.md similarity index 100% rename from devlog/_plan/260827_release_train/000_state_and_decisions.md rename to devlog/_fin/260827_release_train/000_state_and_decisions.md diff --git a/devlog/_plan/260827_release_train/010_preview_promote.md b/devlog/_fin/260827_release_train/010_preview_promote.md similarity index 100% rename from devlog/_plan/260827_release_train/010_preview_promote.md rename to devlog/_fin/260827_release_train/010_preview_promote.md diff --git a/devlog/_plan/260827_release_train/020_preview_release.md b/devlog/_fin/260827_release_train/020_preview_release.md similarity index 100% rename from devlog/_plan/260827_release_train/020_preview_release.md rename to devlog/_fin/260827_release_train/020_preview_release.md diff --git a/devlog/_plan/260827_release_train/030_main_promote.md b/devlog/_fin/260827_release_train/030_main_promote.md similarity index 100% rename from devlog/_plan/260827_release_train/030_main_promote.md rename to devlog/_fin/260827_release_train/030_main_promote.md diff --git a/devlog/_plan/260827_release_train/040_stable_release.md b/devlog/_fin/260827_release_train/040_stable_release.md similarity index 100% rename from devlog/_plan/260827_release_train/040_stable_release.md rename to devlog/_fin/260827_release_train/040_stable_release.md diff --git a/devlog/_plan/260827_release_train/050_deploy_and_verify.md b/devlog/_fin/260827_release_train/050_deploy_and_verify.md similarity index 100% rename from devlog/_plan/260827_release_train/050_deploy_and_verify.md rename to devlog/_fin/260827_release_train/050_deploy_and_verify.md diff --git a/devlog/_plan/260827_remote_hub/000_research.md b/devlog/_fin/260827_remote_hub/000_research.md similarity index 100% rename from devlog/_plan/260827_remote_hub/000_research.md rename to devlog/_fin/260827_remote_hub/000_research.md diff --git a/devlog/_plan/260827_remote_hub/001_interview.md b/devlog/_fin/260827_remote_hub/001_interview.md similarity index 100% rename from devlog/_plan/260827_remote_hub/001_interview.md rename to devlog/_fin/260827_remote_hub/001_interview.md diff --git a/devlog/_plan/260827_remote_hub/002_audit_r1_synthesis.md b/devlog/_fin/260827_remote_hub/002_audit_r1_synthesis.md similarity index 100% rename from devlog/_plan/260827_remote_hub/002_audit_r1_synthesis.md rename to devlog/_fin/260827_remote_hub/002_audit_r1_synthesis.md diff --git a/devlog/_plan/260827_remote_hub/003_audit_r2_synthesis.md b/devlog/_fin/260827_remote_hub/003_audit_r2_synthesis.md similarity index 100% rename from devlog/_plan/260827_remote_hub/003_audit_r2_synthesis.md rename to devlog/_fin/260827_remote_hub/003_audit_r2_synthesis.md diff --git a/devlog/_plan/260827_remote_hub/010_design.md b/devlog/_fin/260827_remote_hub/010_design.md similarity index 100% rename from devlog/_plan/260827_remote_hub/010_design.md rename to devlog/_fin/260827_remote_hub/010_design.md diff --git a/devlog/_plan/260827_remote_hub/020_roadmap.md b/devlog/_fin/260827_remote_hub/020_roadmap.md similarity index 100% rename from devlog/_plan/260827_remote_hub/020_roadmap.md rename to devlog/_fin/260827_remote_hub/020_roadmap.md diff --git a/devlog/_plan/260827_remote_hub/030_phase1_protocol_catalog.md b/devlog/_fin/260827_remote_hub/030_phase1_protocol_catalog.md similarity index 100% rename from devlog/_plan/260827_remote_hub/030_phase1_protocol_catalog.md rename to devlog/_fin/260827_remote_hub/030_phase1_protocol_catalog.md diff --git a/devlog/_plan/260827_remote_hub/040_phase2_remote_session.md b/devlog/_fin/260827_remote_hub/040_phase2_remote_session.md similarity index 100% rename from devlog/_plan/260827_remote_hub/040_phase2_remote_session.md rename to devlog/_fin/260827_remote_hub/040_phase2_remote_session.md diff --git a/devlog/_plan/260827_remote_hub/050_phase3_connect.md b/devlog/_fin/260827_remote_hub/050_phase3_connect.md similarity index 100% rename from devlog/_plan/260827_remote_hub/050_phase3_connect.md rename to devlog/_fin/260827_remote_hub/050_phase3_connect.md diff --git a/devlog/_plan/260827_remote_hub/060_phase4_two_plane.md b/devlog/_fin/260827_remote_hub/060_phase4_two_plane.md similarity index 100% rename from devlog/_plan/260827_remote_hub/060_phase4_two_plane.md rename to devlog/_fin/260827_remote_hub/060_phase4_two_plane.md diff --git a/devlog/_plan/260827_remote_hub/070_phase5_deploy.md b/devlog/_fin/260827_remote_hub/070_phase5_deploy.md similarity index 100% rename from devlog/_plan/260827_remote_hub/070_phase5_deploy.md rename to devlog/_fin/260827_remote_hub/070_phase5_deploy.md diff --git a/devlog/_plan/260827_remote_hub/080_phase6_hardening.md b/devlog/_fin/260827_remote_hub/080_phase6_hardening.md similarity index 100% rename from devlog/_plan/260827_remote_hub/080_phase6_hardening.md rename to devlog/_fin/260827_remote_hub/080_phase6_hardening.md diff --git a/devlog/_plan/260827_remote_hub/090_dogfood_record.md b/devlog/_fin/260827_remote_hub/090_dogfood_record.md similarity index 100% rename from devlog/_plan/260827_remote_hub/090_dogfood_record.md rename to devlog/_fin/260827_remote_hub/090_dogfood_record.md diff --git a/devlog/_fin/260827_remote_hub/090_terminal.md b/devlog/_fin/260827_remote_hub/090_terminal.md new file mode 100644 index 0000000000..1547db2ff2 --- /dev/null +++ b/devlog/_fin/260827_remote_hub/090_terminal.md @@ -0,0 +1,3 @@ +# terminal outcome — abandoned + +ABANDONED: superseded by 260901_remote_hub_restack, which landed the restacked hub (#2771-#2789, #3147). diff --git a/devlog/_plan/260827_remote_hub/assets/gui-p4-dashboard.png b/devlog/_fin/260827_remote_hub/assets/gui-p4-dashboard.png similarity index 100% rename from devlog/_plan/260827_remote_hub/assets/gui-p4-dashboard.png rename to devlog/_fin/260827_remote_hub/assets/gui-p4-dashboard.png diff --git a/devlog/_plan/260828_bugpr_fix_and_reimplement/000_plan.md b/devlog/_fin/260828_bugpr_fix_and_reimplement/000_plan.md similarity index 100% rename from devlog/_plan/260828_bugpr_fix_and_reimplement/000_plan.md rename to devlog/_fin/260828_bugpr_fix_and_reimplement/000_plan.md diff --git a/devlog/_plan/260828_bugpr_fix_and_reimplement/010_phase1.md b/devlog/_fin/260828_bugpr_fix_and_reimplement/010_phase1.md similarity index 100% rename from devlog/_plan/260828_bugpr_fix_and_reimplement/010_phase1.md rename to devlog/_fin/260828_bugpr_fix_and_reimplement/010_phase1.md diff --git a/devlog/_plan/260828_bugpr_fix_and_reimplement/020_phase2.md b/devlog/_fin/260828_bugpr_fix_and_reimplement/020_phase2.md similarity index 100% rename from devlog/_plan/260828_bugpr_fix_and_reimplement/020_phase2.md rename to devlog/_fin/260828_bugpr_fix_and_reimplement/020_phase2.md diff --git a/devlog/_plan/260828_bugpr_fix_and_reimplement/021_wp2_wp3_outcome.md b/devlog/_fin/260828_bugpr_fix_and_reimplement/021_wp2_wp3_outcome.md similarity index 100% rename from devlog/_plan/260828_bugpr_fix_and_reimplement/021_wp2_wp3_outcome.md rename to devlog/_fin/260828_bugpr_fix_and_reimplement/021_wp2_wp3_outcome.md diff --git a/devlog/_plan/260828_bugpr_fix_and_reimplement/030_phase3.md b/devlog/_fin/260828_bugpr_fix_and_reimplement/030_phase3.md similarity index 100% rename from devlog/_plan/260828_bugpr_fix_and_reimplement/030_phase3.md rename to devlog/_fin/260828_bugpr_fix_and_reimplement/030_phase3.md diff --git a/devlog/_plan/260828_bugpr_fix_and_reimplement/040_phase4.md b/devlog/_fin/260828_bugpr_fix_and_reimplement/040_phase4.md similarity index 100% rename from devlog/_plan/260828_bugpr_fix_and_reimplement/040_phase4.md rename to devlog/_fin/260828_bugpr_fix_and_reimplement/040_phase4.md diff --git a/devlog/_plan/260828_bugpr_fix_and_reimplement/050_phase5.md b/devlog/_fin/260828_bugpr_fix_and_reimplement/050_phase5.md similarity index 100% rename from devlog/_plan/260828_bugpr_fix_and_reimplement/050_phase5.md rename to devlog/_fin/260828_bugpr_fix_and_reimplement/050_phase5.md diff --git a/devlog/_plan/260828_bugpr_fix_and_reimplement/060_phase6.md b/devlog/_fin/260828_bugpr_fix_and_reimplement/060_phase6.md similarity index 100% rename from devlog/_plan/260828_bugpr_fix_and_reimplement/060_phase6.md rename to devlog/_fin/260828_bugpr_fix_and_reimplement/060_phase6.md diff --git a/devlog/_plan/260828_bugpr_fix_and_reimplement/070_outcome.md b/devlog/_fin/260828_bugpr_fix_and_reimplement/070_outcome.md similarity index 100% rename from devlog/_plan/260828_bugpr_fix_and_reimplement/070_outcome.md rename to devlog/_fin/260828_bugpr_fix_and_reimplement/070_outcome.md diff --git a/devlog/_plan/260828_cursor_ndjson_backlog_train/000_plan.md b/devlog/_fin/260828_cursor_ndjson_backlog_train/000_plan.md similarity index 100% rename from devlog/_plan/260828_cursor_ndjson_backlog_train/000_plan.md rename to devlog/_fin/260828_cursor_ndjson_backlog_train/000_plan.md diff --git a/devlog/_plan/260828_cursor_ndjson_backlog_train/001_backlog_abort_rca.md b/devlog/_fin/260828_cursor_ndjson_backlog_train/001_backlog_abort_rca.md similarity index 100% rename from devlog/_plan/260828_cursor_ndjson_backlog_train/001_backlog_abort_rca.md rename to devlog/_fin/260828_cursor_ndjson_backlog_train/001_backlog_abort_rca.md diff --git a/devlog/_plan/260828_cursor_ndjson_backlog_train/002_cursor_open_defect_inventory.md b/devlog/_fin/260828_cursor_ndjson_backlog_train/002_cursor_open_defect_inventory.md similarity index 100% rename from devlog/_plan/260828_cursor_ndjson_backlog_train/002_cursor_open_defect_inventory.md rename to devlog/_fin/260828_cursor_ndjson_backlog_train/002_cursor_open_defect_inventory.md diff --git a/devlog/_plan/260828_cursor_ndjson_backlog_train/003_roadmap_lock.md b/devlog/_fin/260828_cursor_ndjson_backlog_train/003_roadmap_lock.md similarity index 100% rename from devlog/_plan/260828_cursor_ndjson_backlog_train/003_roadmap_lock.md rename to devlog/_fin/260828_cursor_ndjson_backlog_train/003_roadmap_lock.md diff --git a/devlog/_plan/260828_cursor_ndjson_backlog_train/010_backlog_abort_fix.md b/devlog/_fin/260828_cursor_ndjson_backlog_train/010_backlog_abort_fix.md similarity index 100% rename from devlog/_plan/260828_cursor_ndjson_backlog_train/010_backlog_abort_fix.md rename to devlog/_fin/260828_cursor_ndjson_backlog_train/010_backlog_abort_fix.md diff --git a/devlog/_plan/260828_cursor_ndjson_backlog_train/020_macmini_probe_round.md b/devlog/_fin/260828_cursor_ndjson_backlog_train/020_macmini_probe_round.md similarity index 100% rename from devlog/_plan/260828_cursor_ndjson_backlog_train/020_macmini_probe_round.md rename to devlog/_fin/260828_cursor_ndjson_backlog_train/020_macmini_probe_round.md diff --git a/devlog/_plan/260828_cursor_ndjson_backlog_train/021_probe_results_round1.md b/devlog/_fin/260828_cursor_ndjson_backlog_train/021_probe_results_round1.md similarity index 100% rename from devlog/_plan/260828_cursor_ndjson_backlog_train/021_probe_results_round1.md rename to devlog/_fin/260828_cursor_ndjson_backlog_train/021_probe_results_round1.md diff --git a/devlog/_plan/260828_cursor_ndjson_backlog_train/030_cursor_fixes.md b/devlog/_fin/260828_cursor_ndjson_backlog_train/030_cursor_fixes.md similarity index 100% rename from devlog/_plan/260828_cursor_ndjson_backlog_train/030_cursor_fixes.md rename to devlog/_fin/260828_cursor_ndjson_backlog_train/030_cursor_fixes.md diff --git a/devlog/_plan/260828_cursor_ndjson_backlog_train/031_midstream_echo_fix.md b/devlog/_fin/260828_cursor_ndjson_backlog_train/031_midstream_echo_fix.md similarity index 100% rename from devlog/_plan/260828_cursor_ndjson_backlog_train/031_midstream_echo_fix.md rename to devlog/_fin/260828_cursor_ndjson_backlog_train/031_midstream_echo_fix.md diff --git a/devlog/_plan/260828_cursor_ndjson_backlog_train/040_closure_round.md b/devlog/_fin/260828_cursor_ndjson_backlog_train/040_closure_round.md similarity index 100% rename from devlog/_plan/260828_cursor_ndjson_backlog_train/040_closure_round.md rename to devlog/_fin/260828_cursor_ndjson_backlog_train/040_closure_round.md diff --git a/devlog/_plan/260828_cursor_ndjson_backlog_train/050_merge_train.md b/devlog/_fin/260828_cursor_ndjson_backlog_train/050_merge_train.md similarity index 100% rename from devlog/_plan/260828_cursor_ndjson_backlog_train/050_merge_train.md rename to devlog/_fin/260828_cursor_ndjson_backlog_train/050_merge_train.md diff --git a/devlog/_plan/260828_cursor_umbrella_catalog/000_plan.md b/devlog/_fin/260828_cursor_umbrella_catalog/000_plan.md similarity index 100% rename from devlog/_plan/260828_cursor_umbrella_catalog/000_plan.md rename to devlog/_fin/260828_cursor_umbrella_catalog/000_plan.md diff --git a/devlog/_plan/260828_cursor_umbrella_catalog/001_reference_analysis.md b/devlog/_fin/260828_cursor_umbrella_catalog/001_reference_analysis.md similarity index 100% rename from devlog/_plan/260828_cursor_umbrella_catalog/001_reference_analysis.md rename to devlog/_fin/260828_cursor_umbrella_catalog/001_reference_analysis.md diff --git a/devlog/_plan/260828_cursor_umbrella_catalog/002_current_surface.md b/devlog/_fin/260828_cursor_umbrella_catalog/002_current_surface.md similarity index 100% rename from devlog/_plan/260828_cursor_umbrella_catalog/002_current_surface.md rename to devlog/_fin/260828_cursor_umbrella_catalog/002_current_surface.md diff --git a/devlog/_plan/260828_cursor_umbrella_catalog/003_design.md b/devlog/_fin/260828_cursor_umbrella_catalog/003_design.md similarity index 100% rename from devlog/_plan/260828_cursor_umbrella_catalog/003_design.md rename to devlog/_fin/260828_cursor_umbrella_catalog/003_design.md diff --git a/devlog/_plan/260828_cursor_umbrella_catalog/004_roadmap_lock.md b/devlog/_fin/260828_cursor_umbrella_catalog/004_roadmap_lock.md similarity index 100% rename from devlog/_plan/260828_cursor_umbrella_catalog/004_roadmap_lock.md rename to devlog/_fin/260828_cursor_umbrella_catalog/004_roadmap_lock.md diff --git a/devlog/_plan/260828_cursor_umbrella_catalog/010_capability_core.md b/devlog/_fin/260828_cursor_umbrella_catalog/010_capability_core.md similarity index 100% rename from devlog/_plan/260828_cursor_umbrella_catalog/010_capability_core.md rename to devlog/_fin/260828_cursor_umbrella_catalog/010_capability_core.md diff --git a/devlog/_plan/260828_cursor_umbrella_catalog/020_catalog_integration.md b/devlog/_fin/260828_cursor_umbrella_catalog/020_catalog_integration.md similarity index 100% rename from devlog/_plan/260828_cursor_umbrella_catalog/020_catalog_integration.md rename to devlog/_fin/260828_cursor_umbrella_catalog/020_catalog_integration.md diff --git a/devlog/_plan/260828_cursor_umbrella_catalog/030_closure.md b/devlog/_fin/260828_cursor_umbrella_catalog/030_closure.md similarity index 100% rename from devlog/_plan/260828_cursor_umbrella_catalog/030_closure.md rename to devlog/_fin/260828_cursor_umbrella_catalog/030_closure.md diff --git a/devlog/_plan/260828_quota_reset_detection/000_plan.md b/devlog/_fin/260828_quota_reset_detection/000_plan.md similarity index 100% rename from devlog/_plan/260828_quota_reset_detection/000_plan.md rename to devlog/_fin/260828_quota_reset_detection/000_plan.md diff --git a/devlog/_plan/260828_quota_reset_detection/001_audit_response.md b/devlog/_fin/260828_quota_reset_detection/001_audit_response.md similarity index 100% rename from devlog/_plan/260828_quota_reset_detection/001_audit_response.md rename to devlog/_fin/260828_quota_reset_detection/001_audit_response.md diff --git a/devlog/_plan/260828_quota_reset_detection/002_wp2_audit_response.md b/devlog/_fin/260828_quota_reset_detection/002_wp2_audit_response.md similarity index 100% rename from devlog/_plan/260828_quota_reset_detection/002_wp2_audit_response.md rename to devlog/_fin/260828_quota_reset_detection/002_wp2_audit_response.md diff --git a/devlog/_plan/260828_quota_reset_detection/003_wp3_audit_response.md b/devlog/_fin/260828_quota_reset_detection/003_wp3_audit_response.md similarity index 100% rename from devlog/_plan/260828_quota_reset_detection/003_wp3_audit_response.md rename to devlog/_fin/260828_quota_reset_detection/003_wp3_audit_response.md diff --git a/devlog/_plan/260828_quota_reset_detection/004_wp3_review_response.md b/devlog/_fin/260828_quota_reset_detection/004_wp3_review_response.md similarity index 100% rename from devlog/_plan/260828_quota_reset_detection/004_wp3_review_response.md rename to devlog/_fin/260828_quota_reset_detection/004_wp3_review_response.md diff --git a/devlog/_plan/260828_quota_reset_detection/010_phase2_detection_core.md b/devlog/_fin/260828_quota_reset_detection/010_phase2_detection_core.md similarity index 100% rename from devlog/_plan/260828_quota_reset_detection/010_phase2_detection_core.md rename to devlog/_fin/260828_quota_reset_detection/010_phase2_detection_core.md diff --git a/devlog/_plan/260828_quota_reset_detection/020_phase3_observation_wiring.md b/devlog/_fin/260828_quota_reset_detection/020_phase3_observation_wiring.md similarity index 100% rename from devlog/_plan/260828_quota_reset_detection/020_phase3_observation_wiring.md rename to devlog/_fin/260828_quota_reset_detection/020_phase3_observation_wiring.md diff --git a/devlog/_plan/260828_quota_reset_detection/030_phase4_sinks_and_surface.md b/devlog/_fin/260828_quota_reset_detection/030_phase4_sinks_and_surface.md similarity index 100% rename from devlog/_plan/260828_quota_reset_detection/030_phase4_sinks_and_surface.md rename to devlog/_fin/260828_quota_reset_detection/030_phase4_sinks_and_surface.md diff --git a/devlog/_plan/260828_quota_reset_detection/040_phase5_hardening_delivery.md b/devlog/_fin/260828_quota_reset_detection/040_phase5_hardening_delivery.md similarity index 100% rename from devlog/_plan/260828_quota_reset_detection/040_phase5_hardening_delivery.md rename to devlog/_fin/260828_quota_reset_detection/040_phase5_hardening_delivery.md diff --git a/devlog/_plan/260828_quota_reset_detection/050_activation_evidence.md b/devlog/_fin/260828_quota_reset_detection/050_activation_evidence.md similarity index 100% rename from devlog/_plan/260828_quota_reset_detection/050_activation_evidence.md rename to devlog/_fin/260828_quota_reset_detection/050_activation_evidence.md diff --git a/devlog/_plan/260828_quota_reset_detection/060_closeout.md b/devlog/_fin/260828_quota_reset_detection/060_closeout.md similarity index 100% rename from devlog/_plan/260828_quota_reset_detection/060_closeout.md rename to devlog/_fin/260828_quota_reset_detection/060_closeout.md diff --git a/devlog/_plan/260829_bugpr_lane_h_residual_issues/120_issue_1527_replay_envelope.md b/devlog/_fin/260829_bugpr_lane_h_residual_issues/120_issue_1527_replay_envelope.md similarity index 100% rename from devlog/_plan/260829_bugpr_lane_h_residual_issues/120_issue_1527_replay_envelope.md rename to devlog/_fin/260829_bugpr_lane_h_residual_issues/120_issue_1527_replay_envelope.md diff --git a/devlog/_plan/260829_bugpr_lane_h_residual_issues/130_pr2872_probe_fingerprint.md b/devlog/_fin/260829_bugpr_lane_h_residual_issues/130_pr2872_probe_fingerprint.md similarity index 100% rename from devlog/_plan/260829_bugpr_lane_h_residual_issues/130_pr2872_probe_fingerprint.md rename to devlog/_fin/260829_bugpr_lane_h_residual_issues/130_pr2872_probe_fingerprint.md diff --git a/devlog/_plan/260829_bugpr_lane_h_residual_issues/140_pr2884_shim_backup_matcher.md b/devlog/_fin/260829_bugpr_lane_h_residual_issues/140_pr2884_shim_backup_matcher.md similarity index 100% rename from devlog/_plan/260829_bugpr_lane_h_residual_issues/140_pr2884_shim_backup_matcher.md rename to devlog/_fin/260829_bugpr_lane_h_residual_issues/140_pr2884_shim_backup_matcher.md diff --git a/devlog/_plan/260829_bugpr_lane_h_residual_issues/150_issue_2887_pool_401_refresh.md b/devlog/_fin/260829_bugpr_lane_h_residual_issues/150_issue_2887_pool_401_refresh.md similarity index 100% rename from devlog/_plan/260829_bugpr_lane_h_residual_issues/150_issue_2887_pool_401_refresh.md rename to devlog/_fin/260829_bugpr_lane_h_residual_issues/150_issue_2887_pool_401_refresh.md diff --git a/devlog/_plan/260829_bugpr_lane_h_residual_issues/160_issue_2886_entitlement_client_version.md b/devlog/_fin/260829_bugpr_lane_h_residual_issues/160_issue_2886_entitlement_client_version.md similarity index 100% rename from devlog/_plan/260829_bugpr_lane_h_residual_issues/160_issue_2886_entitlement_client_version.md rename to devlog/_fin/260829_bugpr_lane_h_residual_issues/160_issue_2886_entitlement_client_version.md diff --git a/devlog/_plan/260829_bugpr_lane_h_residual_issues/170_pr2895_pool_401_recovery_budget.md b/devlog/_fin/260829_bugpr_lane_h_residual_issues/170_pr2895_pool_401_recovery_budget.md similarity index 100% rename from devlog/_plan/260829_bugpr_lane_h_residual_issues/170_pr2895_pool_401_recovery_budget.md rename to devlog/_fin/260829_bugpr_lane_h_residual_issues/170_pr2895_pool_401_recovery_budget.md diff --git a/devlog/_plan/260829_green_pr_merge_train/000_plan.md b/devlog/_fin/260829_green_pr_merge_train/000_plan.md similarity index 100% rename from devlog/_plan/260829_green_pr_merge_train/000_plan.md rename to devlog/_fin/260829_green_pr_merge_train/000_plan.md diff --git a/devlog/_plan/260829_green_pr_merge_train/010_wp1_2429_privacy_scan.md b/devlog/_fin/260829_green_pr_merge_train/010_wp1_2429_privacy_scan.md similarity index 100% rename from devlog/_plan/260829_green_pr_merge_train/010_wp1_2429_privacy_scan.md rename to devlog/_fin/260829_green_pr_merge_train/010_wp1_2429_privacy_scan.md diff --git a/devlog/_plan/260829_green_pr_merge_train/020_wp2_2827_expose_header.md b/devlog/_fin/260829_green_pr_merge_train/020_wp2_2827_expose_header.md similarity index 100% rename from devlog/_plan/260829_green_pr_merge_train/020_wp2_2827_expose_header.md rename to devlog/_fin/260829_green_pr_merge_train/020_wp2_2827_expose_header.md diff --git a/devlog/_plan/260829_green_pr_merge_train/090_closeout.md b/devlog/_fin/260829_green_pr_merge_train/090_closeout.md similarity index 100% rename from devlog/_plan/260829_green_pr_merge_train/090_closeout.md rename to devlog/_fin/260829_green_pr_merge_train/090_closeout.md diff --git a/devlog/_plan/260829_gui_dashboard_slop/000_baseline_and_roadmap.md b/devlog/_fin/260829_gui_dashboard_slop/000_baseline_and_roadmap.md similarity index 100% rename from devlog/_plan/260829_gui_dashboard_slop/000_baseline_and_roadmap.md rename to devlog/_fin/260829_gui_dashboard_slop/000_baseline_and_roadmap.md diff --git a/devlog/_plan/260829_gui_dashboard_slop/010_sidecar_pair_alignment.md b/devlog/_fin/260829_gui_dashboard_slop/010_sidecar_pair_alignment.md similarity index 100% rename from devlog/_plan/260829_gui_dashboard_slop/010_sidecar_pair_alignment.md rename to devlog/_fin/260829_gui_dashboard_slop/010_sidecar_pair_alignment.md diff --git a/devlog/_plan/260829_gui_dashboard_slop/011_audit_correction_align_content.md b/devlog/_fin/260829_gui_dashboard_slop/011_audit_correction_align_content.md similarity index 100% rename from devlog/_plan/260829_gui_dashboard_slop/011_audit_correction_align_content.md rename to devlog/_fin/260829_gui_dashboard_slop/011_audit_correction_align_content.md diff --git a/devlog/_plan/260829_gui_dashboard_slop/012_shipped_fix_and_subgrid_postmortem.md b/devlog/_fin/260829_gui_dashboard_slop/012_shipped_fix_and_subgrid_postmortem.md similarity index 100% rename from devlog/_plan/260829_gui_dashboard_slop/012_shipped_fix_and_subgrid_postmortem.md rename to devlog/_fin/260829_gui_dashboard_slop/012_shipped_fix_and_subgrid_postmortem.md diff --git a/devlog/_plan/260829_gui_dashboard_slop/013_final_shipped_and_measurement_lessons.md b/devlog/_fin/260829_gui_dashboard_slop/013_final_shipped_and_measurement_lessons.md similarity index 100% rename from devlog/_plan/260829_gui_dashboard_slop/013_final_shipped_and_measurement_lessons.md rename to devlog/_fin/260829_gui_dashboard_slop/013_final_shipped_and_measurement_lessons.md diff --git a/devlog/_plan/260829_gui_dashboard_slop/020_phantom_grid_track.md b/devlog/_fin/260829_gui_dashboard_slop/020_phantom_grid_track.md similarity index 100% rename from devlog/_plan/260829_gui_dashboard_slop/020_phantom_grid_track.md rename to devlog/_fin/260829_gui_dashboard_slop/020_phantom_grid_track.md diff --git a/devlog/_plan/260829_gui_dashboard_slop/030_dynamic_viewport_units.md b/devlog/_fin/260829_gui_dashboard_slop/030_dynamic_viewport_units.md similarity index 100% rename from devlog/_plan/260829_gui_dashboard_slop/030_dynamic_viewport_units.md rename to devlog/_fin/260829_gui_dashboard_slop/030_dynamic_viewport_units.md diff --git a/devlog/_plan/260829_lane_n_effort_ladder_and_locale_probe/000_units.md b/devlog/_fin/260829_lane_n_effort_ladder_and_locale_probe/000_units.md similarity index 100% rename from devlog/_plan/260829_lane_n_effort_ladder_and_locale_probe/000_units.md rename to devlog/_fin/260829_lane_n_effort_ladder_and_locale_probe/000_units.md diff --git a/devlog/_plan/260830_dev_version_line_bump_pr/000_cause_and_roadmap.md b/devlog/_fin/260830_dev_version_line_bump_pr/000_cause_and_roadmap.md similarity index 100% rename from devlog/_plan/260830_dev_version_line_bump_pr/000_cause_and_roadmap.md rename to devlog/_fin/260830_dev_version_line_bump_pr/000_cause_and_roadmap.md diff --git a/devlog/_plan/260830_dev_version_line_bump_pr/010_version_repair.md b/devlog/_fin/260830_dev_version_line_bump_pr/010_version_repair.md similarity index 100% rename from devlog/_plan/260830_dev_version_line_bump_pr/010_version_repair.md rename to devlog/_fin/260830_dev_version_line_bump_pr/010_version_repair.md diff --git a/devlog/_plan/260830_dev_version_line_bump_pr/020_post_release_bump.md b/devlog/_fin/260830_dev_version_line_bump_pr/020_post_release_bump.md similarity index 100% rename from devlog/_plan/260830_dev_version_line_bump_pr/020_post_release_bump.md rename to devlog/_fin/260830_dev_version_line_bump_pr/020_post_release_bump.md diff --git a/devlog/_plan/260830_dev_version_line_bump_pr/030_ship.md b/devlog/_fin/260830_dev_version_line_bump_pr/030_ship.md similarity index 100% rename from devlog/_plan/260830_dev_version_line_bump_pr/030_ship.md rename to devlog/_fin/260830_dev_version_line_bump_pr/030_ship.md diff --git a/devlog/_plan/260830_lane_o_reset_recovery_parity/000_units.md b/devlog/_fin/260830_lane_o_reset_recovery_parity/000_units.md similarity index 100% rename from devlog/_plan/260830_lane_o_reset_recovery_parity/000_units.md rename to devlog/_fin/260830_lane_o_reset_recovery_parity/000_units.md diff --git a/devlog/_plan/260830_lane_q_2892_gaps_3_4/000_units.md b/devlog/_fin/260830_lane_q_2892_gaps_3_4/000_units.md similarity index 100% rename from devlog/_plan/260830_lane_q_2892_gaps_3_4/000_units.md rename to devlog/_fin/260830_lane_q_2892_gaps_3_4/000_units.md diff --git a/devlog/_plan/260830_lane_r_2941_copilot_vision/000_units.md b/devlog/_fin/260830_lane_r_2941_copilot_vision/000_units.md similarity index 100% rename from devlog/_plan/260830_lane_r_2941_copilot_vision/000_units.md rename to devlog/_fin/260830_lane_r_2941_copilot_vision/000_units.md diff --git a/devlog/_plan/260830_release_readiness_train/000_plan.md b/devlog/_fin/260830_release_readiness_train/000_plan.md similarity index 100% rename from devlog/_plan/260830_release_readiness_train/000_plan.md rename to devlog/_fin/260830_release_readiness_train/000_plan.md diff --git a/devlog/_plan/260830_release_readiness_train/010_wp1_runner_and_hygiene.md b/devlog/_fin/260830_release_readiness_train/010_wp1_runner_and_hygiene.md similarity index 100% rename from devlog/_plan/260830_release_readiness_train/010_wp1_runner_and_hygiene.md rename to devlog/_fin/260830_release_readiness_train/010_wp1_runner_and_hygiene.md diff --git a/devlog/_plan/260830_release_readiness_train/020_wp2_quota_expiry.md b/devlog/_fin/260830_release_readiness_train/020_wp2_quota_expiry.md similarity index 100% rename from devlog/_plan/260830_release_readiness_train/020_wp2_quota_expiry.md rename to devlog/_fin/260830_release_readiness_train/020_wp2_quota_expiry.md diff --git a/devlog/_plan/260830_release_readiness_train/030_wp3_responses_and_config.md b/devlog/_fin/260830_release_readiness_train/030_wp3_responses_and_config.md similarity index 100% rename from devlog/_plan/260830_release_readiness_train/030_wp3_responses_and_config.md rename to devlog/_fin/260830_release_readiness_train/030_wp3_responses_and_config.md diff --git a/devlog/_plan/260830_release_readiness_train/040_wp4_issue_2899_antigravity.md b/devlog/_fin/260830_release_readiness_train/040_wp4_issue_2899_antigravity.md similarity index 100% rename from devlog/_plan/260830_release_readiness_train/040_wp4_issue_2899_antigravity.md rename to devlog/_fin/260830_release_readiness_train/040_wp4_issue_2899_antigravity.md diff --git a/devlog/_plan/260830_release_readiness_train/050_wp5_issue_1298_acl_proof.md b/devlog/_fin/260830_release_readiness_train/050_wp5_issue_1298_acl_proof.md similarity index 100% rename from devlog/_plan/260830_release_readiness_train/050_wp5_issue_1298_acl_proof.md rename to devlog/_fin/260830_release_readiness_train/050_wp5_issue_1298_acl_proof.md diff --git a/devlog/_plan/260830_release_readiness_train/060_wp6_release_gates.md b/devlog/_fin/260830_release_readiness_train/060_wp6_release_gates.md similarity index 100% rename from devlog/_plan/260830_release_readiness_train/060_wp6_release_gates.md rename to devlog/_fin/260830_release_readiness_train/060_wp6_release_gates.md diff --git a/devlog/_plan/260830_release_readiness_train/070_outcome.md b/devlog/_fin/260830_release_readiness_train/070_outcome.md similarity index 100% rename from devlog/_plan/260830_release_readiness_train/070_outcome.md rename to devlog/_fin/260830_release_readiness_train/070_outcome.md diff --git a/devlog/_plan/260830_sidecar_control_band/010_shared_control_band.md b/devlog/_fin/260830_sidecar_control_band/010_shared_control_band.md similarity index 100% rename from devlog/_plan/260830_sidecar_control_band/010_shared_control_band.md rename to devlog/_fin/260830_sidecar_control_band/010_shared_control_band.md diff --git a/devlog/_plan/260830_sidecar_control_band/evidence/010-after-ko-1024.png b/devlog/_fin/260830_sidecar_control_band/evidence/010-after-ko-1024.png similarity index 100% rename from devlog/_plan/260830_sidecar_control_band/evidence/010-after-ko-1024.png rename to devlog/_fin/260830_sidecar_control_band/evidence/010-after-ko-1024.png diff --git a/devlog/_plan/260830_sidecar_control_band/evidence/010-after-ko-1440.png b/devlog/_fin/260830_sidecar_control_band/evidence/010-after-ko-1440.png similarity index 100% rename from devlog/_plan/260830_sidecar_control_band/evidence/010-after-ko-1440.png rename to devlog/_fin/260830_sidecar_control_band/evidence/010-after-ko-1440.png diff --git a/devlog/_plan/260830_sidecar_control_band/evidence/010-before-ko-1024.png b/devlog/_fin/260830_sidecar_control_band/evidence/010-before-ko-1024.png similarity index 100% rename from devlog/_plan/260830_sidecar_control_band/evidence/010-before-ko-1024.png rename to devlog/_fin/260830_sidecar_control_band/evidence/010-before-ko-1024.png diff --git a/devlog/_plan/260830_sidecar_control_band/evidence/010-before-ko-1440.png b/devlog/_fin/260830_sidecar_control_band/evidence/010-before-ko-1440.png similarity index 100% rename from devlog/_plan/260830_sidecar_control_band/evidence/010-before-ko-1440.png rename to devlog/_fin/260830_sidecar_control_band/evidence/010-before-ko-1440.png diff --git a/devlog/_plan/260830_sidecar_control_band/evidence/020-after-en-1600.png b/devlog/_fin/260830_sidecar_control_band/evidence/020-after-en-1600.png similarity index 100% rename from devlog/_plan/260830_sidecar_control_band/evidence/020-after-en-1600.png rename to devlog/_fin/260830_sidecar_control_band/evidence/020-after-en-1600.png diff --git a/devlog/_plan/260830_sidecar_control_band/evidence/020-after-ko-1024.png b/devlog/_fin/260830_sidecar_control_band/evidence/020-after-ko-1024.png similarity index 100% rename from devlog/_plan/260830_sidecar_control_band/evidence/020-after-ko-1024.png rename to devlog/_fin/260830_sidecar_control_band/evidence/020-after-ko-1024.png diff --git a/devlog/_plan/260830_sidecar_control_band/evidence/020-after-ko-1440.png b/devlog/_fin/260830_sidecar_control_band/evidence/020-after-ko-1440.png similarity index 100% rename from devlog/_plan/260830_sidecar_control_band/evidence/020-after-ko-1440.png rename to devlog/_fin/260830_sidecar_control_band/evidence/020-after-ko-1440.png diff --git a/devlog/_plan/260830_sidecar_control_band/evidence/020-after-ru-1440.png b/devlog/_fin/260830_sidecar_control_band/evidence/020-after-ru-1440.png similarity index 100% rename from devlog/_plan/260830_sidecar_control_band/evidence/020-after-ru-1440.png rename to devlog/_fin/260830_sidecar_control_band/evidence/020-after-ru-1440.png diff --git a/devlog/_plan/260831_bug_triage_nonprio70/000_roadmap.md b/devlog/_fin/260831_bug_triage_nonprio70/000_roadmap.md similarity index 100% rename from devlog/_plan/260831_bug_triage_nonprio70/000_roadmap.md rename to devlog/_fin/260831_bug_triage_nonprio70/000_roadmap.md diff --git a/devlog/_plan/260831_bug_triage_nonprio70/001_scan_verdicts.md b/devlog/_fin/260831_bug_triage_nonprio70/001_scan_verdicts.md similarity index 100% rename from devlog/_plan/260831_bug_triage_nonprio70/001_scan_verdicts.md rename to devlog/_fin/260831_bug_triage_nonprio70/001_scan_verdicts.md diff --git a/devlog/_plan/260831_bug_triage_nonprio70/002_audit_round1_synthesis.md b/devlog/_fin/260831_bug_triage_nonprio70/002_audit_round1_synthesis.md similarity index 100% rename from devlog/_plan/260831_bug_triage_nonprio70/002_audit_round1_synthesis.md rename to devlog/_fin/260831_bug_triage_nonprio70/002_audit_round1_synthesis.md diff --git a/devlog/_plan/260831_bug_triage_nonprio70/003_audit_round2_synthesis.md b/devlog/_fin/260831_bug_triage_nonprio70/003_audit_round2_synthesis.md similarity index 100% rename from devlog/_plan/260831_bug_triage_nonprio70/003_audit_round2_synthesis.md rename to devlog/_fin/260831_bug_triage_nonprio70/003_audit_round2_synthesis.md diff --git a/devlog/_plan/260831_bug_triage_nonprio70/004_audit_round3_synthesis.md b/devlog/_fin/260831_bug_triage_nonprio70/004_audit_round3_synthesis.md similarity index 100% rename from devlog/_plan/260831_bug_triage_nonprio70/004_audit_round3_synthesis.md rename to devlog/_fin/260831_bug_triage_nonprio70/004_audit_round3_synthesis.md diff --git a/devlog/_plan/260831_bug_triage_nonprio70/070_outcome.md b/devlog/_fin/260831_bug_triage_nonprio70/070_outcome.md similarity index 100% rename from devlog/_plan/260831_bug_triage_nonprio70/070_outcome.md rename to devlog/_fin/260831_bug_triage_nonprio70/070_outcome.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/000_plan.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/000_plan.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/000_plan.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/000_plan.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/001_research_3022_entitlement_floor.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/001_research_3022_entitlement_floor.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/001_research_3022_entitlement_floor.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/001_research_3022_entitlement_floor.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/002_research_3023_roster_ttl.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/002_research_3023_roster_ttl.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/002_research_3023_roster_ttl.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/002_research_3023_roster_ttl.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/003_audit_3018_spill_drain.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/003_audit_3018_spill_drain.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/003_audit_3018_spill_drain.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/003_audit_3018_spill_drain.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/004_audit_round1_synthesis.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/004_audit_round1_synthesis.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/004_audit_round1_synthesis.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/004_audit_round1_synthesis.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/005_audit_round2_synthesis.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/005_audit_round2_synthesis.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/005_audit_round2_synthesis.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/005_audit_round2_synthesis.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/006_audit_round3_synthesis.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/006_audit_round3_synthesis.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/006_audit_round3_synthesis.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/006_audit_round3_synthesis.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/007_audit_round4_wp1_plan.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/007_audit_round4_wp1_plan.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/007_audit_round4_wp1_plan.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/007_audit_round4_wp1_plan.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/008_audit_round5_wp2_plan.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/008_audit_round5_wp2_plan.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/008_audit_round5_wp2_plan.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/008_audit_round5_wp2_plan.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/009_audit_rounds6to8_wp2_and_wp3_review.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/009_audit_rounds6to8_wp2_and_wp3_review.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/009_audit_rounds6to8_wp2_and_wp3_review.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/009_audit_rounds6to8_wp2_and_wp3_review.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/010_wp1_entitlement_floor_and_empty_roster.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/010_wp1_entitlement_floor_and_empty_roster.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/010_wp1_entitlement_floor_and_empty_roster.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/010_wp1_entitlement_floor_and_empty_roster.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/020_wp2_roster_ttl_refresh.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/020_wp2_roster_ttl_refresh.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/020_wp2_roster_ttl_refresh.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/020_wp2_roster_ttl_refresh.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/030_wp3_spill_publication_drain.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/030_wp3_spill_publication_drain.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/030_wp3_spill_publication_drain.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/030_wp3_spill_publication_drain.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/040_wp4_entitlement_diagnostic.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/040_wp4_entitlement_diagnostic.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/040_wp4_entitlement_diagnostic.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/040_wp4_entitlement_diagnostic.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/050_wp5_tristate_entitlement_authority.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/050_wp5_tristate_entitlement_authority.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/050_wp5_tristate_entitlement_authority.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/050_wp5_tristate_entitlement_authority.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/060_wp6_ensure_freshness.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/060_wp6_ensure_freshness.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/060_wp6_ensure_freshness.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/060_wp6_ensure_freshness.md diff --git a/devlog/_plan/260831_prio70_entitlement_and_spill_train/070_outcome.md b/devlog/_fin/260831_prio70_entitlement_and_spill_train/070_outcome.md similarity index 100% rename from devlog/_plan/260831_prio70_entitlement_and_spill_train/070_outcome.md rename to devlog/_fin/260831_prio70_entitlement_and_spill_train/070_outcome.md diff --git a/devlog/_plan/260831_prio70_train_round2/000_plan.md b/devlog/_fin/260831_prio70_train_round2/000_plan.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/000_plan.md rename to devlog/_fin/260831_prio70_train_round2/000_plan.md diff --git a/devlog/_plan/260831_prio70_train_round2/001_research_scan_evidence.md b/devlog/_fin/260831_prio70_train_round2/001_research_scan_evidence.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/001_research_scan_evidence.md rename to devlog/_fin/260831_prio70_train_round2/001_research_scan_evidence.md diff --git a/devlog/_plan/260831_prio70_train_round2/002_audit_round1_synthesis.md b/devlog/_fin/260831_prio70_train_round2/002_audit_round1_synthesis.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/002_audit_round1_synthesis.md rename to devlog/_fin/260831_prio70_train_round2/002_audit_round1_synthesis.md diff --git a/devlog/_plan/260831_prio70_train_round2/003_audit_round2_synthesis.md b/devlog/_fin/260831_prio70_train_round2/003_audit_round2_synthesis.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/003_audit_round2_synthesis.md rename to devlog/_fin/260831_prio70_train_round2/003_audit_round2_synthesis.md diff --git a/devlog/_plan/260831_prio70_train_round2/004_audit_round3_synthesis.md b/devlog/_fin/260831_prio70_train_round2/004_audit_round3_synthesis.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/004_audit_round3_synthesis.md rename to devlog/_fin/260831_prio70_train_round2/004_audit_round3_synthesis.md diff --git a/devlog/_plan/260831_prio70_train_round2/005_audit_round4_synthesis.md b/devlog/_fin/260831_prio70_train_round2/005_audit_round4_synthesis.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/005_audit_round4_synthesis.md rename to devlog/_fin/260831_prio70_train_round2/005_audit_round4_synthesis.md diff --git a/devlog/_plan/260831_prio70_train_round2/006_audit_round5_synthesis.md b/devlog/_fin/260831_prio70_train_round2/006_audit_round5_synthesis.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/006_audit_round5_synthesis.md rename to devlog/_fin/260831_prio70_train_round2/006_audit_round5_synthesis.md diff --git a/devlog/_plan/260831_prio70_train_round2/007_audit_round6_synthesis.md b/devlog/_fin/260831_prio70_train_round2/007_audit_round6_synthesis.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/007_audit_round6_synthesis.md rename to devlog/_fin/260831_prio70_train_round2/007_audit_round6_synthesis.md diff --git a/devlog/_plan/260831_prio70_train_round2/008_audit_round7_synthesis.md b/devlog/_fin/260831_prio70_train_round2/008_audit_round7_synthesis.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/008_audit_round7_synthesis.md rename to devlog/_fin/260831_prio70_train_round2/008_audit_round7_synthesis.md diff --git a/devlog/_plan/260831_prio70_train_round2/009_audit_round8_synthesis.md b/devlog/_fin/260831_prio70_train_round2/009_audit_round8_synthesis.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/009_audit_round8_synthesis.md rename to devlog/_fin/260831_prio70_train_round2/009_audit_round8_synthesis.md diff --git a/devlog/_plan/260831_prio70_train_round2/010_audit_round9_synthesis.md b/devlog/_fin/260831_prio70_train_round2/010_audit_round9_synthesis.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/010_audit_round9_synthesis.md rename to devlog/_fin/260831_prio70_train_round2/010_audit_round9_synthesis.md diff --git a/devlog/_plan/260831_prio70_train_round2/010_wp1_web_search_query_field.md b/devlog/_fin/260831_prio70_train_round2/010_wp1_web_search_query_field.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/010_wp1_web_search_query_field.md rename to devlog/_fin/260831_prio70_train_round2/010_wp1_web_search_query_field.md diff --git a/devlog/_plan/260831_prio70_train_round2/011_audit_round10_synthesis.md b/devlog/_fin/260831_prio70_train_round2/011_audit_round10_synthesis.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/011_audit_round10_synthesis.md rename to devlog/_fin/260831_prio70_train_round2/011_audit_round10_synthesis.md diff --git a/devlog/_plan/260831_prio70_train_round2/020_wp2_spill_disk_budget.md b/devlog/_fin/260831_prio70_train_round2/020_wp2_spill_disk_budget.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/020_wp2_spill_disk_budget.md rename to devlog/_fin/260831_prio70_train_round2/020_wp2_spill_disk_budget.md diff --git a/devlog/_plan/260831_prio70_train_round2/030_wp3_forked_rollout_restore.md b/devlog/_fin/260831_prio70_train_round2/030_wp3_forked_rollout_restore.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/030_wp3_forked_rollout_restore.md rename to devlog/_fin/260831_prio70_train_round2/030_wp3_forked_rollout_restore.md diff --git a/devlog/_plan/260831_prio70_train_round2/040_wp4_terminal_short_window.md b/devlog/_fin/260831_prio70_train_round2/040_wp4_terminal_short_window.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/040_wp4_terminal_short_window.md rename to devlog/_fin/260831_prio70_train_round2/040_wp4_terminal_short_window.md diff --git a/devlog/_plan/260831_prio70_train_round2/050_wp5_stop_failure_taxonomy.md b/devlog/_fin/260831_prio70_train_round2/050_wp5_stop_failure_taxonomy.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/050_wp5_stop_failure_taxonomy.md rename to devlog/_fin/260831_prio70_train_round2/050_wp5_stop_failure_taxonomy.md diff --git a/devlog/_plan/260831_prio70_train_round2/051_wp5_outcome.md b/devlog/_fin/260831_prio70_train_round2/051_wp5_outcome.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/051_wp5_outcome.md rename to devlog/_fin/260831_prio70_train_round2/051_wp5_outcome.md diff --git a/devlog/_plan/260831_prio70_train_round2/060_wp6_wham_401_refresh.md b/devlog/_fin/260831_prio70_train_round2/060_wp6_wham_401_refresh.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/060_wp6_wham_401_refresh.md rename to devlog/_fin/260831_prio70_train_round2/060_wp6_wham_401_refresh.md diff --git a/devlog/_plan/260831_prio70_train_round2/061_wp6_outcome.md b/devlog/_fin/260831_prio70_train_round2/061_wp6_outcome.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/061_wp6_outcome.md rename to devlog/_fin/260831_prio70_train_round2/061_wp6_outcome.md diff --git a/devlog/_plan/260831_prio70_train_round2/070_outcome.md b/devlog/_fin/260831_prio70_train_round2/070_outcome.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/070_outcome.md rename to devlog/_fin/260831_prio70_train_round2/070_outcome.md diff --git a/devlog/_plan/260831_prio70_train_round2/070_rescan.md b/devlog/_fin/260831_prio70_train_round2/070_rescan.md similarity index 100% rename from devlog/_plan/260831_prio70_train_round2/070_rescan.md rename to devlog/_fin/260831_prio70_train_round2/070_rescan.md diff --git a/devlog/_plan/260901_release_train_2390/000_plan.md b/devlog/_fin/260901_release_train_2390/000_plan.md similarity index 100% rename from devlog/_plan/260901_release_train_2390/000_plan.md rename to devlog/_fin/260901_release_train_2390/000_plan.md diff --git a/devlog/_plan/260901_release_train_2390/010_wp1_regression_audit.md b/devlog/_fin/260901_release_train_2390/010_wp1_regression_audit.md similarity index 100% rename from devlog/_plan/260901_release_train_2390/010_wp1_regression_audit.md rename to devlog/_fin/260901_release_train_2390/010_wp1_regression_audit.md diff --git a/devlog/_plan/260901_release_train_2390/020_wp2_preview_promotion.md b/devlog/_fin/260901_release_train_2390/020_wp2_preview_promotion.md similarity index 100% rename from devlog/_plan/260901_release_train_2390/020_wp2_preview_promotion.md rename to devlog/_fin/260901_release_train_2390/020_wp2_preview_promotion.md diff --git a/devlog/_plan/260901_release_train_2390/030_wp3_main_promotion.md b/devlog/_fin/260901_release_train_2390/030_wp3_main_promotion.md similarity index 100% rename from devlog/_plan/260901_release_train_2390/030_wp3_main_promotion.md rename to devlog/_fin/260901_release_train_2390/030_wp3_main_promotion.md diff --git a/devlog/_plan/260901_release_train_2390/040_wp4_publish.md b/devlog/_fin/260901_release_train_2390/040_wp4_publish.md similarity index 100% rename from devlog/_plan/260901_release_train_2390/040_wp4_publish.md rename to devlog/_fin/260901_release_train_2390/040_wp4_publish.md diff --git a/devlog/_plan/260901_release_train_2390/050_audit_verdicts.md b/devlog/_fin/260901_release_train_2390/050_audit_verdicts.md similarity index 100% rename from devlog/_plan/260901_release_train_2390/050_audit_verdicts.md rename to devlog/_fin/260901_release_train_2390/050_audit_verdicts.md diff --git a/devlog/_plan/260901_release_train_2390/070_outcome.md b/devlog/_fin/260901_release_train_2390/070_outcome.md similarity index 100% rename from devlog/_plan/260901_release_train_2390/070_outcome.md rename to devlog/_fin/260901_release_train_2390/070_outcome.md diff --git a/devlog/_plan/260901_release_train_2390/080_post_release_repairs.md b/devlog/_fin/260901_release_train_2390/080_post_release_repairs.md similarity index 100% rename from devlog/_plan/260901_release_train_2390/080_post_release_repairs.md rename to devlog/_fin/260901_release_train_2390/080_post_release_repairs.md diff --git a/devlog/_plan/260901_remote_hub_restack/000_research.md b/devlog/_fin/260901_remote_hub_restack/000_research.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/000_research.md rename to devlog/_fin/260901_remote_hub_restack/000_research.md diff --git a/devlog/_plan/260901_remote_hub_restack/002_audit_r1_synthesis.md b/devlog/_fin/260901_remote_hub_restack/002_audit_r1_synthesis.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/002_audit_r1_synthesis.md rename to devlog/_fin/260901_remote_hub_restack/002_audit_r1_synthesis.md diff --git a/devlog/_plan/260901_remote_hub_restack/003_review_thread_ledger.md b/devlog/_fin/260901_remote_hub_restack/003_review_thread_ledger.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/003_review_thread_ledger.md rename to devlog/_fin/260901_remote_hub_restack/003_review_thread_ledger.md diff --git a/devlog/_plan/260901_remote_hub_restack/010_wp1_design_contract.md b/devlog/_fin/260901_remote_hub_restack/010_wp1_design_contract.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/010_wp1_design_contract.md rename to devlog/_fin/260901_remote_hub_restack/010_wp1_design_contract.md diff --git a/devlog/_plan/260901_remote_hub_restack/011_wp1_outcome.md b/devlog/_fin/260901_remote_hub_restack/011_wp1_outcome.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/011_wp1_outcome.md rename to devlog/_fin/260901_remote_hub_restack/011_wp1_outcome.md diff --git a/devlog/_plan/260901_remote_hub_restack/020_wp2_p1_protocol_catalog.md b/devlog/_fin/260901_remote_hub_restack/020_wp2_p1_protocol_catalog.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/020_wp2_p1_protocol_catalog.md rename to devlog/_fin/260901_remote_hub_restack/020_wp2_p1_protocol_catalog.md diff --git a/devlog/_plan/260901_remote_hub_restack/021_wp2_outcome.md b/devlog/_fin/260901_remote_hub_restack/021_wp2_outcome.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/021_wp2_outcome.md rename to devlog/_fin/260901_remote_hub_restack/021_wp2_outcome.md diff --git a/devlog/_plan/260901_remote_hub_restack/030_wp3_p2_remote_session.md b/devlog/_fin/260901_remote_hub_restack/030_wp3_p2_remote_session.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/030_wp3_p2_remote_session.md rename to devlog/_fin/260901_remote_hub_restack/030_wp3_p2_remote_session.md diff --git a/devlog/_plan/260901_remote_hub_restack/031_wp3_outcome.md b/devlog/_fin/260901_remote_hub_restack/031_wp3_outcome.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/031_wp3_outcome.md rename to devlog/_fin/260901_remote_hub_restack/031_wp3_outcome.md diff --git a/devlog/_plan/260901_remote_hub_restack/040_wp4_p3_connect.md b/devlog/_fin/260901_remote_hub_restack/040_wp4_p3_connect.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/040_wp4_p3_connect.md rename to devlog/_fin/260901_remote_hub_restack/040_wp4_p3_connect.md diff --git a/devlog/_plan/260901_remote_hub_restack/041_wp4_outcome.md b/devlog/_fin/260901_remote_hub_restack/041_wp4_outcome.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/041_wp4_outcome.md rename to devlog/_fin/260901_remote_hub_restack/041_wp4_outcome.md diff --git a/devlog/_plan/260901_remote_hub_restack/050_wp5_p4_two_plane.md b/devlog/_fin/260901_remote_hub_restack/050_wp5_p4_two_plane.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/050_wp5_p4_two_plane.md rename to devlog/_fin/260901_remote_hub_restack/050_wp5_p4_two_plane.md diff --git a/devlog/_plan/260901_remote_hub_restack/051_wp5_outcome.md b/devlog/_fin/260901_remote_hub_restack/051_wp5_outcome.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/051_wp5_outcome.md rename to devlog/_fin/260901_remote_hub_restack/051_wp5_outcome.md diff --git a/devlog/_plan/260901_remote_hub_restack/060_wp6_p5_deploy.md b/devlog/_fin/260901_remote_hub_restack/060_wp6_p5_deploy.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/060_wp6_p5_deploy.md rename to devlog/_fin/260901_remote_hub_restack/060_wp6_p5_deploy.md diff --git a/devlog/_plan/260901_remote_hub_restack/061_wp6_outcome.md b/devlog/_fin/260901_remote_hub_restack/061_wp6_outcome.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/061_wp6_outcome.md rename to devlog/_fin/260901_remote_hub_restack/061_wp6_outcome.md diff --git a/devlog/_plan/260901_remote_hub_restack/070_wp7_p6_hardening.md b/devlog/_fin/260901_remote_hub_restack/070_wp7_p6_hardening.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/070_wp7_p6_hardening.md rename to devlog/_fin/260901_remote_hub_restack/070_wp7_p6_hardening.md diff --git a/devlog/_plan/260901_remote_hub_restack/071_wp7_outcome.md b/devlog/_fin/260901_remote_hub_restack/071_wp7_outcome.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/071_wp7_outcome.md rename to devlog/_fin/260901_remote_hub_restack/071_wp7_outcome.md diff --git a/devlog/_plan/260901_remote_hub_restack/072_stack_state.md b/devlog/_fin/260901_remote_hub_restack/072_stack_state.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/072_stack_state.md rename to devlog/_fin/260901_remote_hub_restack/072_stack_state.md diff --git a/devlog/_plan/260901_remote_hub_restack/080_wp8_stack_integrity.md b/devlog/_fin/260901_remote_hub_restack/080_wp8_stack_integrity.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/080_wp8_stack_integrity.md rename to devlog/_fin/260901_remote_hub_restack/080_wp8_stack_integrity.md diff --git a/devlog/_plan/260901_remote_hub_restack/081_wp8_ci_repairs.md b/devlog/_fin/260901_remote_hub_restack/081_wp8_ci_repairs.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/081_wp8_ci_repairs.md rename to devlog/_fin/260901_remote_hub_restack/081_wp8_ci_repairs.md diff --git a/devlog/_plan/260901_remote_hub_restack/090_outcome.md b/devlog/_fin/260901_remote_hub_restack/090_outcome.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/090_outcome.md rename to devlog/_fin/260901_remote_hub_restack/090_outcome.md diff --git a/devlog/_plan/260901_remote_hub_restack/100_polish_audit.md b/devlog/_fin/260901_remote_hub_restack/100_polish_audit.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/100_polish_audit.md rename to devlog/_fin/260901_remote_hub_restack/100_polish_audit.md diff --git a/devlog/_plan/260901_remote_hub_restack/101_polish_outcome.md b/devlog/_fin/260901_remote_hub_restack/101_polish_outcome.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/101_polish_outcome.md rename to devlog/_fin/260901_remote_hub_restack/101_polish_outcome.md diff --git a/devlog/_plan/260901_remote_hub_restack/102_axis_ledger.md b/devlog/_fin/260901_remote_hub_restack/102_axis_ledger.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/102_axis_ledger.md rename to devlog/_fin/260901_remote_hub_restack/102_axis_ledger.md diff --git a/devlog/_plan/260901_remote_hub_restack/110_merge_train_plan.md b/devlog/_fin/260901_remote_hub_restack/110_merge_train_plan.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/110_merge_train_plan.md rename to devlog/_fin/260901_remote_hub_restack/110_merge_train_plan.md diff --git a/devlog/_plan/260901_remote_hub_restack/111_wp1_3147_outcome.md b/devlog/_fin/260901_remote_hub_restack/111_wp1_3147_outcome.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/111_wp1_3147_outcome.md rename to devlog/_fin/260901_remote_hub_restack/111_wp1_3147_outcome.md diff --git a/devlog/_plan/260901_remote_hub_restack/112_wp2_order_reversal.md b/devlog/_fin/260901_remote_hub_restack/112_wp2_order_reversal.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/112_wp2_order_reversal.md rename to devlog/_fin/260901_remote_hub_restack/112_wp2_order_reversal.md diff --git a/devlog/_plan/260901_remote_hub_restack/120_merge_train_outcome.md b/devlog/_fin/260901_remote_hub_restack/120_merge_train_outcome.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/120_merge_train_outcome.md rename to devlog/_fin/260901_remote_hub_restack/120_merge_train_outcome.md diff --git a/devlog/_plan/260901_remote_hub_restack/121_open_threads_after_merge.md b/devlog/_fin/260901_remote_hub_restack/121_open_threads_after_merge.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/121_open_threads_after_merge.md rename to devlog/_fin/260901_remote_hub_restack/121_open_threads_after_merge.md diff --git a/devlog/_plan/260901_remote_hub_restack/122_post_merge_verification.md b/devlog/_fin/260901_remote_hub_restack/122_post_merge_verification.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/122_post_merge_verification.md rename to devlog/_fin/260901_remote_hub_restack/122_post_merge_verification.md diff --git a/devlog/_plan/260901_remote_hub_restack/130_final_state.md b/devlog/_fin/260901_remote_hub_restack/130_final_state.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/130_final_state.md rename to devlog/_fin/260901_remote_hub_restack/130_final_state.md diff --git a/devlog/_plan/260901_remote_hub_restack/131_worktree_state.md b/devlog/_fin/260901_remote_hub_restack/131_worktree_state.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/131_worktree_state.md rename to devlog/_fin/260901_remote_hub_restack/131_worktree_state.md diff --git a/devlog/_plan/260901_remote_hub_restack/140_objective_closeout.md b/devlog/_fin/260901_remote_hub_restack/140_objective_closeout.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/140_objective_closeout.md rename to devlog/_fin/260901_remote_hub_restack/140_objective_closeout.md diff --git a/devlog/_plan/260901_remote_hub_restack/141_review_fix_verification.md b/devlog/_fin/260901_remote_hub_restack/141_review_fix_verification.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/141_review_fix_verification.md rename to devlog/_fin/260901_remote_hub_restack/141_review_fix_verification.md diff --git a/devlog/_plan/260901_remote_hub_restack/150_unit_closed.md b/devlog/_fin/260901_remote_hub_restack/150_unit_closed.md similarity index 100% rename from devlog/_plan/260901_remote_hub_restack/150_unit_closed.md rename to devlog/_fin/260901_remote_hub_restack/150_unit_closed.md diff --git a/devlog/_plan/260902_admin_merge_3190/000_plan.md b/devlog/_fin/260902_admin_merge_3190/000_plan.md similarity index 100% rename from devlog/_plan/260902_admin_merge_3190/000_plan.md rename to devlog/_fin/260902_admin_merge_3190/000_plan.md diff --git a/devlog/_plan/260902_admin_merge_3190/002_audit_round1.md b/devlog/_fin/260902_admin_merge_3190/002_audit_round1.md similarity index 100% rename from devlog/_plan/260902_admin_merge_3190/002_audit_round1.md rename to devlog/_fin/260902_admin_merge_3190/002_audit_round1.md diff --git a/devlog/_plan/260902_admin_merge_3190/010_wp1_anonymize_home_paths.md b/devlog/_fin/260902_admin_merge_3190/010_wp1_anonymize_home_paths.md similarity index 100% rename from devlog/_plan/260902_admin_merge_3190/010_wp1_anonymize_home_paths.md rename to devlog/_fin/260902_admin_merge_3190/010_wp1_anonymize_home_paths.md diff --git a/devlog/_plan/260902_admin_merge_3190/011_wp1_stale_check.md b/devlog/_fin/260902_admin_merge_3190/011_wp1_stale_check.md similarity index 100% rename from devlog/_plan/260902_admin_merge_3190/011_wp1_stale_check.md rename to devlog/_fin/260902_admin_merge_3190/011_wp1_stale_check.md diff --git a/devlog/_plan/260902_admin_merge_3190/020_wp2_rebase_merge_3190.md b/devlog/_fin/260902_admin_merge_3190/020_wp2_rebase_merge_3190.md similarity index 100% rename from devlog/_plan/260902_admin_merge_3190/020_wp2_rebase_merge_3190.md rename to devlog/_fin/260902_admin_merge_3190/020_wp2_rebase_merge_3190.md diff --git a/devlog/_plan/260902_admin_merge_3190/021_wp2_cherry_pick.md b/devlog/_fin/260902_admin_merge_3190/021_wp2_cherry_pick.md similarity index 100% rename from devlog/_plan/260902_admin_merge_3190/021_wp2_cherry_pick.md rename to devlog/_fin/260902_admin_merge_3190/021_wp2_cherry_pick.md diff --git a/devlog/_plan/260902_admin_merge_3190/030_wp3_inventory_refresh.md b/devlog/_fin/260902_admin_merge_3190/030_wp3_inventory_refresh.md similarity index 100% rename from devlog/_plan/260902_admin_merge_3190/030_wp3_inventory_refresh.md rename to devlog/_fin/260902_admin_merge_3190/030_wp3_inventory_refresh.md diff --git a/devlog/_plan/260902_admin_merge_3190/031_wp3_live_inventory.md b/devlog/_fin/260902_admin_merge_3190/031_wp3_live_inventory.md similarity index 100% rename from devlog/_plan/260902_admin_merge_3190/031_wp3_live_inventory.md rename to devlog/_fin/260902_admin_merge_3190/031_wp3_live_inventory.md diff --git a/devlog/_plan/260902_bug_pr_closeout_stack/000_plan.md b/devlog/_fin/260902_bug_pr_closeout_stack/000_plan.md similarity index 100% rename from devlog/_plan/260902_bug_pr_closeout_stack/000_plan.md rename to devlog/_fin/260902_bug_pr_closeout_stack/000_plan.md diff --git a/devlog/_plan/260902_bug_pr_closeout_stack/010_phase1.md b/devlog/_fin/260902_bug_pr_closeout_stack/010_phase1.md similarity index 100% rename from devlog/_plan/260902_bug_pr_closeout_stack/010_phase1.md rename to devlog/_fin/260902_bug_pr_closeout_stack/010_phase1.md diff --git a/devlog/_plan/260902_bug_pr_closeout_stack/011_wp1_landing.md b/devlog/_fin/260902_bug_pr_closeout_stack/011_wp1_landing.md similarity index 100% rename from devlog/_plan/260902_bug_pr_closeout_stack/011_wp1_landing.md rename to devlog/_fin/260902_bug_pr_closeout_stack/011_wp1_landing.md diff --git a/devlog/_plan/260902_bug_pr_closeout_stack/020_phase2.md b/devlog/_fin/260902_bug_pr_closeout_stack/020_phase2.md similarity index 100% rename from devlog/_plan/260902_bug_pr_closeout_stack/020_phase2.md rename to devlog/_fin/260902_bug_pr_closeout_stack/020_phase2.md diff --git a/devlog/_plan/260902_bug_pr_closeout_stack/021_wp2_landing.md b/devlog/_fin/260902_bug_pr_closeout_stack/021_wp2_landing.md similarity index 100% rename from devlog/_plan/260902_bug_pr_closeout_stack/021_wp2_landing.md rename to devlog/_fin/260902_bug_pr_closeout_stack/021_wp2_landing.md diff --git a/devlog/_plan/260902_bug_pr_closeout_stack/030_phase3.md b/devlog/_fin/260902_bug_pr_closeout_stack/030_phase3.md similarity index 100% rename from devlog/_plan/260902_bug_pr_closeout_stack/030_phase3.md rename to devlog/_fin/260902_bug_pr_closeout_stack/030_phase3.md diff --git a/devlog/_plan/260902_bug_pr_closeout_stack/031_wp3_disposition.md b/devlog/_fin/260902_bug_pr_closeout_stack/031_wp3_disposition.md similarity index 100% rename from devlog/_plan/260902_bug_pr_closeout_stack/031_wp3_disposition.md rename to devlog/_fin/260902_bug_pr_closeout_stack/031_wp3_disposition.md diff --git a/devlog/_plan/260902_bug_pr_closeout_stack/040_phase4.md b/devlog/_fin/260902_bug_pr_closeout_stack/040_phase4.md similarity index 100% rename from devlog/_plan/260902_bug_pr_closeout_stack/040_phase4.md rename to devlog/_fin/260902_bug_pr_closeout_stack/040_phase4.md diff --git a/devlog/_plan/260902_bug_pr_closeout_stack/050_phase5.md b/devlog/_fin/260902_bug_pr_closeout_stack/050_phase5.md similarity index 100% rename from devlog/_plan/260902_bug_pr_closeout_stack/050_phase5.md rename to devlog/_fin/260902_bug_pr_closeout_stack/050_phase5.md diff --git a/devlog/_plan/260902_bug_pr_closeout_stack/060_phase6.md b/devlog/_fin/260902_bug_pr_closeout_stack/060_phase6.md similarity index 100% rename from devlog/_plan/260902_bug_pr_closeout_stack/060_phase6.md rename to devlog/_fin/260902_bug_pr_closeout_stack/060_phase6.md diff --git a/devlog/_plan/260902_bug_pr_closeout_stack/070_closeout.md b/devlog/_fin/260902_bug_pr_closeout_stack/070_closeout.md similarity index 100% rename from devlog/_plan/260902_bug_pr_closeout_stack/070_closeout.md rename to devlog/_fin/260902_bug_pr_closeout_stack/070_closeout.md diff --git a/devlog/_plan/260902_cursor_local_models_schema/000_research.md b/devlog/_fin/260902_cursor_local_models_schema/000_research.md similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/000_research.md rename to devlog/_fin/260902_cursor_local_models_schema/000_research.md diff --git a/devlog/_plan/260902_cursor_local_models_schema/005_audit_round1.md b/devlog/_fin/260902_cursor_local_models_schema/005_audit_round1.md similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/005_audit_round1.md rename to devlog/_fin/260902_cursor_local_models_schema/005_audit_round1.md diff --git a/devlog/_plan/260902_cursor_local_models_schema/010_layer1_models_capabilities.md b/devlog/_fin/260902_cursor_local_models_schema/010_layer1_models_capabilities.md similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/010_layer1_models_capabilities.md rename to devlog/_fin/260902_cursor_local_models_schema/010_layer1_models_capabilities.md diff --git a/devlog/_plan/260902_cursor_local_models_schema/011_effort_control.png b/devlog/_fin/260902_cursor_local_models_schema/011_effort_control.png similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/011_effort_control.png rename to devlog/_fin/260902_cursor_local_models_schema/011_effort_control.png diff --git a/devlog/_plan/260902_cursor_local_models_schema/012_effort_ladder.png b/devlog/_fin/260902_cursor_local_models_schema/012_effort_ladder.png similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/012_effort_ladder.png rename to devlog/_fin/260902_cursor_local_models_schema/012_effort_ladder.png diff --git a/devlog/_plan/260902_cursor_local_models_schema/013_high_turn.png b/devlog/_fin/260902_cursor_local_models_schema/013_high_turn.png similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/013_high_turn.png rename to devlog/_fin/260902_cursor_local_models_schema/013_high_turn.png diff --git a/devlog/_plan/260902_cursor_local_models_schema/015_layer1_live_evidence.md b/devlog/_fin/260902_cursor_local_models_schema/015_layer1_live_evidence.md similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/015_layer1_live_evidence.md rename to devlog/_fin/260902_cursor_local_models_schema/015_layer1_live_evidence.md diff --git a/devlog/_plan/260902_cursor_local_models_schema/016_layer1_check.md b/devlog/_fin/260902_cursor_local_models_schema/016_layer1_check.md similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/016_layer1_check.md rename to devlog/_fin/260902_cursor_local_models_schema/016_layer1_check.md diff --git a/devlog/_plan/260902_cursor_local_models_schema/017_impl_review.md b/devlog/_fin/260902_cursor_local_models_schema/017_impl_review.md similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/017_impl_review.md rename to devlog/_fin/260902_cursor_local_models_schema/017_impl_review.md diff --git a/devlog/_plan/260902_cursor_local_models_schema/020_layer2_docs_guide.md b/devlog/_fin/260902_cursor_local_models_schema/020_layer2_docs_guide.md similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/020_layer2_docs_guide.md rename to devlog/_fin/260902_cursor_local_models_schema/020_layer2_docs_guide.md diff --git a/devlog/_plan/260902_cursor_local_models_schema/021_pr_rollup.json b/devlog/_fin/260902_cursor_local_models_schema/021_pr_rollup.json similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/021_pr_rollup.json rename to devlog/_fin/260902_cursor_local_models_schema/021_pr_rollup.json diff --git a/devlog/_plan/260902_cursor_local_models_schema/022_stack_closeout.md b/devlog/_fin/260902_cursor_local_models_schema/022_stack_closeout.md similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/022_stack_closeout.md rename to devlog/_fin/260902_cursor_local_models_schema/022_stack_closeout.md diff --git a/devlog/_plan/260902_cursor_local_models_schema/030_max_mode_context_selector.md b/devlog/_fin/260902_cursor_local_models_schema/030_max_mode_context_selector.md similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/030_max_mode_context_selector.md rename to devlog/_fin/260902_cursor_local_models_schema/030_max_mode_context_selector.md diff --git a/devlog/_plan/260902_cursor_local_models_schema/031_context_selector.png b/devlog/_fin/260902_cursor_local_models_schema/031_context_selector.png similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/031_context_selector.png rename to devlog/_fin/260902_cursor_local_models_schema/031_context_selector.png diff --git a/devlog/_plan/260902_cursor_local_models_schema/032_context_options.png b/devlog/_fin/260902_cursor_local_models_schema/032_context_options.png similarity index 100% rename from devlog/_plan/260902_cursor_local_models_schema/032_context_options.png rename to devlog/_fin/260902_cursor_local_models_schema/032_context_options.png diff --git a/devlog/_plan/260902_cursor_unified_identity/000_plan.md b/devlog/_fin/260902_cursor_unified_identity/000_plan.md similarity index 100% rename from devlog/_plan/260902_cursor_unified_identity/000_plan.md rename to devlog/_fin/260902_cursor_unified_identity/000_plan.md diff --git a/devlog/_plan/260902_cursor_unified_identity/001_current_state.md b/devlog/_fin/260902_cursor_unified_identity/001_current_state.md similarity index 100% rename from devlog/_plan/260902_cursor_unified_identity/001_current_state.md rename to devlog/_fin/260902_cursor_unified_identity/001_current_state.md diff --git a/devlog/_plan/260902_cursor_unified_identity/002_audit_round1.md b/devlog/_fin/260902_cursor_unified_identity/002_audit_round1.md similarity index 100% rename from devlog/_plan/260902_cursor_unified_identity/002_audit_round1.md rename to devlog/_fin/260902_cursor_unified_identity/002_audit_round1.md diff --git a/devlog/_plan/260902_cursor_unified_identity/010_wp2_umbrella_seed.md b/devlog/_fin/260902_cursor_unified_identity/010_wp2_umbrella_seed.md similarity index 100% rename from devlog/_plan/260902_cursor_unified_identity/010_wp2_umbrella_seed.md rename to devlog/_fin/260902_cursor_unified_identity/010_wp2_umbrella_seed.md diff --git a/devlog/_plan/260902_cursor_unified_identity/020_wp3_codex_fast_toggle.md b/devlog/_fin/260902_cursor_unified_identity/020_wp3_codex_fast_toggle.md similarity index 100% rename from devlog/_plan/260902_cursor_unified_identity/020_wp3_codex_fast_toggle.md rename to devlog/_fin/260902_cursor_unified_identity/020_wp3_codex_fast_toggle.md diff --git a/devlog/_plan/260902_cursor_unified_identity/030_wp4_global_fast_switch.md b/devlog/_fin/260902_cursor_unified_identity/030_wp4_global_fast_switch.md similarity index 100% rename from devlog/_plan/260902_cursor_unified_identity/030_wp4_global_fast_switch.md rename to devlog/_fin/260902_cursor_unified_identity/030_wp4_global_fast_switch.md diff --git a/devlog/_plan/260902_cursor_unified_identity/040_residuals.md b/devlog/_fin/260902_cursor_unified_identity/040_residuals.md similarity index 100% rename from devlog/_plan/260902_cursor_unified_identity/040_residuals.md rename to devlog/_fin/260902_cursor_unified_identity/040_residuals.md diff --git a/devlog/_plan/260902_multiplatform_qa_and_gui/000_host_inventory.md b/devlog/_fin/260902_multiplatform_qa_and_gui/000_host_inventory.md similarity index 100% rename from devlog/_plan/260902_multiplatform_qa_and_gui/000_host_inventory.md rename to devlog/_fin/260902_multiplatform_qa_and_gui/000_host_inventory.md diff --git a/devlog/_plan/260902_multiplatform_qa_and_gui/001_qa_evidence_layout.md b/devlog/_fin/260902_multiplatform_qa_and_gui/001_qa_evidence_layout.md similarity index 100% rename from devlog/_plan/260902_multiplatform_qa_and_gui/001_qa_evidence_layout.md rename to devlog/_fin/260902_multiplatform_qa_and_gui/001_qa_evidence_layout.md diff --git a/devlog/_plan/260902_multiplatform_qa_and_gui/002_qa_verdicts.md b/devlog/_fin/260902_multiplatform_qa_and_gui/002_qa_verdicts.md similarity index 100% rename from devlog/_plan/260902_multiplatform_qa_and_gui/002_qa_verdicts.md rename to devlog/_fin/260902_multiplatform_qa_and_gui/002_qa_verdicts.md diff --git a/devlog/_plan/260902_multiplatform_qa_and_gui/010_wp2_gui_design.md b/devlog/_fin/260902_multiplatform_qa_and_gui/010_wp2_gui_design.md similarity index 100% rename from devlog/_plan/260902_multiplatform_qa_and_gui/010_wp2_gui_design.md rename to devlog/_fin/260902_multiplatform_qa_and_gui/010_wp2_gui_design.md diff --git a/devlog/_plan/260902_multiplatform_qa_and_gui/011_wp2_outcome.md b/devlog/_fin/260902_multiplatform_qa_and_gui/011_wp2_outcome.md similarity index 100% rename from devlog/_plan/260902_multiplatform_qa_and_gui/011_wp2_outcome.md rename to devlog/_fin/260902_multiplatform_qa_and_gui/011_wp2_outcome.md diff --git a/devlog/_plan/260902_multiplatform_qa_and_gui/020_wp3_wp5_deploy_qa.md b/devlog/_fin/260902_multiplatform_qa_and_gui/020_wp3_wp5_deploy_qa.md similarity index 100% rename from devlog/_plan/260902_multiplatform_qa_and_gui/020_wp3_wp5_deploy_qa.md rename to devlog/_fin/260902_multiplatform_qa_and_gui/020_wp3_wp5_deploy_qa.md diff --git a/devlog/_plan/260902_multiplatform_qa_and_gui/021_wp3_macmini_outcome.md b/devlog/_fin/260902_multiplatform_qa_and_gui/021_wp3_macmini_outcome.md similarity index 100% rename from devlog/_plan/260902_multiplatform_qa_and_gui/021_wp3_macmini_outcome.md rename to devlog/_fin/260902_multiplatform_qa_and_gui/021_wp3_macmini_outcome.md diff --git a/devlog/_plan/260902_multiplatform_qa_and_gui/022_wp4_linux_macbook_outcome.md b/devlog/_fin/260902_multiplatform_qa_and_gui/022_wp4_linux_macbook_outcome.md similarity index 100% rename from devlog/_plan/260902_multiplatform_qa_and_gui/022_wp4_linux_macbook_outcome.md rename to devlog/_fin/260902_multiplatform_qa_and_gui/022_wp4_linux_macbook_outcome.md diff --git a/devlog/_plan/260902_multiplatform_qa_and_gui/023_wp5_windows_outcome.md b/devlog/_fin/260902_multiplatform_qa_and_gui/023_wp5_windows_outcome.md similarity index 100% rename from devlog/_plan/260902_multiplatform_qa_and_gui/023_wp5_windows_outcome.md rename to devlog/_fin/260902_multiplatform_qa_and_gui/023_wp5_windows_outcome.md diff --git a/devlog/_plan/260902_multiplatform_qa_and_gui/090_outcome.md b/devlog/_fin/260902_multiplatform_qa_and_gui/090_outcome.md similarity index 100% rename from devlog/_plan/260902_multiplatform_qa_and_gui/090_outcome.md rename to devlog/_fin/260902_multiplatform_qa_and_gui/090_outcome.md diff --git a/devlog/_plan/260902_multiplatform_qa_and_gui/091_wp6_merge_outcome.md b/devlog/_fin/260902_multiplatform_qa_and_gui/091_wp6_merge_outcome.md similarity index 100% rename from devlog/_plan/260902_multiplatform_qa_and_gui/091_wp6_merge_outcome.md rename to devlog/_fin/260902_multiplatform_qa_and_gui/091_wp6_merge_outcome.md diff --git a/devlog/_plan/260902_multiplatform_qa_and_gui/092_objective_closeout.md b/devlog/_fin/260902_multiplatform_qa_and_gui/092_objective_closeout.md similarity index 100% rename from devlog/_plan/260902_multiplatform_qa_and_gui/092_objective_closeout.md rename to devlog/_fin/260902_multiplatform_qa_and_gui/092_objective_closeout.md diff --git a/devlog/_plan/260902_windows_ci_release/000_inventory.md b/devlog/_fin/260902_windows_ci_release/000_inventory.md similarity index 100% rename from devlog/_plan/260902_windows_ci_release/000_inventory.md rename to devlog/_fin/260902_windows_ci_release/000_inventory.md diff --git a/devlog/_plan/260902_windows_ci_release/010_async_acl_lifecycle.md b/devlog/_fin/260902_windows_ci_release/010_async_acl_lifecycle.md similarity index 100% rename from devlog/_plan/260902_windows_ci_release/010_async_acl_lifecycle.md rename to devlog/_fin/260902_windows_ci_release/010_async_acl_lifecycle.md diff --git a/devlog/_plan/260902_windows_ci_release/011_wp2_evidence.md b/devlog/_fin/260902_windows_ci_release/011_wp2_evidence.md similarity index 100% rename from devlog/_plan/260902_windows_ci_release/011_wp2_evidence.md rename to devlog/_fin/260902_windows_ci_release/011_wp2_evidence.md diff --git a/devlog/_plan/260902_windows_ci_release/020_fsync_spill_bump.md b/devlog/_fin/260902_windows_ci_release/020_fsync_spill_bump.md similarity index 100% rename from devlog/_plan/260902_windows_ci_release/020_fsync_spill_bump.md rename to devlog/_fin/260902_windows_ci_release/020_fsync_spill_bump.md diff --git a/devlog/_plan/260902_windows_ci_release/030_regression_audit.md b/devlog/_fin/260902_windows_ci_release/030_regression_audit.md similarity index 100% rename from devlog/_plan/260902_windows_ci_release/030_regression_audit.md rename to devlog/_fin/260902_windows_ci_release/030_regression_audit.md diff --git a/devlog/_plan/260902_windows_ci_release/040_promote_and_bump.md b/devlog/_fin/260902_windows_ci_release/040_promote_and_bump.md similarity index 100% rename from devlog/_plan/260902_windows_ci_release/040_promote_and_bump.md rename to devlog/_fin/260902_windows_ci_release/040_promote_and_bump.md diff --git a/devlog/_plan/260902_windows_ci_release/070_outcome.md b/devlog/_fin/260902_windows_ci_release/070_outcome.md similarity index 100% rename from devlog/_plan/260902_windows_ci_release/070_outcome.md rename to devlog/_fin/260902_windows_ci_release/070_outcome.md diff --git a/devlog/_plan/260902_windows_ci_release/080_release_automation_followups.md b/devlog/_fin/260902_windows_ci_release/080_release_automation_followups.md similarity index 100% rename from devlog/_plan/260902_windows_ci_release/080_release_automation_followups.md rename to devlog/_fin/260902_windows_ci_release/080_release_automation_followups.md diff --git a/devlog/_plan/260903_bug_drawdown_bcda/000_plan.md b/devlog/_fin/260903_bug_drawdown_bcda/000_plan.md similarity index 100% rename from devlog/_plan/260903_bug_drawdown_bcda/000_plan.md rename to devlog/_fin/260903_bug_drawdown_bcda/000_plan.md diff --git a/devlog/_plan/260903_bug_drawdown_bcda/010_phase1.md b/devlog/_fin/260903_bug_drawdown_bcda/010_phase1.md similarity index 100% rename from devlog/_plan/260903_bug_drawdown_bcda/010_phase1.md rename to devlog/_fin/260903_bug_drawdown_bcda/010_phase1.md diff --git a/devlog/_plan/260903_bug_drawdown_bcda/020_phase2.md b/devlog/_fin/260903_bug_drawdown_bcda/020_phase2.md similarity index 100% rename from devlog/_plan/260903_bug_drawdown_bcda/020_phase2.md rename to devlog/_fin/260903_bug_drawdown_bcda/020_phase2.md diff --git a/devlog/_plan/260903_bug_drawdown_bcda/030_phase3.md b/devlog/_fin/260903_bug_drawdown_bcda/030_phase3.md similarity index 100% rename from devlog/_plan/260903_bug_drawdown_bcda/030_phase3.md rename to devlog/_fin/260903_bug_drawdown_bcda/030_phase3.md diff --git a/devlog/_plan/260903_bug_drawdown_bcda/040_phase4.md b/devlog/_fin/260903_bug_drawdown_bcda/040_phase4.md similarity index 100% rename from devlog/_plan/260903_bug_drawdown_bcda/040_phase4.md rename to devlog/_fin/260903_bug_drawdown_bcda/040_phase4.md diff --git a/devlog/_plan/260903_bug_drawdown_bcda/050_phase5.md b/devlog/_fin/260903_bug_drawdown_bcda/050_phase5.md similarity index 100% rename from devlog/_plan/260903_bug_drawdown_bcda/050_phase5.md rename to devlog/_fin/260903_bug_drawdown_bcda/050_phase5.md diff --git a/devlog/_plan/260903_bug_drawdown_bcda/060_phase6.md b/devlog/_fin/260903_bug_drawdown_bcda/060_phase6.md similarity index 100% rename from devlog/_plan/260903_bug_drawdown_bcda/060_phase6.md rename to devlog/_fin/260903_bug_drawdown_bcda/060_phase6.md diff --git a/devlog/_plan/260903_bug_drawdown_bcda/070_phase7.md b/devlog/_fin/260903_bug_drawdown_bcda/070_phase7.md similarity index 100% rename from devlog/_plan/260903_bug_drawdown_bcda/070_phase7.md rename to devlog/_fin/260903_bug_drawdown_bcda/070_phase7.md diff --git a/devlog/_plan/260903_bug_drawdown_bcda/080_phase8.md b/devlog/_fin/260903_bug_drawdown_bcda/080_phase8.md similarity index 100% rename from devlog/_plan/260903_bug_drawdown_bcda/080_phase8.md rename to devlog/_fin/260903_bug_drawdown_bcda/080_phase8.md diff --git a/devlog/_plan/260903_bug_drawdown_bcda/090_phase9.md b/devlog/_fin/260903_bug_drawdown_bcda/090_phase9.md similarity index 100% rename from devlog/_plan/260903_bug_drawdown_bcda/090_phase9.md rename to devlog/_fin/260903_bug_drawdown_bcda/090_phase9.md diff --git a/devlog/_plan/260903_bug_drawdown_bcda/100_closeout.md b/devlog/_fin/260903_bug_drawdown_bcda/100_closeout.md similarity index 100% rename from devlog/_plan/260903_bug_drawdown_bcda/100_closeout.md rename to devlog/_fin/260903_bug_drawdown_bcda/100_closeout.md diff --git a/devlog/_plan/260903_contributor_credit_restoration/000_plan.md b/devlog/_fin/260903_contributor_credit_restoration/000_plan.md similarity index 100% rename from devlog/_plan/260903_contributor_credit_restoration/000_plan.md rename to devlog/_fin/260903_contributor_credit_restoration/000_plan.md diff --git a/devlog/_plan/260903_contributor_credit_restoration/010_credits_file.md b/devlog/_fin/260903_contributor_credit_restoration/010_credits_file.md similarity index 100% rename from devlog/_plan/260903_contributor_credit_restoration/010_credits_file.md rename to devlog/_fin/260903_contributor_credit_restoration/010_credits_file.md diff --git a/devlog/_plan/260903_contributor_credit_restoration/020_hygiene_gate.md b/devlog/_fin/260903_contributor_credit_restoration/020_hygiene_gate.md similarity index 100% rename from devlog/_plan/260903_contributor_credit_restoration/020_hygiene_gate.md rename to devlog/_fin/260903_contributor_credit_restoration/020_hygiene_gate.md diff --git a/devlog/_plan/260903_contributor_credit_restoration/030_release_notes.md b/devlog/_fin/260903_contributor_credit_restoration/030_release_notes.md similarity index 100% rename from devlog/_plan/260903_contributor_credit_restoration/030_release_notes.md rename to devlog/_fin/260903_contributor_credit_restoration/030_release_notes.md diff --git a/devlog/_plan/260903_contributor_credit_restoration/100_closeout.md b/devlog/_fin/260903_contributor_credit_restoration/100_closeout.md similarity index 100% rename from devlog/_plan/260903_contributor_credit_restoration/100_closeout.md rename to devlog/_fin/260903_contributor_credit_restoration/100_closeout.md diff --git a/devlog/_plan/260903_gemini_38_rollout/000_plan.md b/devlog/_fin/260903_gemini_38_rollout/000_plan.md similarity index 100% rename from devlog/_plan/260903_gemini_38_rollout/000_plan.md rename to devlog/_fin/260903_gemini_38_rollout/000_plan.md diff --git a/devlog/_plan/260903_gemini_38_rollout/001_vendor_claim_ledger.md b/devlog/_fin/260903_gemini_38_rollout/001_vendor_claim_ledger.md similarity index 100% rename from devlog/_plan/260903_gemini_38_rollout/001_vendor_claim_ledger.md rename to devlog/_fin/260903_gemini_38_rollout/001_vendor_claim_ledger.md diff --git a/devlog/_plan/260903_gemini_38_rollout/002_live_cca_probe.md b/devlog/_fin/260903_gemini_38_rollout/002_live_cca_probe.md similarity index 100% rename from devlog/_plan/260903_gemini_38_rollout/002_live_cca_probe.md rename to devlog/_fin/260903_gemini_38_rollout/002_live_cca_probe.md diff --git a/devlog/_plan/260903_gemini_38_rollout/003_audit_round1_synthesis.md b/devlog/_fin/260903_gemini_38_rollout/003_audit_round1_synthesis.md similarity index 100% rename from devlog/_plan/260903_gemini_38_rollout/003_audit_round1_synthesis.md rename to devlog/_fin/260903_gemini_38_rollout/003_audit_round1_synthesis.md diff --git a/devlog/_plan/260903_gemini_38_rollout/004_no_change_inventory.md b/devlog/_fin/260903_gemini_38_rollout/004_no_change_inventory.md similarity index 100% rename from devlog/_plan/260903_gemini_38_rollout/004_no_change_inventory.md rename to devlog/_fin/260903_gemini_38_rollout/004_no_change_inventory.md diff --git a/devlog/_plan/260903_gemini_38_rollout/005_audit_round2_synthesis.md b/devlog/_fin/260903_gemini_38_rollout/005_audit_round2_synthesis.md similarity index 100% rename from devlog/_plan/260903_gemini_38_rollout/005_audit_round2_synthesis.md rename to devlog/_fin/260903_gemini_38_rollout/005_audit_round2_synthesis.md diff --git a/devlog/_plan/260903_gemini_38_rollout/006_maintainer_review_fold.md b/devlog/_fin/260903_gemini_38_rollout/006_maintainer_review_fold.md similarity index 100% rename from devlog/_plan/260903_gemini_38_rollout/006_maintainer_review_fold.md rename to devlog/_fin/260903_gemini_38_rollout/006_maintainer_review_fold.md diff --git a/devlog/_plan/260903_gemini_38_rollout/007_closeout.md b/devlog/_fin/260903_gemini_38_rollout/007_closeout.md similarity index 100% rename from devlog/_plan/260903_gemini_38_rollout/007_closeout.md rename to devlog/_fin/260903_gemini_38_rollout/007_closeout.md diff --git a/devlog/_plan/260903_gemini_38_rollout/010_wp1_antigravity_core.md b/devlog/_fin/260903_gemini_38_rollout/010_wp1_antigravity_core.md similarity index 100% rename from devlog/_plan/260903_gemini_38_rollout/010_wp1_antigravity_core.md rename to devlog/_fin/260903_gemini_38_rollout/010_wp1_antigravity_core.md diff --git a/devlog/_plan/260903_gemini_38_rollout/020_wp2_metadata_pricing.md b/devlog/_fin/260903_gemini_38_rollout/020_wp2_metadata_pricing.md similarity index 100% rename from devlog/_plan/260903_gemini_38_rollout/020_wp2_metadata_pricing.md rename to devlog/_fin/260903_gemini_38_rollout/020_wp2_metadata_pricing.md diff --git a/devlog/_plan/260903_gemini_38_rollout/030_wp3_peripheral_surfaces.md b/devlog/_fin/260903_gemini_38_rollout/030_wp3_peripheral_surfaces.md similarity index 100% rename from devlog/_plan/260903_gemini_38_rollout/030_wp3_peripheral_surfaces.md rename to devlog/_fin/260903_gemini_38_rollout/030_wp3_peripheral_surfaces.md diff --git a/devlog/_plan/260903_gemini_38_rollout/040_wp4_delivery.md b/devlog/_fin/260903_gemini_38_rollout/040_wp4_delivery.md similarity index 100% rename from devlog/_plan/260903_gemini_38_rollout/040_wp4_delivery.md rename to devlog/_fin/260903_gemini_38_rollout/040_wp4_delivery.md diff --git a/devlog/_plan/260903_gemini_38_rollout/050_followups.md b/devlog/_fin/260903_gemini_38_rollout/050_followups.md similarity index 100% rename from devlog/_plan/260903_gemini_38_rollout/050_followups.md rename to devlog/_fin/260903_gemini_38_rollout/050_followups.md diff --git a/devlog/_plan/260903_muse_provider_parity/000_plan.md b/devlog/_fin/260903_muse_provider_parity/000_plan.md similarity index 100% rename from devlog/_plan/260903_muse_provider_parity/000_plan.md rename to devlog/_fin/260903_muse_provider_parity/000_plan.md diff --git a/devlog/_plan/260903_muse_provider_parity/001_parity_inventory.md b/devlog/_fin/260903_muse_provider_parity/001_parity_inventory.md similarity index 100% rename from devlog/_plan/260903_muse_provider_parity/001_parity_inventory.md rename to devlog/_fin/260903_muse_provider_parity/001_parity_inventory.md diff --git a/devlog/_plan/260903_muse_provider_parity/010_wp1_passive_quota_core.md b/devlog/_fin/260903_muse_provider_parity/010_wp1_passive_quota_core.md similarity index 100% rename from devlog/_plan/260903_muse_provider_parity/010_wp1_passive_quota_core.md rename to devlog/_fin/260903_muse_provider_parity/010_wp1_passive_quota_core.md diff --git a/devlog/_plan/260903_muse_provider_parity/020_wp2_observation_age_ui.md b/devlog/_fin/260903_muse_provider_parity/020_wp2_observation_age_ui.md similarity index 100% rename from devlog/_plan/260903_muse_provider_parity/020_wp2_observation_age_ui.md rename to devlog/_fin/260903_muse_provider_parity/020_wp2_observation_age_ui.md diff --git a/devlog/_plan/260903_muse_provider_parity/030_wp3_parity_closeout.md b/devlog/_fin/260903_muse_provider_parity/030_wp3_parity_closeout.md similarity index 100% rename from devlog/_plan/260903_muse_provider_parity/030_wp3_parity_closeout.md rename to devlog/_fin/260903_muse_provider_parity/030_wp3_parity_closeout.md diff --git a/devlog/_plan/260903_muse_provider_parity/031_wp3_disposition_record.md b/devlog/_fin/260903_muse_provider_parity/031_wp3_disposition_record.md similarity index 100% rename from devlog/_plan/260903_muse_provider_parity/031_wp3_disposition_record.md rename to devlog/_fin/260903_muse_provider_parity/031_wp3_disposition_record.md diff --git a/devlog/_plan/260903_muse_provider_parity/040_wp4_provider_level_quota.md b/devlog/_fin/260903_muse_provider_parity/040_wp4_provider_level_quota.md similarity index 100% rename from devlog/_plan/260903_muse_provider_parity/040_wp4_provider_level_quota.md rename to devlog/_fin/260903_muse_provider_parity/040_wp4_provider_level_quota.md diff --git a/devlog/_plan/260903_muse_provider_parity/assets/021_observed_age_render.png b/devlog/_fin/260903_muse_provider_parity/assets/021_observed_age_render.png similarity index 100% rename from devlog/_plan/260903_muse_provider_parity/assets/021_observed_age_render.png rename to devlog/_fin/260903_muse_provider_parity/assets/021_observed_age_render.png diff --git a/devlog/_plan/260903_muse_release_train/000_plan.md b/devlog/_fin/260903_muse_release_train/000_plan.md similarity index 100% rename from devlog/_plan/260903_muse_release_train/000_plan.md rename to devlog/_fin/260903_muse_release_train/000_plan.md diff --git a/devlog/_plan/260903_muse_release_train/005_audit_round1_synthesis.md b/devlog/_fin/260903_muse_release_train/005_audit_round1_synthesis.md similarity index 100% rename from devlog/_plan/260903_muse_release_train/005_audit_round1_synthesis.md rename to devlog/_fin/260903_muse_release_train/005_audit_round1_synthesis.md diff --git a/devlog/_plan/260903_muse_release_train/010_wp1_regression_review.md b/devlog/_fin/260903_muse_release_train/010_wp1_regression_review.md similarity index 100% rename from devlog/_plan/260903_muse_release_train/010_wp1_regression_review.md rename to devlog/_fin/260903_muse_release_train/010_wp1_regression_review.md diff --git a/devlog/_plan/260903_muse_release_train/011_review_ledger.md b/devlog/_fin/260903_muse_release_train/011_review_ledger.md similarity index 100% rename from devlog/_plan/260903_muse_release_train/011_review_ledger.md rename to devlog/_fin/260903_muse_release_train/011_review_ledger.md diff --git a/devlog/_plan/260903_muse_release_train/020_wp2_muse_mark.md b/devlog/_fin/260903_muse_release_train/020_wp2_muse_mark.md similarity index 100% rename from devlog/_plan/260903_muse_release_train/020_wp2_muse_mark.md rename to devlog/_fin/260903_muse_release_train/020_wp2_muse_mark.md diff --git a/devlog/_plan/260903_muse_release_train/021_meta_mark_screenshot.png b/devlog/_fin/260903_muse_release_train/021_meta_mark_screenshot.png similarity index 100% rename from devlog/_plan/260903_muse_release_train/021_meta_mark_screenshot.png rename to devlog/_fin/260903_muse_release_train/021_meta_mark_screenshot.png diff --git a/devlog/_plan/260903_muse_release_train/030_wp3_preview_release.md b/devlog/_fin/260903_muse_release_train/030_wp3_preview_release.md similarity index 100% rename from devlog/_plan/260903_muse_release_train/030_wp3_preview_release.md rename to devlog/_fin/260903_muse_release_train/030_wp3_preview_release.md diff --git a/devlog/_plan/260903_muse_release_train/040_wp4_main_release.md b/devlog/_fin/260903_muse_release_train/040_wp4_main_release.md similarity index 100% rename from devlog/_plan/260903_muse_release_train/040_wp4_main_release.md rename to devlog/_fin/260903_muse_release_train/040_wp4_main_release.md diff --git a/devlog/_plan/260903_muse_release_train/050_followups.md b/devlog/_fin/260903_muse_release_train/050_followups.md similarity index 100% rename from devlog/_plan/260903_muse_release_train/050_followups.md rename to devlog/_fin/260903_muse_release_train/050_followups.md diff --git a/devlog/_plan/260903_muse_spark_plan_oauth/000_plan.md b/devlog/_fin/260903_muse_spark_plan_oauth/000_plan.md similarity index 100% rename from devlog/_plan/260903_muse_spark_plan_oauth/000_plan.md rename to devlog/_fin/260903_muse_spark_plan_oauth/000_plan.md diff --git a/devlog/_plan/260903_muse_spark_plan_oauth/001_vendor_claim_ledger.md b/devlog/_fin/260903_muse_spark_plan_oauth/001_vendor_claim_ledger.md similarity index 100% rename from devlog/_plan/260903_muse_spark_plan_oauth/001_vendor_claim_ledger.md rename to devlog/_fin/260903_muse_spark_plan_oauth/001_vendor_claim_ledger.md diff --git a/devlog/_plan/260903_muse_spark_plan_oauth/002_plan_credential_feasibility.md b/devlog/_fin/260903_muse_spark_plan_oauth/002_plan_credential_feasibility.md similarity index 100% rename from devlog/_plan/260903_muse_spark_plan_oauth/002_plan_credential_feasibility.md rename to devlog/_fin/260903_muse_spark_plan_oauth/002_plan_credential_feasibility.md diff --git a/devlog/_plan/260903_muse_spark_plan_oauth/003_credential_and_quota_measurements.md b/devlog/_fin/260903_muse_spark_plan_oauth/003_credential_and_quota_measurements.md similarity index 100% rename from devlog/_plan/260903_muse_spark_plan_oauth/003_credential_and_quota_measurements.md rename to devlog/_fin/260903_muse_spark_plan_oauth/003_credential_and_quota_measurements.md diff --git a/devlog/_plan/260903_muse_spark_plan_oauth/004_muse_quota_emission_questions.md b/devlog/_fin/260903_muse_spark_plan_oauth/004_muse_quota_emission_questions.md similarity index 100% rename from devlog/_plan/260903_muse_spark_plan_oauth/004_muse_quota_emission_questions.md rename to devlog/_fin/260903_muse_spark_plan_oauth/004_muse_quota_emission_questions.md diff --git a/devlog/_plan/260903_muse_spark_plan_oauth/010_wp1_direct_provider.md b/devlog/_fin/260903_muse_spark_plan_oauth/010_wp1_direct_provider.md similarity index 100% rename from devlog/_plan/260903_muse_spark_plan_oauth/010_wp1_direct_provider.md rename to devlog/_fin/260903_muse_spark_plan_oauth/010_wp1_direct_provider.md diff --git a/devlog/_plan/260903_muse_spark_plan_oauth/020_wp2_device_oauth.md b/devlog/_fin/260903_muse_spark_plan_oauth/020_wp2_device_oauth.md similarity index 100% rename from devlog/_plan/260903_muse_spark_plan_oauth/020_wp2_device_oauth.md rename to devlog/_fin/260903_muse_spark_plan_oauth/020_wp2_device_oauth.md diff --git a/devlog/_plan/260903_muse_spark_plan_oauth/030_wp3_delivery.md b/devlog/_fin/260903_muse_spark_plan_oauth/030_wp3_delivery.md similarity index 100% rename from devlog/_plan/260903_muse_spark_plan_oauth/030_wp3_delivery.md rename to devlog/_fin/260903_muse_spark_plan_oauth/030_wp3_delivery.md diff --git a/devlog/_plan/260903_muse_spark_plan_oauth/040_wp4_muse_oauth_provider.md b/devlog/_fin/260903_muse_spark_plan_oauth/040_wp4_muse_oauth_provider.md similarity index 100% rename from devlog/_plan/260903_muse_spark_plan_oauth/040_wp4_muse_oauth_provider.md rename to devlog/_fin/260903_muse_spark_plan_oauth/040_wp4_muse_oauth_provider.md diff --git a/devlog/_plan/260903_muse_spark_plan_oauth/050_wp5_passive_muse_quota.md b/devlog/_fin/260903_muse_spark_plan_oauth/050_wp5_passive_muse_quota.md similarity index 100% rename from devlog/_plan/260903_muse_spark_plan_oauth/050_wp5_passive_muse_quota.md rename to devlog/_fin/260903_muse_spark_plan_oauth/050_wp5_passive_muse_quota.md diff --git a/devlog/_plan/260903_responses_passthrough/000_research.md b/devlog/_fin/260903_responses_passthrough/000_research.md similarity index 100% rename from devlog/_plan/260903_responses_passthrough/000_research.md rename to devlog/_fin/260903_responses_passthrough/000_research.md diff --git a/devlog/_plan/260903_responses_passthrough/010_wp2_raw_usage_bridge.md b/devlog/_fin/260903_responses_passthrough/010_wp2_raw_usage_bridge.md similarity index 100% rename from devlog/_plan/260903_responses_passthrough/010_wp2_raw_usage_bridge.md rename to devlog/_fin/260903_responses_passthrough/010_wp2_raw_usage_bridge.md diff --git a/devlog/_plan/260903_responses_passthrough/020_wp3_coverage.md b/devlog/_fin/260903_responses_passthrough/020_wp3_coverage.md similarity index 100% rename from devlog/_plan/260903_responses_passthrough/020_wp3_coverage.md rename to devlog/_fin/260903_responses_passthrough/020_wp3_coverage.md diff --git a/devlog/_plan/260903_responses_passthrough/030_wp4_prs.md b/devlog/_fin/260903_responses_passthrough/030_wp4_prs.md similarity index 100% rename from devlog/_plan/260903_responses_passthrough/030_wp4_prs.md rename to devlog/_fin/260903_responses_passthrough/030_wp4_prs.md diff --git a/devlog/_plan/260903_voice_sideband_regression/000_research.md b/devlog/_fin/260903_voice_sideband_regression/000_research.md similarity index 100% rename from devlog/_plan/260903_voice_sideband_regression/000_research.md rename to devlog/_fin/260903_voice_sideband_regression/000_research.md diff --git a/devlog/_plan/260903_voice_sideband_regression/010_wp2_inject_realtime_ws_override.md b/devlog/_fin/260903_voice_sideband_regression/010_wp2_inject_realtime_ws_override.md similarity index 100% rename from devlog/_plan/260903_voice_sideband_regression/010_wp2_inject_realtime_ws_override.md rename to devlog/_fin/260903_voice_sideband_regression/010_wp2_inject_realtime_ws_override.md diff --git a/devlog/_plan/260903_voice_sideband_regression/020_wp3_proxy_affinity_probe.md b/devlog/_fin/260903_voice_sideband_regression/020_wp3_proxy_affinity_probe.md similarity index 100% rename from devlog/_plan/260903_voice_sideband_regression/020_wp3_proxy_affinity_probe.md rename to devlog/_fin/260903_voice_sideband_regression/020_wp3_proxy_affinity_probe.md diff --git a/devlog/_plan/260903_voice_sideband_regression/021_wp3_probe_transcript.md b/devlog/_fin/260903_voice_sideband_regression/021_wp3_probe_transcript.md similarity index 100% rename from devlog/_plan/260903_voice_sideband_regression/021_wp3_probe_transcript.md rename to devlog/_fin/260903_voice_sideband_regression/021_wp3_probe_transcript.md diff --git a/devlog/_plan/260903_voice_sideband_regression/030_wp4_docs_pr.md b/devlog/_fin/260903_voice_sideband_regression/030_wp4_docs_pr.md similarity index 100% rename from devlog/_plan/260903_voice_sideband_regression/030_wp4_docs_pr.md rename to devlog/_fin/260903_voice_sideband_regression/030_wp4_docs_pr.md diff --git a/devlog/_plan/260903_voice_sideband_regression/040_d_record.md b/devlog/_fin/260903_voice_sideband_regression/040_d_record.md similarity index 100% rename from devlog/_plan/260903_voice_sideband_regression/040_d_record.md rename to devlog/_fin/260903_voice_sideband_regression/040_d_record.md diff --git a/devlog/_plan/260904_anthropic_effort_ladder/000_research.md b/devlog/_fin/260904_anthropic_effort_ladder/000_research.md similarity index 100% rename from devlog/_plan/260904_anthropic_effort_ladder/000_research.md rename to devlog/_fin/260904_anthropic_effort_ladder/000_research.md diff --git a/devlog/_plan/260904_anthropic_effort_ladder/010_registry_ladder.md b/devlog/_fin/260904_anthropic_effort_ladder/010_registry_ladder.md similarity index 100% rename from devlog/_plan/260904_anthropic_effort_ladder/010_registry_ladder.md rename to devlog/_fin/260904_anthropic_effort_ladder/010_registry_ladder.md diff --git a/devlog/_plan/260904_anthropic_effort_ladder/020_verification_and_pr.md b/devlog/_fin/260904_anthropic_effort_ladder/020_verification_and_pr.md similarity index 100% rename from devlog/_plan/260904_anthropic_effort_ladder/020_verification_and_pr.md rename to devlog/_fin/260904_anthropic_effort_ladder/020_verification_and_pr.md diff --git a/devlog/_plan/260904_anthropic_effort_ladder/030_related_empty_ladders.md b/devlog/_fin/260904_anthropic_effort_ladder/030_related_empty_ladders.md similarity index 100% rename from devlog/_plan/260904_anthropic_effort_ladder/030_related_empty_ladders.md rename to devlog/_fin/260904_anthropic_effort_ladder/030_related_empty_ladders.md diff --git a/devlog/_plan/260904_astra_release_alignment/000_research.md b/devlog/_fin/260904_astra_release_alignment/000_research.md similarity index 100% rename from devlog/_plan/260904_astra_release_alignment/000_research.md rename to devlog/_fin/260904_astra_release_alignment/000_research.md diff --git a/devlog/_plan/260904_astra_release_alignment/010_wp2_catalog_alignment.md b/devlog/_fin/260904_astra_release_alignment/010_wp2_catalog_alignment.md similarity index 100% rename from devlog/_plan/260904_astra_release_alignment/010_wp2_catalog_alignment.md rename to devlog/_fin/260904_astra_release_alignment/010_wp2_catalog_alignment.md diff --git a/devlog/_plan/260904_astra_release_alignment/015_audit_synthesis.md b/devlog/_fin/260904_astra_release_alignment/015_audit_synthesis.md similarity index 100% rename from devlog/_plan/260904_astra_release_alignment/015_audit_synthesis.md rename to devlog/_fin/260904_astra_release_alignment/015_audit_synthesis.md diff --git a/devlog/_plan/260904_astra_release_alignment/020_wp3_adapter_eof.md b/devlog/_fin/260904_astra_release_alignment/020_wp3_adapter_eof.md similarity index 100% rename from devlog/_plan/260904_astra_release_alignment/020_wp3_adapter_eof.md rename to devlog/_fin/260904_astra_release_alignment/020_wp3_adapter_eof.md diff --git a/devlog/_plan/260904_astra_release_alignment/021_wp3_evidence.md b/devlog/_fin/260904_astra_release_alignment/021_wp3_evidence.md similarity index 100% rename from devlog/_plan/260904_astra_release_alignment/021_wp3_evidence.md rename to devlog/_fin/260904_astra_release_alignment/021_wp3_evidence.md diff --git a/devlog/_plan/260904_astra_release_alignment/030_wp4_merge.md b/devlog/_fin/260904_astra_release_alignment/030_wp4_merge.md similarity index 100% rename from devlog/_plan/260904_astra_release_alignment/030_wp4_merge.md rename to devlog/_fin/260904_astra_release_alignment/030_wp4_merge.md diff --git a/devlog/_plan/260904_bug_backlog_closeout/000_research.md b/devlog/_fin/260904_bug_backlog_closeout/000_research.md similarity index 100% rename from devlog/_plan/260904_bug_backlog_closeout/000_research.md rename to devlog/_fin/260904_bug_backlog_closeout/000_research.md diff --git a/devlog/_plan/260904_bug_backlog_closeout/010_wp2_green_merge_train.md b/devlog/_fin/260904_bug_backlog_closeout/010_wp2_green_merge_train.md similarity index 100% rename from devlog/_plan/260904_bug_backlog_closeout/010_wp2_green_merge_train.md rename to devlog/_fin/260904_bug_backlog_closeout/010_wp2_green_merge_train.md diff --git a/devlog/_plan/260904_bug_backlog_closeout/020_wp3_draft_pr_triage.md b/devlog/_fin/260904_bug_backlog_closeout/020_wp3_draft_pr_triage.md similarity index 100% rename from devlog/_plan/260904_bug_backlog_closeout/020_wp3_draft_pr_triage.md rename to devlog/_fin/260904_bug_backlog_closeout/020_wp3_draft_pr_triage.md diff --git a/devlog/_plan/260904_bug_backlog_closeout/030_wp4_account_pool.md b/devlog/_fin/260904_bug_backlog_closeout/030_wp4_account_pool.md similarity index 100% rename from devlog/_plan/260904_bug_backlog_closeout/030_wp4_account_pool.md rename to devlog/_fin/260904_bug_backlog_closeout/030_wp4_account_pool.md diff --git a/devlog/_plan/260904_bug_backlog_closeout/040_wp5_remaining_issues.md b/devlog/_fin/260904_bug_backlog_closeout/040_wp5_remaining_issues.md similarity index 100% rename from devlog/_plan/260904_bug_backlog_closeout/040_wp5_remaining_issues.md rename to devlog/_fin/260904_bug_backlog_closeout/040_wp5_remaining_issues.md diff --git a/devlog/_plan/260904_bug_backlog_closeout/050_wp6_needs_info.md b/devlog/_fin/260904_bug_backlog_closeout/050_wp6_needs_info.md similarity index 100% rename from devlog/_plan/260904_bug_backlog_closeout/050_wp6_needs_info.md rename to devlog/_fin/260904_bug_backlog_closeout/050_wp6_needs_info.md diff --git a/devlog/_plan/260904_bug_backlog_closeout/060_ledger.md b/devlog/_fin/260904_bug_backlog_closeout/060_ledger.md similarity index 100% rename from devlog/_plan/260904_bug_backlog_closeout/060_ledger.md rename to devlog/_fin/260904_bug_backlog_closeout/060_ledger.md diff --git a/devlog/_plan/260904_bug_backlog_closeout/070_closeout.md b/devlog/_fin/260904_bug_backlog_closeout/070_closeout.md similarity index 100% rename from devlog/_plan/260904_bug_backlog_closeout/070_closeout.md rename to devlog/_fin/260904_bug_backlog_closeout/070_closeout.md diff --git a/devlog/_plan/260904_bug_stack_train/000_research.md b/devlog/_fin/260904_bug_stack_train/000_research.md similarity index 100% rename from devlog/_plan/260904_bug_stack_train/000_research.md rename to devlog/_fin/260904_bug_stack_train/000_research.md diff --git a/devlog/_plan/260904_bug_stack_train/010_wp2_deviceauth_core.md b/devlog/_fin/260904_bug_stack_train/010_wp2_deviceauth_core.md similarity index 100% rename from devlog/_plan/260904_bug_stack_train/010_wp2_deviceauth_core.md rename to devlog/_fin/260904_bug_stack_train/010_wp2_deviceauth_core.md diff --git a/devlog/_plan/260904_bug_stack_train/020_wp3_deviceauth_surface.md b/devlog/_fin/260904_bug_stack_train/020_wp3_deviceauth_surface.md similarity index 100% rename from devlog/_plan/260904_bug_stack_train/020_wp3_deviceauth_surface.md rename to devlog/_fin/260904_bug_stack_train/020_wp3_deviceauth_surface.md diff --git a/devlog/_plan/260904_bug_stack_train/030_wp4_bug_carry.md b/devlog/_fin/260904_bug_stack_train/030_wp4_bug_carry.md similarity index 100% rename from devlog/_plan/260904_bug_stack_train/030_wp4_bug_carry.md rename to devlog/_fin/260904_bug_stack_train/030_wp4_bug_carry.md diff --git a/devlog/_plan/260904_bug_stack_train/040_deferrals.md b/devlog/_fin/260904_bug_stack_train/040_deferrals.md similarity index 100% rename from devlog/_plan/260904_bug_stack_train/040_deferrals.md rename to devlog/_fin/260904_bug_stack_train/040_deferrals.md diff --git a/devlog/_plan/260904_bug_stack_train/050_outcome.md b/devlog/_fin/260904_bug_stack_train/050_outcome.md similarity index 100% rename from devlog/_plan/260904_bug_stack_train/050_outcome.md rename to devlog/_fin/260904_bug_stack_train/050_outcome.md diff --git a/devlog/_plan/260904_bug_stack_train/060_closeout.md b/devlog/_fin/260904_bug_stack_train/060_closeout.md similarity index 100% rename from devlog/_plan/260904_bug_stack_train/060_closeout.md rename to devlog/_fin/260904_bug_stack_train/060_closeout.md diff --git a/devlog/_plan/260904_bug_stack_train/070_issue_dispositions.md b/devlog/_fin/260904_bug_stack_train/070_issue_dispositions.md similarity index 100% rename from devlog/_plan/260904_bug_stack_train/070_issue_dispositions.md rename to devlog/_fin/260904_bug_stack_train/070_issue_dispositions.md diff --git a/devlog/_plan/260904_bug_stack_train/080_merge_ledger.md b/devlog/_fin/260904_bug_stack_train/080_merge_ledger.md similarity index 100% rename from devlog/_plan/260904_bug_stack_train/080_merge_ledger.md rename to devlog/_fin/260904_bug_stack_train/080_merge_ledger.md diff --git a/devlog/_plan/260904_codex_set_head_and_logo/000_research.md b/devlog/_fin/260904_codex_set_head_and_logo/000_research.md similarity index 100% rename from devlog/_plan/260904_codex_set_head_and_logo/000_research.md rename to devlog/_fin/260904_codex_set_head_and_logo/000_research.md diff --git a/devlog/_plan/260904_codex_set_head_and_logo/010_wp1_page_head_wrap.md b/devlog/_fin/260904_codex_set_head_and_logo/010_wp1_page_head_wrap.md similarity index 100% rename from devlog/_plan/260904_codex_set_head_and_logo/010_wp1_page_head_wrap.md rename to devlog/_fin/260904_codex_set_head_and_logo/010_wp1_page_head_wrap.md diff --git a/devlog/_plan/260904_codex_set_head_and_logo/020_wp2_codex_nav_mark.md b/devlog/_fin/260904_codex_set_head_and_logo/020_wp2_codex_nav_mark.md similarity index 100% rename from devlog/_plan/260904_codex_set_head_and_logo/020_wp2_codex_nav_mark.md rename to devlog/_fin/260904_codex_set_head_and_logo/020_wp2_codex_nav_mark.md diff --git a/devlog/_plan/260904_codex_set_head_and_logo/030_live_verification_record.md b/devlog/_fin/260904_codex_set_head_and_logo/030_live_verification_record.md similarity index 100% rename from devlog/_plan/260904_codex_set_head_and_logo/030_live_verification_record.md rename to devlog/_fin/260904_codex_set_head_and_logo/030_live_verification_record.md diff --git a/devlog/_plan/260904_codex_set_head_and_logo/040_wp4_regression_coverage.md b/devlog/_fin/260904_codex_set_head_and_logo/040_wp4_regression_coverage.md similarity index 100% rename from devlog/_plan/260904_codex_set_head_and_logo/040_wp4_regression_coverage.md rename to devlog/_fin/260904_codex_set_head_and_logo/040_wp4_regression_coverage.md diff --git a/devlog/_plan/260904_codex_set_head_and_logo/assets/010_after_850_wrapped.png b/devlog/_fin/260904_codex_set_head_and_logo/assets/010_after_850_wrapped.png similarity index 100% rename from devlog/_plan/260904_codex_set_head_and_logo/assets/010_after_850_wrapped.png rename to devlog/_fin/260904_codex_set_head_and_logo/assets/010_after_850_wrapped.png diff --git a/devlog/_plan/260904_codex_set_head_and_logo/assets/010_before_850_clipped.png b/devlog/_fin/260904_codex_set_head_and_logo/assets/010_before_850_clipped.png similarity index 100% rename from devlog/_plan/260904_codex_set_head_and_logo/assets/010_before_850_clipped.png rename to devlog/_fin/260904_codex_set_head_and_logo/assets/010_before_850_clipped.png diff --git a/devlog/_plan/260904_codex_set_head_and_logo/assets/020_nav_codex_mark_dark.png b/devlog/_fin/260904_codex_set_head_and_logo/assets/020_nav_codex_mark_dark.png similarity index 100% rename from devlog/_plan/260904_codex_set_head_and_logo/assets/020_nav_codex_mark_dark.png rename to devlog/_fin/260904_codex_set_head_and_logo/assets/020_nav_codex_mark_dark.png diff --git a/devlog/_plan/260904_codex_set_head_and_logo/assets/020_nav_codex_mark_light.png b/devlog/_fin/260904_codex_set_head_and_logo/assets/020_nav_codex_mark_light.png similarity index 100% rename from devlog/_plan/260904_codex_set_head_and_logo/assets/020_nav_codex_mark_light.png rename to devlog/_fin/260904_codex_set_head_and_logo/assets/020_nav_codex_mark_light.png diff --git a/devlog/_plan/260904_cross_platform_parity/000_problem_model.md b/devlog/_fin/260904_cross_platform_parity/000_problem_model.md similarity index 100% rename from devlog/_plan/260904_cross_platform_parity/000_problem_model.md rename to devlog/_fin/260904_cross_platform_parity/000_problem_model.md diff --git a/devlog/_plan/260904_cross_platform_parity/001_darwin_surface_inventory.md b/devlog/_fin/260904_cross_platform_parity/001_darwin_surface_inventory.md similarity index 100% rename from devlog/_plan/260904_cross_platform_parity/001_darwin_surface_inventory.md rename to devlog/_fin/260904_cross_platform_parity/001_darwin_surface_inventory.md diff --git a/devlog/_plan/260904_cross_platform_parity/002_muse_cli_storage_measurement.md b/devlog/_fin/260904_cross_platform_parity/002_muse_cli_storage_measurement.md similarity index 100% rename from devlog/_plan/260904_cross_platform_parity/002_muse_cli_storage_measurement.md rename to devlog/_fin/260904_cross_platform_parity/002_muse_cli_storage_measurement.md diff --git a/devlog/_plan/260904_cross_platform_parity/003_issue_3320_root_cause.md b/devlog/_fin/260904_cross_platform_parity/003_issue_3320_root_cause.md similarity index 100% rename from devlog/_plan/260904_cross_platform_parity/003_issue_3320_root_cause.md rename to devlog/_fin/260904_cross_platform_parity/003_issue_3320_root_cause.md diff --git a/devlog/_plan/260904_cross_platform_parity/004_implementation_outcome.md b/devlog/_fin/260904_cross_platform_parity/004_implementation_outcome.md similarity index 100% rename from devlog/_plan/260904_cross_platform_parity/004_implementation_outcome.md rename to devlog/_fin/260904_cross_platform_parity/004_implementation_outcome.md diff --git a/devlog/_plan/260904_cross_platform_parity/010_wp1_muse_manual_key.md b/devlog/_fin/260904_cross_platform_parity/010_wp1_muse_manual_key.md similarity index 100% rename from devlog/_plan/260904_cross_platform_parity/010_wp1_muse_manual_key.md rename to devlog/_fin/260904_cross_platform_parity/010_wp1_muse_manual_key.md diff --git a/devlog/_plan/260904_cross_platform_parity/020_wp2_platform_support_docs.md b/devlog/_fin/260904_cross_platform_parity/020_wp2_platform_support_docs.md similarity index 100% rename from devlog/_plan/260904_cross_platform_parity/020_wp2_platform_support_docs.md rename to devlog/_fin/260904_cross_platform_parity/020_wp2_platform_support_docs.md diff --git a/devlog/_plan/260904_cross_platform_parity/030_wp3_windows_identity_decode.md b/devlog/_fin/260904_cross_platform_parity/030_wp3_windows_identity_decode.md similarity index 100% rename from devlog/_plan/260904_cross_platform_parity/030_wp3_windows_identity_decode.md rename to devlog/_fin/260904_cross_platform_parity/030_wp3_windows_identity_decode.md diff --git a/devlog/_plan/260904_cross_platform_parity/040_wp4_stack_closeout.md b/devlog/_fin/260904_cross_platform_parity/040_wp4_stack_closeout.md similarity index 100% rename from devlog/_plan/260904_cross_platform_parity/040_wp4_stack_closeout.md rename to devlog/_fin/260904_cross_platform_parity/040_wp4_stack_closeout.md diff --git a/devlog/_plan/260904_cross_platform_parity/041_ci_triage.md b/devlog/_fin/260904_cross_platform_parity/041_ci_triage.md similarity index 100% rename from devlog/_plan/260904_cross_platform_parity/041_ci_triage.md rename to devlog/_fin/260904_cross_platform_parity/041_ci_triage.md diff --git a/devlog/_plan/260904_cross_platform_parity/050_followups.md b/devlog/_fin/260904_cross_platform_parity/050_followups.md similarity index 100% rename from devlog/_plan/260904_cross_platform_parity/050_followups.md rename to devlog/_fin/260904_cross_platform_parity/050_followups.md diff --git a/devlog/_plan/260904_dashboard_minimal/000_inventory.md b/devlog/_fin/260904_dashboard_minimal/000_inventory.md similarity index 100% rename from devlog/_plan/260904_dashboard_minimal/000_inventory.md rename to devlog/_fin/260904_dashboard_minimal/000_inventory.md diff --git a/devlog/_plan/260904_dashboard_minimal/001_subagent_opinions.md b/devlog/_fin/260904_dashboard_minimal/001_subagent_opinions.md similarity index 100% rename from devlog/_plan/260904_dashboard_minimal/001_subagent_opinions.md rename to devlog/_fin/260904_dashboard_minimal/001_subagent_opinions.md diff --git a/devlog/_plan/260904_dashboard_minimal/002_decisions.md b/devlog/_fin/260904_dashboard_minimal/002_decisions.md similarity index 100% rename from devlog/_plan/260904_dashboard_minimal/002_decisions.md rename to devlog/_fin/260904_dashboard_minimal/002_decisions.md diff --git a/devlog/_plan/260904_dashboard_minimal/003_audit_record.md b/devlog/_fin/260904_dashboard_minimal/003_audit_record.md similarity index 100% rename from devlog/_plan/260904_dashboard_minimal/003_audit_record.md rename to devlog/_fin/260904_dashboard_minimal/003_audit_record.md diff --git a/devlog/_plan/260904_dashboard_minimal/010_sidebar_footer.md b/devlog/_fin/260904_dashboard_minimal/010_sidebar_footer.md similarity index 100% rename from devlog/_plan/260904_dashboard_minimal/010_sidebar_footer.md rename to devlog/_fin/260904_dashboard_minimal/010_sidebar_footer.md diff --git a/devlog/_plan/260904_dashboard_minimal/020_dashboard_home.md b/devlog/_fin/260904_dashboard_minimal/020_dashboard_home.md similarity index 100% rename from devlog/_plan/260904_dashboard_minimal/020_dashboard_home.md rename to devlog/_fin/260904_dashboard_minimal/020_dashboard_home.md diff --git a/devlog/_plan/260904_dashboard_minimal/030_models_catalog.md b/devlog/_fin/260904_dashboard_minimal/030_models_catalog.md similarity index 100% rename from devlog/_plan/260904_dashboard_minimal/030_models_catalog.md rename to devlog/_fin/260904_dashboard_minimal/030_models_catalog.md diff --git a/devlog/_plan/260904_dashboard_minimal/040_integrations.md b/devlog/_fin/260904_dashboard_minimal/040_integrations.md similarity index 100% rename from devlog/_plan/260904_dashboard_minimal/040_integrations.md rename to devlog/_fin/260904_dashboard_minimal/040_integrations.md diff --git a/devlog/_plan/260904_dashboard_minimal/050_codex_set.md b/devlog/_fin/260904_dashboard_minimal/050_codex_set.md similarity index 100% rename from devlog/_plan/260904_dashboard_minimal/050_codex_set.md rename to devlog/_fin/260904_dashboard_minimal/050_codex_set.md diff --git a/devlog/_plan/260904_dashboard_minimal/060_usage.md b/devlog/_fin/260904_dashboard_minimal/060_usage.md similarity index 100% rename from devlog/_plan/260904_dashboard_minimal/060_usage.md rename to devlog/_fin/260904_dashboard_minimal/060_usage.md diff --git a/devlog/_plan/260904_dashboard_minimal/070_startup.md b/devlog/_fin/260904_dashboard_minimal/070_startup.md similarity index 100% rename from devlog/_plan/260904_dashboard_minimal/070_startup.md rename to devlog/_fin/260904_dashboard_minimal/070_startup.md diff --git a/devlog/_plan/260904_dashboard_minimal/080_page_polish.md b/devlog/_fin/260904_dashboard_minimal/080_page_polish.md similarity index 100% rename from devlog/_plan/260904_dashboard_minimal/080_page_polish.md rename to devlog/_fin/260904_dashboard_minimal/080_page_polish.md diff --git a/devlog/_plan/260904_dashboard_minimal/090_i18n_prune_docs.md b/devlog/_fin/260904_dashboard_minimal/090_i18n_prune_docs.md similarity index 100% rename from devlog/_plan/260904_dashboard_minimal/090_i18n_prune_docs.md rename to devlog/_fin/260904_dashboard_minimal/090_i18n_prune_docs.md diff --git a/devlog/_fin/260904_dashboard_minimal/090_terminal.md b/devlog/_fin/260904_dashboard_minimal/090_terminal.md new file mode 100644 index 0000000000..4101656c12 --- /dev/null +++ b/devlog/_fin/260904_dashboard_minimal/090_terminal.md @@ -0,0 +1,3 @@ +# terminal outcome — abandoned + +ABANDONED: reverted. The minimal GUI pass (#3381) was reverted on origin/dev by #3415 (`0e76b20bd`). diff --git a/devlog/_plan/260904_external_fast_wire/000_plan.md b/devlog/_fin/260904_external_fast_wire/000_plan.md similarity index 100% rename from devlog/_plan/260904_external_fast_wire/000_plan.md rename to devlog/_fin/260904_external_fast_wire/000_plan.md diff --git a/devlog/_plan/260904_external_fast_wire/005_audit_round1.md b/devlog/_fin/260904_external_fast_wire/005_audit_round1.md similarity index 100% rename from devlog/_plan/260904_external_fast_wire/005_audit_round1.md rename to devlog/_fin/260904_external_fast_wire/005_audit_round1.md diff --git a/devlog/_plan/260904_external_fast_wire/006_wp0_receipt.md b/devlog/_fin/260904_external_fast_wire/006_wp0_receipt.md similarity index 100% rename from devlog/_plan/260904_external_fast_wire/006_wp0_receipt.md rename to devlog/_fin/260904_external_fast_wire/006_wp0_receipt.md diff --git a/devlog/_plan/260904_external_fast_wire/010_wp1_fast_row_core.md b/devlog/_fin/260904_external_fast_wire/010_wp1_fast_row_core.md similarity index 100% rename from devlog/_plan/260904_external_fast_wire/010_wp1_fast_row_core.md rename to devlog/_fin/260904_external_fast_wire/010_wp1_fast_row_core.md diff --git a/devlog/_plan/260904_external_fast_wire/020_wp2_listing.md b/devlog/_fin/260904_external_fast_wire/020_wp2_listing.md similarity index 100% rename from devlog/_plan/260904_external_fast_wire/020_wp2_listing.md rename to devlog/_fin/260904_external_fast_wire/020_wp2_listing.md diff --git a/devlog/_plan/260904_external_fast_wire/030_wp3_ingress.md b/devlog/_fin/260904_external_fast_wire/030_wp3_ingress.md similarity index 100% rename from devlog/_plan/260904_external_fast_wire/030_wp3_ingress.md rename to devlog/_fin/260904_external_fast_wire/030_wp3_ingress.md diff --git a/devlog/_plan/260904_external_fast_wire/040_wp4_docs_and_landing.md b/devlog/_fin/260904_external_fast_wire/040_wp4_docs_and_landing.md similarity index 100% rename from devlog/_plan/260904_external_fast_wire/040_wp4_docs_and_landing.md rename to devlog/_fin/260904_external_fast_wire/040_wp4_docs_and_landing.md diff --git a/devlog/_plan/260904_external_fast_wire/050_outcome.md b/devlog/_fin/260904_external_fast_wire/050_outcome.md similarity index 100% rename from devlog/_plan/260904_external_fast_wire/050_outcome.md rename to devlog/_fin/260904_external_fast_wire/050_outcome.md diff --git a/devlog/_plan/260904_gated_client_version_floor/000_research.md b/devlog/_fin/260904_gated_client_version_floor/000_research.md similarity index 100% rename from devlog/_plan/260904_gated_client_version_floor/000_research.md rename to devlog/_fin/260904_gated_client_version_floor/000_research.md diff --git a/devlog/_plan/260904_gated_client_version_floor/005_audit_synthesis.md b/devlog/_fin/260904_gated_client_version_floor/005_audit_synthesis.md similarity index 100% rename from devlog/_plan/260904_gated_client_version_floor/005_audit_synthesis.md rename to devlog/_fin/260904_gated_client_version_floor/005_audit_synthesis.md diff --git a/devlog/_plan/260904_gated_client_version_floor/010_wp2_floor_aware_tier2.md b/devlog/_fin/260904_gated_client_version_floor/010_wp2_floor_aware_tier2.md similarity index 100% rename from devlog/_plan/260904_gated_client_version_floor/010_wp2_floor_aware_tier2.md rename to devlog/_fin/260904_gated_client_version_floor/010_wp2_floor_aware_tier2.md diff --git a/devlog/_plan/260904_gated_client_version_floor/020_wp3_projection_verification.md b/devlog/_fin/260904_gated_client_version_floor/020_wp3_projection_verification.md similarity index 100% rename from devlog/_plan/260904_gated_client_version_floor/020_wp3_projection_verification.md rename to devlog/_fin/260904_gated_client_version_floor/020_wp3_projection_verification.md diff --git a/devlog/_plan/260904_gated_client_version_floor/030_wp4_landing.md b/devlog/_fin/260904_gated_client_version_floor/030_wp4_landing.md similarity index 100% rename from devlog/_plan/260904_gated_client_version_floor/030_wp4_landing.md rename to devlog/_fin/260904_gated_client_version_floor/030_wp4_landing.md diff --git a/devlog/_plan/260904_gated_client_version_floor/070_outcome.md b/devlog/_fin/260904_gated_client_version_floor/070_outcome.md similarity index 100% rename from devlog/_plan/260904_gated_client_version_floor/070_outcome.md rename to devlog/_fin/260904_gated_client_version_floor/070_outcome.md diff --git a/devlog/_plan/260904_gpt6_astra_prereg/000_plan.md b/devlog/_fin/260904_gpt6_astra_prereg/000_plan.md similarity index 100% rename from devlog/_plan/260904_gpt6_astra_prereg/000_plan.md rename to devlog/_fin/260904_gpt6_astra_prereg/000_plan.md diff --git a/devlog/_plan/260904_logs_cost_effort_polish/000_research.md b/devlog/_fin/260904_logs_cost_effort_polish/000_research.md similarity index 100% rename from devlog/_plan/260904_logs_cost_effort_polish/000_research.md rename to devlog/_fin/260904_logs_cost_effort_polish/000_research.md diff --git a/devlog/_plan/260904_logs_cost_effort_polish/010_wp1_plan.md b/devlog/_fin/260904_logs_cost_effort_polish/010_wp1_plan.md similarity index 100% rename from devlog/_plan/260904_logs_cost_effort_polish/010_wp1_plan.md rename to devlog/_fin/260904_logs_cost_effort_polish/010_wp1_plan.md diff --git a/devlog/_plan/260904_logs_cost_effort_polish/assets/020_logs_ko_after.png b/devlog/_fin/260904_logs_cost_effort_polish/assets/020_logs_ko_after.png similarity index 100% rename from devlog/_plan/260904_logs_cost_effort_polish/assets/020_logs_ko_after.png rename to devlog/_fin/260904_logs_cost_effort_polish/assets/020_logs_ko_after.png diff --git a/devlog/_plan/260904_main_card_badge_parity/000_evidence.md b/devlog/_fin/260904_main_card_badge_parity/000_evidence.md similarity index 100% rename from devlog/_plan/260904_main_card_badge_parity/000_evidence.md rename to devlog/_fin/260904_main_card_badge_parity/000_evidence.md diff --git a/devlog/_plan/260904_main_card_badge_parity/010_server_dto_parity.md b/devlog/_fin/260904_main_card_badge_parity/010_server_dto_parity.md similarity index 100% rename from devlog/_plan/260904_main_card_badge_parity/010_server_dto_parity.md rename to devlog/_fin/260904_main_card_badge_parity/010_server_dto_parity.md diff --git a/devlog/_plan/260904_main_card_badge_parity/020_gui_plan_badge.md b/devlog/_fin/260904_main_card_badge_parity/020_gui_plan_badge.md similarity index 100% rename from devlog/_plan/260904_main_card_badge_parity/020_gui_plan_badge.md rename to devlog/_fin/260904_main_card_badge_parity/020_gui_plan_badge.md diff --git a/devlog/_plan/260904_main_card_badge_parity/030_verification_and_pr.md b/devlog/_fin/260904_main_card_badge_parity/030_verification_and_pr.md similarity index 100% rename from devlog/_plan/260904_main_card_badge_parity/030_verification_and_pr.md rename to devlog/_fin/260904_main_card_badge_parity/030_verification_and_pr.md diff --git a/devlog/_plan/260904_main_card_badge_parity/040_promotion_and_release.md b/devlog/_fin/260904_main_card_badge_parity/040_promotion_and_release.md similarity index 100% rename from devlog/_plan/260904_main_card_badge_parity/040_promotion_and_release.md rename to devlog/_fin/260904_main_card_badge_parity/040_promotion_and_release.md diff --git a/devlog/_plan/260904_main_card_badge_parity/evidence/main-card-badges.png b/devlog/_fin/260904_main_card_badge_parity/evidence/main-card-badges.png similarity index 100% rename from devlog/_plan/260904_main_card_badge_parity/evidence/main-card-badges.png rename to devlog/_fin/260904_main_card_badge_parity/evidence/main-card-badges.png diff --git a/devlog/_plan/260904_priority65_closeout/000_research.md b/devlog/_fin/260904_priority65_closeout/000_research.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/000_research.md rename to devlog/_fin/260904_priority65_closeout/000_research.md diff --git a/devlog/_plan/260904_priority65_closeout/010_wp2_responses_boundary.md b/devlog/_fin/260904_priority65_closeout/010_wp2_responses_boundary.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/010_wp2_responses_boundary.md rename to devlog/_fin/260904_priority65_closeout/010_wp2_responses_boundary.md diff --git a/devlog/_plan/260904_priority65_closeout/020_wp3_combo_metadata_carry.md b/devlog/_fin/260904_priority65_closeout/020_wp3_combo_metadata_carry.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/020_wp3_combo_metadata_carry.md rename to devlog/_fin/260904_priority65_closeout/020_wp3_combo_metadata_carry.md diff --git a/devlog/_plan/260904_priority65_closeout/030_wp4_combo_context_cap.md b/devlog/_fin/260904_priority65_closeout/030_wp4_combo_context_cap.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/030_wp4_combo_context_cap.md rename to devlog/_fin/260904_priority65_closeout/030_wp4_combo_context_cap.md diff --git a/devlog/_plan/260904_priority65_closeout/040_wp5_reset_credit_identity.md b/devlog/_fin/260904_priority65_closeout/040_wp5_reset_credit_identity.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/040_wp5_reset_credit_identity.md rename to devlog/_fin/260904_priority65_closeout/040_wp5_reset_credit_identity.md diff --git a/devlog/_plan/260904_priority65_closeout/050_wp6_gate_unblock.md b/devlog/_fin/260904_priority65_closeout/050_wp6_gate_unblock.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/050_wp6_gate_unblock.md rename to devlog/_fin/260904_priority65_closeout/050_wp6_gate_unblock.md diff --git a/devlog/_plan/260904_priority65_closeout/060_wp7_rollback_journal_crud.md b/devlog/_fin/260904_priority65_closeout/060_wp7_rollback_journal_crud.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/060_wp7_rollback_journal_crud.md rename to devlog/_fin/260904_priority65_closeout/060_wp7_rollback_journal_crud.md diff --git a/devlog/_plan/260904_priority65_closeout/070_wp8_dispositions_and_regression.md b/devlog/_fin/260904_priority65_closeout/070_wp8_dispositions_and_regression.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/070_wp8_dispositions_and_regression.md rename to devlog/_fin/260904_priority65_closeout/070_wp8_dispositions_and_regression.md diff --git a/devlog/_plan/260904_priority65_closeout/080_outcome.md b/devlog/_fin/260904_priority65_closeout/080_outcome.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/080_outcome.md rename to devlog/_fin/260904_priority65_closeout/080_outcome.md diff --git a/devlog/_plan/260904_priority65_closeout/081_outcome_wp4.md b/devlog/_fin/260904_priority65_closeout/081_outcome_wp4.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/081_outcome_wp4.md rename to devlog/_fin/260904_priority65_closeout/081_outcome_wp4.md diff --git a/devlog/_plan/260904_priority65_closeout/082_outcome_wp5.md b/devlog/_fin/260904_priority65_closeout/082_outcome_wp5.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/082_outcome_wp5.md rename to devlog/_fin/260904_priority65_closeout/082_outcome_wp5.md diff --git a/devlog/_plan/260904_priority65_closeout/083_outcome_wp6.md b/devlog/_fin/260904_priority65_closeout/083_outcome_wp6.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/083_outcome_wp6.md rename to devlog/_fin/260904_priority65_closeout/083_outcome_wp6.md diff --git a/devlog/_plan/260904_priority65_closeout/084_outcome_wp7.md b/devlog/_fin/260904_priority65_closeout/084_outcome_wp7.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/084_outcome_wp7.md rename to devlog/_fin/260904_priority65_closeout/084_outcome_wp7.md diff --git a/devlog/_plan/260904_priority65_closeout/085_outcome_wp8.md b/devlog/_fin/260904_priority65_closeout/085_outcome_wp8.md similarity index 100% rename from devlog/_plan/260904_priority65_closeout/085_outcome_wp8.md rename to devlog/_fin/260904_priority65_closeout/085_outcome_wp8.md diff --git a/devlog/_plan/260904_provider_quota_refresh/000_plan.md b/devlog/_fin/260904_provider_quota_refresh/000_plan.md similarity index 100% rename from devlog/_plan/260904_provider_quota_refresh/000_plan.md rename to devlog/_fin/260904_provider_quota_refresh/000_plan.md diff --git a/devlog/_plan/260904_provider_quota_refresh/010_wp1_passive_quota_visibility.md b/devlog/_fin/260904_provider_quota_refresh/010_wp1_passive_quota_visibility.md similarity index 100% rename from devlog/_plan/260904_provider_quota_refresh/010_wp1_passive_quota_visibility.md rename to devlog/_fin/260904_provider_quota_refresh/010_wp1_passive_quota_visibility.md diff --git a/devlog/_plan/260904_provider_quota_refresh/020_wp2_refresh_affordance.md b/devlog/_fin/260904_provider_quota_refresh/020_wp2_refresh_affordance.md similarity index 100% rename from devlog/_plan/260904_provider_quota_refresh/020_wp2_refresh_affordance.md rename to devlog/_fin/260904_provider_quota_refresh/020_wp2_refresh_affordance.md diff --git a/devlog/_plan/260904_provider_quota_refresh/021_audit_round1_synthesis.md b/devlog/_fin/260904_provider_quota_refresh/021_audit_round1_synthesis.md similarity index 100% rename from devlog/_plan/260904_provider_quota_refresh/021_audit_round1_synthesis.md rename to devlog/_fin/260904_provider_quota_refresh/021_audit_round1_synthesis.md diff --git a/devlog/_plan/260904_provider_quota_refresh/030_wp3_live_verification_and_pr.md b/devlog/_fin/260904_provider_quota_refresh/030_wp3_live_verification_and_pr.md similarity index 100% rename from devlog/_plan/260904_provider_quota_refresh/030_wp3_live_verification_and_pr.md rename to devlog/_fin/260904_provider_quota_refresh/030_wp3_live_verification_and_pr.md diff --git a/devlog/_plan/260904_provider_quota_refresh/031_live_verification_record.md b/devlog/_fin/260904_provider_quota_refresh/031_live_verification_record.md similarity index 100% rename from devlog/_plan/260904_provider_quota_refresh/031_live_verification_record.md rename to devlog/_fin/260904_provider_quota_refresh/031_live_verification_record.md diff --git a/devlog/_plan/260904_providers_home_and_quota_refresh/000_research.md b/devlog/_fin/260904_providers_home_and_quota_refresh/000_research.md similarity index 100% rename from devlog/_plan/260904_providers_home_and_quota_refresh/000_research.md rename to devlog/_fin/260904_providers_home_and_quota_refresh/000_research.md diff --git a/devlog/_plan/260904_providers_home_and_quota_refresh/010_brand_home_affordance.md b/devlog/_fin/260904_providers_home_and_quota_refresh/010_brand_home_affordance.md similarity index 100% rename from devlog/_plan/260904_providers_home_and_quota_refresh/010_brand_home_affordance.md rename to devlog/_fin/260904_providers_home_and_quota_refresh/010_brand_home_affordance.md diff --git a/devlog/_plan/260904_providers_home_and_quota_refresh/020_overview_quota_refresh.md b/devlog/_fin/260904_providers_home_and_quota_refresh/020_overview_quota_refresh.md similarity index 100% rename from devlog/_plan/260904_providers_home_and_quota_refresh/020_overview_quota_refresh.md rename to devlog/_fin/260904_providers_home_and_quota_refresh/020_overview_quota_refresh.md diff --git a/devlog/_plan/260904_providers_home_and_quota_refresh/030_verify_and_land.md b/devlog/_fin/260904_providers_home_and_quota_refresh/030_verify_and_land.md similarity index 100% rename from devlog/_plan/260904_providers_home_and_quota_refresh/030_verify_and_land.md rename to devlog/_fin/260904_providers_home_and_quota_refresh/030_verify_and_land.md diff --git a/devlog/_plan/260904_providers_home_and_quota_refresh/040_delivery_record.md b/devlog/_fin/260904_providers_home_and_quota_refresh/040_delivery_record.md similarity index 100% rename from devlog/_plan/260904_providers_home_and_quota_refresh/040_delivery_record.md rename to devlog/_fin/260904_providers_home_and_quota_refresh/040_delivery_record.md diff --git a/devlog/_plan/260904_providers_home_and_quota_refresh/assets/010_overview_refresh_all_quotas.png b/devlog/_fin/260904_providers_home_and_quota_refresh/assets/010_overview_refresh_all_quotas.png similarity index 100% rename from devlog/_plan/260904_providers_home_and_quota_refresh/assets/010_overview_refresh_all_quotas.png rename to devlog/_fin/260904_providers_home_and_quota_refresh/assets/010_overview_refresh_all_quotas.png diff --git a/devlog/_plan/260904_providers_home_and_quota_refresh/assets/020_brand_home_dashboard.png b/devlog/_fin/260904_providers_home_and_quota_refresh/assets/020_brand_home_dashboard.png similarity index 100% rename from devlog/_plan/260904_providers_home_and_quota_refresh/assets/020_brand_home_dashboard.png rename to devlog/_fin/260904_providers_home_and_quota_refresh/assets/020_brand_home_dashboard.png diff --git a/devlog/_plan/260904_raycast_integration/000_plan.md b/devlog/_fin/260904_raycast_integration/000_plan.md similarity index 100% rename from devlog/_plan/260904_raycast_integration/000_plan.md rename to devlog/_fin/260904_raycast_integration/000_plan.md diff --git a/devlog/_plan/260904_release_version_line/000_research.md b/devlog/_fin/260904_release_version_line/000_research.md similarity index 100% rename from devlog/_plan/260904_release_version_line/000_research.md rename to devlog/_fin/260904_release_version_line/000_research.md diff --git a/devlog/_plan/260904_release_version_line/001_design.md b/devlog/_fin/260904_release_version_line/001_design.md similarity index 100% rename from devlog/_plan/260904_release_version_line/001_design.md rename to devlog/_fin/260904_release_version_line/001_design.md diff --git a/devlog/_plan/260904_release_version_line/010_phase1_version_algebra.md b/devlog/_fin/260904_release_version_line/010_phase1_version_algebra.md similarity index 100% rename from devlog/_plan/260904_release_version_line/010_phase1_version_algebra.md rename to devlog/_fin/260904_release_version_line/010_phase1_version_algebra.md diff --git a/devlog/_plan/260904_release_version_line/020_phase2_bump_input.md b/devlog/_fin/260904_release_version_line/020_phase2_bump_input.md similarity index 100% rename from devlog/_plan/260904_release_version_line/020_phase2_bump_input.md rename to devlog/_fin/260904_release_version_line/020_phase2_bump_input.md diff --git a/devlog/_plan/260904_release_version_line/030_phase3_premove.md b/devlog/_fin/260904_release_version_line/030_phase3_premove.md similarity index 100% rename from devlog/_plan/260904_release_version_line/030_phase3_premove.md rename to devlog/_fin/260904_release_version_line/030_phase3_premove.md diff --git a/devlog/_plan/260904_release_version_line/040_phase4_invariant_and_docs.md b/devlog/_fin/260904_release_version_line/040_phase4_invariant_and_docs.md similarity index 100% rename from devlog/_plan/260904_release_version_line/040_phase4_invariant_and_docs.md rename to devlog/_fin/260904_release_version_line/040_phase4_invariant_and_docs.md diff --git a/devlog/_plan/260904_release_version_line/050_migration.md b/devlog/_fin/260904_release_version_line/050_migration.md similarity index 100% rename from devlog/_plan/260904_release_version_line/050_migration.md rename to devlog/_fin/260904_release_version_line/050_migration.md diff --git a/devlog/_plan/260904_release_version_line/060_rollback_and_failure_modes.md b/devlog/_fin/260904_release_version_line/060_rollback_and_failure_modes.md similarity index 100% rename from devlog/_plan/260904_release_version_line/060_rollback_and_failure_modes.md rename to devlog/_fin/260904_release_version_line/060_rollback_and_failure_modes.md diff --git a/devlog/_plan/260904_release_version_line/070_final_rebase.md b/devlog/_fin/260904_release_version_line/070_final_rebase.md similarity index 100% rename from devlog/_plan/260904_release_version_line/070_final_rebase.md rename to devlog/_fin/260904_release_version_line/070_final_rebase.md diff --git a/devlog/_plan/260904_triage_gap_closure/000_research.md b/devlog/_fin/260904_triage_gap_closure/000_research.md similarity index 100% rename from devlog/_plan/260904_triage_gap_closure/000_research.md rename to devlog/_fin/260904_triage_gap_closure/000_research.md diff --git a/devlog/_plan/260904_triage_gap_closure/010_fable_metadata.md b/devlog/_fin/260904_triage_gap_closure/010_fable_metadata.md similarity index 100% rename from devlog/_plan/260904_triage_gap_closure/010_fable_metadata.md rename to devlog/_fin/260904_triage_gap_closure/010_fable_metadata.md diff --git a/devlog/_plan/260904_triage_gap_closure/020_credits_3284.md b/devlog/_fin/260904_triage_gap_closure/020_credits_3284.md similarity index 100% rename from devlog/_plan/260904_triage_gap_closure/020_credits_3284.md rename to devlog/_fin/260904_triage_gap_closure/020_credits_3284.md diff --git a/devlog/_plan/260904_triage_gap_closure/030_ultrafast_optin.md b/devlog/_fin/260904_triage_gap_closure/030_ultrafast_optin.md similarity index 100% rename from devlog/_plan/260904_triage_gap_closure/030_ultrafast_optin.md rename to devlog/_fin/260904_triage_gap_closure/030_ultrafast_optin.md diff --git a/devlog/_plan/260904_triage_gap_closure/040_delivery_record.md b/devlog/_fin/260904_triage_gap_closure/040_delivery_record.md similarity index 100% rename from devlog/_plan/260904_triage_gap_closure/040_delivery_record.md rename to devlog/_fin/260904_triage_gap_closure/040_delivery_record.md diff --git a/devlog/_plan/260904_triage_gap_closure/assets/010_codex_set_head_relocated.png b/devlog/_fin/260904_triage_gap_closure/assets/010_codex_set_head_relocated.png similarity index 100% rename from devlog/_plan/260904_triage_gap_closure/assets/010_codex_set_head_relocated.png rename to devlog/_fin/260904_triage_gap_closure/assets/010_codex_set_head_relocated.png diff --git a/devlog/_plan/260905_admin_token_local_ux/000_research.md b/devlog/_fin/260905_admin_token_local_ux/000_research.md similarity index 100% rename from devlog/_plan/260905_admin_token_local_ux/000_research.md rename to devlog/_fin/260905_admin_token_local_ux/000_research.md diff --git a/devlog/_plan/260905_admin_token_local_ux/010_suppress_local_prompt.md b/devlog/_fin/260905_admin_token_local_ux/010_suppress_local_prompt.md similarity index 100% rename from devlog/_plan/260905_admin_token_local_ux/010_suppress_local_prompt.md rename to devlog/_fin/260905_admin_token_local_ux/010_suppress_local_prompt.md diff --git a/devlog/_plan/260905_admin_token_local_ux/020_dialog_repair.md b/devlog/_fin/260905_admin_token_local_ux/020_dialog_repair.md similarity index 100% rename from devlog/_plan/260905_admin_token_local_ux/020_dialog_repair.md rename to devlog/_fin/260905_admin_token_local_ux/020_dialog_repair.md diff --git a/devlog/_plan/260905_admin_token_local_ux/030_windows_baseline.md b/devlog/_fin/260905_admin_token_local_ux/030_windows_baseline.md similarity index 100% rename from devlog/_plan/260905_admin_token_local_ux/030_windows_baseline.md rename to devlog/_fin/260905_admin_token_local_ux/030_windows_baseline.md diff --git a/devlog/_plan/260905_admin_token_local_ux/031_preload_guard_ordering.md b/devlog/_fin/260905_admin_token_local_ux/031_preload_guard_ordering.md similarity index 100% rename from devlog/_plan/260905_admin_token_local_ux/031_preload_guard_ordering.md rename to devlog/_fin/260905_admin_token_local_ux/031_preload_guard_ordering.md diff --git a/devlog/_plan/260905_admin_token_local_ux/040_delivery_record.md b/devlog/_fin/260905_admin_token_local_ux/040_delivery_record.md similarity index 100% rename from devlog/_plan/260905_admin_token_local_ux/040_delivery_record.md rename to devlog/_fin/260905_admin_token_local_ux/040_delivery_record.md diff --git a/devlog/_plan/260905_admin_token_local_ux/assets/admin-token-dialog-before-after.png b/devlog/_fin/260905_admin_token_local_ux/assets/admin-token-dialog-before-after.png similarity index 100% rename from devlog/_plan/260905_admin_token_local_ux/assets/admin-token-dialog-before-after.png rename to devlog/_fin/260905_admin_token_local_ux/assets/admin-token-dialog-before-after.png diff --git a/devlog/_plan/260905_admin_token_local_ux/assets/local-session-notice-before-after.png b/devlog/_fin/260905_admin_token_local_ux/assets/local-session-notice-before-after.png similarity index 100% rename from devlog/_plan/260905_admin_token_local_ux/assets/local-session-notice-before-after.png rename to devlog/_fin/260905_admin_token_local_ux/assets/local-session-notice-before-after.png diff --git a/devlog/_plan/260905_apply_patch_envelope_gap/000_survey.md b/devlog/_fin/260905_apply_patch_envelope_gap/000_survey.md similarity index 100% rename from devlog/_plan/260905_apply_patch_envelope_gap/000_survey.md rename to devlog/_fin/260905_apply_patch_envelope_gap/000_survey.md diff --git a/devlog/_plan/260905_apply_patch_envelope_gap/010_disposition.md b/devlog/_fin/260905_apply_patch_envelope_gap/010_disposition.md similarity index 100% rename from devlog/_plan/260905_apply_patch_envelope_gap/010_disposition.md rename to devlog/_fin/260905_apply_patch_envelope_gap/010_disposition.md diff --git a/devlog/_plan/260905_apply_patch_envelope_gap/020_wp2_implementation.md b/devlog/_fin/260905_apply_patch_envelope_gap/020_wp2_implementation.md similarity index 100% rename from devlog/_plan/260905_apply_patch_envelope_gap/020_wp2_implementation.md rename to devlog/_fin/260905_apply_patch_envelope_gap/020_wp2_implementation.md diff --git a/devlog/_plan/260905_apply_patch_envelope_gap/030_review_round.md b/devlog/_fin/260905_apply_patch_envelope_gap/030_review_round.md similarity index 100% rename from devlog/_plan/260905_apply_patch_envelope_gap/030_review_round.md rename to devlog/_fin/260905_apply_patch_envelope_gap/030_review_round.md diff --git a/devlog/_plan/260905_apply_patch_envelope_gap/040_wp1b_parser_guidance.md b/devlog/_fin/260905_apply_patch_envelope_gap/040_wp1b_parser_guidance.md similarity index 100% rename from devlog/_plan/260905_apply_patch_envelope_gap/040_wp1b_parser_guidance.md rename to devlog/_fin/260905_apply_patch_envelope_gap/040_wp1b_parser_guidance.md diff --git a/devlog/_plan/260905_apply_patch_envelope_gap/050_delivery_record.md b/devlog/_fin/260905_apply_patch_envelope_gap/050_delivery_record.md similarity index 100% rename from devlog/_plan/260905_apply_patch_envelope_gap/050_delivery_record.md rename to devlog/_fin/260905_apply_patch_envelope_gap/050_delivery_record.md diff --git a/devlog/_plan/260905_external_image_roundtrip/000_plan.md b/devlog/_fin/260905_external_image_roundtrip/000_plan.md similarity index 100% rename from devlog/_plan/260905_external_image_roundtrip/000_plan.md rename to devlog/_fin/260905_external_image_roundtrip/000_plan.md diff --git a/devlog/_plan/260905_external_image_roundtrip/003_all_format_audit.md b/devlog/_fin/260905_external_image_roundtrip/003_all_format_audit.md similarity index 100% rename from devlog/_plan/260905_external_image_roundtrip/003_all_format_audit.md rename to devlog/_fin/260905_external_image_roundtrip/003_all_format_audit.md diff --git a/devlog/_plan/260905_external_image_roundtrip/010_chat_image_parts.md b/devlog/_fin/260905_external_image_roundtrip/010_chat_image_parts.md similarity index 100% rename from devlog/_plan/260905_external_image_roundtrip/010_chat_image_parts.md rename to devlog/_fin/260905_external_image_roundtrip/010_chat_image_parts.md diff --git a/devlog/_plan/260905_external_image_roundtrip/020_wire_contract.md b/devlog/_fin/260905_external_image_roundtrip/020_wire_contract.md similarity index 100% rename from devlog/_plan/260905_external_image_roundtrip/020_wire_contract.md rename to devlog/_fin/260905_external_image_roundtrip/020_wire_contract.md diff --git a/devlog/_plan/260905_external_image_roundtrip/030_image_input_forms.md b/devlog/_fin/260905_external_image_roundtrip/030_image_input_forms.md similarity index 100% rename from devlog/_plan/260905_external_image_roundtrip/030_image_input_forms.md rename to devlog/_fin/260905_external_image_roundtrip/030_image_input_forms.md diff --git a/devlog/_plan/260905_external_image_roundtrip/040_orphan_image_carriers.md b/devlog/_fin/260905_external_image_roundtrip/040_orphan_image_carriers.md similarity index 100% rename from devlog/_plan/260905_external_image_roundtrip/040_orphan_image_carriers.md rename to devlog/_fin/260905_external_image_roundtrip/040_orphan_image_carriers.md diff --git a/devlog/_plan/260905_external_image_roundtrip/050_cursor_tool_images.md b/devlog/_fin/260905_external_image_roundtrip/050_cursor_tool_images.md similarity index 100% rename from devlog/_plan/260905_external_image_roundtrip/050_cursor_tool_images.md rename to devlog/_fin/260905_external_image_roundtrip/050_cursor_tool_images.md diff --git a/devlog/_plan/260905_external_image_roundtrip/060_delivery.md b/devlog/_fin/260905_external_image_roundtrip/060_delivery.md similarity index 100% rename from devlog/_plan/260905_external_image_roundtrip/060_delivery.md rename to devlog/_fin/260905_external_image_roundtrip/060_delivery.md diff --git a/devlog/_plan/260905_external_image_roundtrip/070_final_head_delivery.md b/devlog/_fin/260905_external_image_roundtrip/070_final_head_delivery.md similarity index 100% rename from devlog/_plan/260905_external_image_roundtrip/070_final_head_delivery.md rename to devlog/_fin/260905_external_image_roundtrip/070_final_head_delivery.md diff --git a/devlog/_fin/260905_external_image_roundtrip/090_closeout.md b/devlog/_fin/260905_external_image_roundtrip/090_closeout.md new file mode 100644 index 0000000000..b612a40671 --- /dev/null +++ b/devlog/_fin/260905_external_image_roundtrip/090_closeout.md @@ -0,0 +1,3 @@ +# closeout (recorded 2026-09-09) + +Landed via #3586/#3589/#3591/#3593/#3595/#3596 (all MERGED; `c514a32c` ancestor of origin/dev). Unit never recorded a post-merge terminal outcome; recorded here during the 260909 post-2.49 reconciliation. diff --git a/devlog/_plan/260905_fast_default_exports/000_plan.md b/devlog/_fin/260905_fast_default_exports/000_plan.md similarity index 100% rename from devlog/_plan/260905_fast_default_exports/000_plan.md rename to devlog/_fin/260905_fast_default_exports/000_plan.md diff --git a/devlog/_plan/260905_fast_default_exports/010_implementation.md b/devlog/_fin/260905_fast_default_exports/010_implementation.md similarity index 100% rename from devlog/_plan/260905_fast_default_exports/010_implementation.md rename to devlog/_fin/260905_fast_default_exports/010_implementation.md diff --git a/devlog/_plan/260905_fast_default_exports/011_review.md b/devlog/_fin/260905_fast_default_exports/011_review.md similarity index 100% rename from devlog/_plan/260905_fast_default_exports/011_review.md rename to devlog/_fin/260905_fast_default_exports/011_review.md diff --git a/devlog/_fin/260905_fast_default_exports/090_closeout.md b/devlog/_fin/260905_fast_default_exports/090_closeout.md new file mode 100644 index 0000000000..1cede7db07 --- /dev/null +++ b/devlog/_fin/260905_fast_default_exports/090_closeout.md @@ -0,0 +1,3 @@ +# closeout (recorded 2026-09-09) + +Landed via #3674 MERGED (`0b7f60ee2` on origin/dev). Unit docs stopped at pre-merge review; terminal outcome recorded here during the 260909 post-2.49 reconciliation. diff --git a/devlog/_plan/260905_grok_responses_default/000_plan.md b/devlog/_fin/260905_grok_responses_default/000_plan.md similarity index 100% rename from devlog/_plan/260905_grok_responses_default/000_plan.md rename to devlog/_fin/260905_grok_responses_default/000_plan.md diff --git a/devlog/_plan/260905_grok_responses_default/002_first_output_rca.md b/devlog/_fin/260905_grok_responses_default/002_first_output_rca.md similarity index 100% rename from devlog/_plan/260905_grok_responses_default/002_first_output_rca.md rename to devlog/_fin/260905_grok_responses_default/002_first_output_rca.md diff --git a/devlog/_plan/260905_grok_responses_default/010_default_and_controls.md b/devlog/_fin/260905_grok_responses_default/010_default_and_controls.md similarity index 100% rename from devlog/_plan/260905_grok_responses_default/010_default_and_controls.md rename to devlog/_fin/260905_grok_responses_default/010_default_and_controls.md diff --git a/devlog/_plan/260905_grok_responses_default/011_verification.md b/devlog/_fin/260905_grok_responses_default/011_verification.md similarity index 100% rename from devlog/_plan/260905_grok_responses_default/011_verification.md rename to devlog/_fin/260905_grok_responses_default/011_verification.md diff --git a/devlog/_fin/260905_grok_responses_default/090_closeout.md b/devlog/_fin/260905_grok_responses_default/090_closeout.md new file mode 100644 index 0000000000..f1ae8e5c8a --- /dev/null +++ b/devlog/_fin/260905_grok_responses_default/090_closeout.md @@ -0,0 +1,3 @@ +# closeout (recorded 2026-09-09) + +Landed via #3670 MERGED 2026-09-05. Unit docs stopped at pending-gate verification; terminal outcome recorded here during the 260909 post-2.49 reconciliation. diff --git a/devlog/_plan/260905_grok_responses_default/assets/001_chat_optin.png b/devlog/_fin/260905_grok_responses_default/assets/001_chat_optin.png similarity index 100% rename from devlog/_plan/260905_grok_responses_default/assets/001_chat_optin.png rename to devlog/_fin/260905_grok_responses_default/assets/001_chat_optin.png diff --git a/devlog/_plan/260905_unified_quota_activation/010_unified_control.md b/devlog/_fin/260905_unified_quota_activation/010_unified_control.md similarity index 100% rename from devlog/_plan/260905_unified_quota_activation/010_unified_control.md rename to devlog/_fin/260905_unified_quota_activation/010_unified_control.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/000_plan.md b/devlog/_fin/260906_lane_b_catalog_stack/000_plan.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/000_plan.md rename to devlog/_fin/260906_lane_b_catalog_stack/000_plan.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/009_roadmap_lock.md b/devlog/_fin/260906_lane_b_catalog_stack/009_roadmap_lock.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/009_roadmap_lock.md rename to devlog/_fin/260906_lane_b_catalog_stack/009_roadmap_lock.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/010_visibility.md b/devlog/_fin/260906_lane_b_catalog_stack/010_visibility.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/010_visibility.md rename to devlog/_fin/260906_lane_b_catalog_stack/010_visibility.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/011_visibility_build.md b/devlog/_fin/260906_lane_b_catalog_stack/011_visibility_build.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/011_visibility_build.md rename to devlog/_fin/260906_lane_b_catalog_stack/011_visibility_build.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/020_context.md b/devlog/_fin/260906_lane_b_catalog_stack/020_context.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/020_context.md rename to devlog/_fin/260906_lane_b_catalog_stack/020_context.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/021_context_build.md b/devlog/_fin/260906_lane_b_catalog_stack/021_context_build.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/021_context_build.md rename to devlog/_fin/260906_lane_b_catalog_stack/021_context_build.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/030_ordering.md b/devlog/_fin/260906_lane_b_catalog_stack/030_ordering.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/030_ordering.md rename to devlog/_fin/260906_lane_b_catalog_stack/030_ordering.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/031_ordering_build.md b/devlog/_fin/260906_lane_b_catalog_stack/031_ordering_build.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/031_ordering_build.md rename to devlog/_fin/260906_lane_b_catalog_stack/031_ordering_build.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/032_ordering_repair.md b/devlog/_fin/260906_lane_b_catalog_stack/032_ordering_repair.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/032_ordering_repair.md rename to devlog/_fin/260906_lane_b_catalog_stack/032_ordering_repair.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/033_ordering_control.md b/devlog/_fin/260906_lane_b_catalog_stack/033_ordering_control.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/033_ordering_control.md rename to devlog/_fin/260906_lane_b_catalog_stack/033_ordering_control.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/034_verification_followup.md b/devlog/_fin/260906_lane_b_catalog_stack/034_verification_followup.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/034_verification_followup.md rename to devlog/_fin/260906_lane_b_catalog_stack/034_verification_followup.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/035_ordering_landing.md b/devlog/_fin/260906_lane_b_catalog_stack/035_ordering_landing.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/035_ordering_landing.md rename to devlog/_fin/260906_lane_b_catalog_stack/035_ordering_landing.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/040_management.md b/devlog/_fin/260906_lane_b_catalog_stack/040_management.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/040_management.md rename to devlog/_fin/260906_lane_b_catalog_stack/040_management.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/041_static_verification.md b/devlog/_fin/260906_lane_b_catalog_stack/041_static_verification.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/041_static_verification.md rename to devlog/_fin/260906_lane_b_catalog_stack/041_static_verification.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/042_management_build.md b/devlog/_fin/260906_lane_b_catalog_stack/042_management_build.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/042_management_build.md rename to devlog/_fin/260906_lane_b_catalog_stack/042_management_build.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/043_carry_boundary.md b/devlog/_fin/260906_lane_b_catalog_stack/043_carry_boundary.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/043_carry_boundary.md rename to devlog/_fin/260906_lane_b_catalog_stack/043_carry_boundary.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/044_react_doctor.md b/devlog/_fin/260906_lane_b_catalog_stack/044_react_doctor.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/044_react_doctor.md rename to devlog/_fin/260906_lane_b_catalog_stack/044_react_doctor.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/050_fable.md b/devlog/_fin/260906_lane_b_catalog_stack/050_fable.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/050_fable.md rename to devlog/_fin/260906_lane_b_catalog_stack/050_fable.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/060_landing.md b/devlog/_fin/260906_lane_b_catalog_stack/060_landing.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/060_landing.md rename to devlog/_fin/260906_lane_b_catalog_stack/060_landing.md diff --git a/devlog/_plan/260906_lane_b_catalog_stack/061_landed.md b/devlog/_fin/260906_lane_b_catalog_stack/061_landed.md similarity index 100% rename from devlog/_plan/260906_lane_b_catalog_stack/061_landed.md rename to devlog/_fin/260906_lane_b_catalog_stack/061_landed.md diff --git a/devlog/_plan/260906_opaque_transport_finality/000_plan.md b/devlog/_fin/260906_opaque_transport_finality/000_plan.md similarity index 100% rename from devlog/_plan/260906_opaque_transport_finality/000_plan.md rename to devlog/_fin/260906_opaque_transport_finality/000_plan.md diff --git a/devlog/_plan/260906_opaque_transport_finality/010_failure_boundaries.md b/devlog/_fin/260906_opaque_transport_finality/010_failure_boundaries.md similarity index 100% rename from devlog/_plan/260906_opaque_transport_finality/010_failure_boundaries.md rename to devlog/_fin/260906_opaque_transport_finality/010_failure_boundaries.md diff --git a/devlog/_fin/260906_opaque_transport_finality/090_closeout.md b/devlog/_fin/260906_opaque_transport_finality/090_closeout.md new file mode 100644 index 0000000000..14e3301678 --- /dev/null +++ b/devlog/_fin/260906_opaque_transport_finality/090_closeout.md @@ -0,0 +1,3 @@ +# closeout (recorded 2026-09-09) + +Landed via #3753/#3754 (both MERGED 2026-09-06). Unit 000 still listed them as open/draft; terminal outcome recorded here during the 260909 post-2.49 reconciliation. diff --git a/devlog/_plan/260906_release_244_publish/000_plan.md b/devlog/_fin/260906_release_244_publish/000_plan.md similarity index 100% rename from devlog/_plan/260906_release_244_publish/000_plan.md rename to devlog/_fin/260906_release_244_publish/000_plan.md diff --git a/devlog/_plan/260906_release_244_publish/010_roadmap.md b/devlog/_fin/260906_release_244_publish/010_roadmap.md similarity index 100% rename from devlog/_plan/260906_release_244_publish/010_roadmap.md rename to devlog/_fin/260906_release_244_publish/010_roadmap.md diff --git a/devlog/_plan/260906_release_244_publish/020_integrate.md b/devlog/_fin/260906_release_244_publish/020_integrate.md similarity index 100% rename from devlog/_plan/260906_release_244_publish/020_integrate.md rename to devlog/_fin/260906_release_244_publish/020_integrate.md diff --git a/devlog/_plan/260906_release_244_publish/030_dev_bump.md b/devlog/_fin/260906_release_244_publish/030_dev_bump.md similarity index 100% rename from devlog/_plan/260906_release_244_publish/030_dev_bump.md rename to devlog/_fin/260906_release_244_publish/030_dev_bump.md diff --git a/devlog/_plan/260906_release_244_publish/040_preview.md b/devlog/_fin/260906_release_244_publish/040_preview.md similarity index 100% rename from devlog/_plan/260906_release_244_publish/040_preview.md rename to devlog/_fin/260906_release_244_publish/040_preview.md diff --git a/devlog/_plan/260906_release_244_publish/050_stable.md b/devlog/_fin/260906_release_244_publish/050_stable.md similarity index 100% rename from devlog/_plan/260906_release_244_publish/050_stable.md rename to devlog/_fin/260906_release_244_publish/050_stable.md diff --git a/devlog/_fin/260906_release_244_publish/090_closeout.md b/devlog/_fin/260906_release_244_publish/090_closeout.md new file mode 100644 index 0000000000..c955060918 --- /dev/null +++ b/devlog/_fin/260906_release_244_publish/090_closeout.md @@ -0,0 +1,3 @@ +# closeout (recorded 2026-09-09) + +v2.44.0 published: #3785 MERGED, `07b48da8f` ancestor of origin/main, tag v2.44.0 exists. Unit never recorded the DONE receipt; terminal outcome recorded here during the 260909 post-2.49 reconciliation. diff --git a/devlog/_plan/260907_code_mode_host_contract/000_plan.md b/devlog/_fin/260907_code_mode_host_contract/000_plan.md similarity index 100% rename from devlog/_plan/260907_code_mode_host_contract/000_plan.md rename to devlog/_fin/260907_code_mode_host_contract/000_plan.md diff --git a/devlog/_plan/260907_code_mode_host_contract/001_host_probe_evidence.md b/devlog/_fin/260907_code_mode_host_contract/001_host_probe_evidence.md similarity index 100% rename from devlog/_plan/260907_code_mode_host_contract/001_host_probe_evidence.md rename to devlog/_fin/260907_code_mode_host_contract/001_host_probe_evidence.md diff --git a/devlog/_plan/260907_code_mode_host_contract/010_pre_call_contract.md b/devlog/_fin/260907_code_mode_host_contract/010_pre_call_contract.md similarity index 100% rename from devlog/_plan/260907_code_mode_host_contract/010_pre_call_contract.md rename to devlog/_fin/260907_code_mode_host_contract/010_pre_call_contract.md diff --git a/devlog/_plan/260907_code_mode_host_contract/020_post_hoc_annotation.md b/devlog/_fin/260907_code_mode_host_contract/020_post_hoc_annotation.md similarity index 100% rename from devlog/_plan/260907_code_mode_host_contract/020_post_hoc_annotation.md rename to devlog/_fin/260907_code_mode_host_contract/020_post_hoc_annotation.md diff --git a/devlog/_plan/260907_code_mode_host_contract/030_docs_and_delivery.md b/devlog/_fin/260907_code_mode_host_contract/030_docs_and_delivery.md similarity index 100% rename from devlog/_plan/260907_code_mode_host_contract/030_docs_and_delivery.md rename to devlog/_fin/260907_code_mode_host_contract/030_docs_and_delivery.md diff --git a/devlog/_plan/260907_code_mode_host_contract/040_delivery_record.md b/devlog/_fin/260907_code_mode_host_contract/040_delivery_record.md similarity index 100% rename from devlog/_plan/260907_code_mode_host_contract/040_delivery_record.md rename to devlog/_fin/260907_code_mode_host_contract/040_delivery_record.md diff --git a/devlog/_plan/260908_a_stack_responses_compat/000_plan.md b/devlog/_fin/260908_a_stack_responses_compat/000_plan.md similarity index 100% rename from devlog/_plan/260908_a_stack_responses_compat/000_plan.md rename to devlog/_fin/260908_a_stack_responses_compat/000_plan.md diff --git a/devlog/_plan/260908_a_stack_responses_compat/010_phase1.md b/devlog/_fin/260908_a_stack_responses_compat/010_phase1.md similarity index 100% rename from devlog/_plan/260908_a_stack_responses_compat/010_phase1.md rename to devlog/_fin/260908_a_stack_responses_compat/010_phase1.md diff --git a/devlog/_plan/260908_a_stack_responses_compat/020_phase2.md b/devlog/_fin/260908_a_stack_responses_compat/020_phase2.md similarity index 100% rename from devlog/_plan/260908_a_stack_responses_compat/020_phase2.md rename to devlog/_fin/260908_a_stack_responses_compat/020_phase2.md diff --git a/devlog/_plan/260908_a_stack_responses_compat/030_phase3.md b/devlog/_fin/260908_a_stack_responses_compat/030_phase3.md similarity index 100% rename from devlog/_plan/260908_a_stack_responses_compat/030_phase3.md rename to devlog/_fin/260908_a_stack_responses_compat/030_phase3.md diff --git a/devlog/_plan/260908_a_stack_responses_compat/040_phase4.md b/devlog/_fin/260908_a_stack_responses_compat/040_phase4.md similarity index 100% rename from devlog/_plan/260908_a_stack_responses_compat/040_phase4.md rename to devlog/_fin/260908_a_stack_responses_compat/040_phase4.md diff --git a/devlog/_plan/260908_a_stack_responses_compat/050_phase5.md b/devlog/_fin/260908_a_stack_responses_compat/050_phase5.md similarity index 100% rename from devlog/_plan/260908_a_stack_responses_compat/050_phase5.md rename to devlog/_fin/260908_a_stack_responses_compat/050_phase5.md diff --git a/devlog/_fin/260908_a_stack_responses_compat/090_closeout.md b/devlog/_fin/260908_a_stack_responses_compat/090_closeout.md new file mode 100644 index 0000000000..e4775732e6 --- /dev/null +++ b/devlog/_fin/260908_a_stack_responses_compat/090_closeout.md @@ -0,0 +1,3 @@ +# closeout (recorded 2026-09-09) + +Landed via #3942 MERGED (`a13041740` ancestor of origin/dev and origin/main); issues #3885/#3922/#3911 CLOSED. Unit docs stopped at the publish/merge procedure; terminal outcome recorded here during the 260909 post-2.49 reconciliation. diff --git a/devlog/_plan/260908_d_group_test_infra_stack/000_plan.md b/devlog/_fin/260908_d_group_test_infra_stack/000_plan.md similarity index 100% rename from devlog/_plan/260908_d_group_test_infra_stack/000_plan.md rename to devlog/_fin/260908_d_group_test_infra_stack/000_plan.md diff --git a/devlog/_plan/260908_d_group_test_infra_stack/001_audit_record.md b/devlog/_fin/260908_d_group_test_infra_stack/001_audit_record.md similarity index 100% rename from devlog/_plan/260908_d_group_test_infra_stack/001_audit_record.md rename to devlog/_fin/260908_d_group_test_infra_stack/001_audit_record.md diff --git a/devlog/_plan/260908_d_group_test_infra_stack/010_phase1_stack_build.md b/devlog/_fin/260908_d_group_test_infra_stack/010_phase1_stack_build.md similarity index 100% rename from devlog/_plan/260908_d_group_test_infra_stack/010_phase1_stack_build.md rename to devlog/_fin/260908_d_group_test_infra_stack/010_phase1_stack_build.md diff --git a/devlog/_plan/260908_d_group_test_infra_stack/020_phase2_publish.md b/devlog/_fin/260908_d_group_test_infra_stack/020_phase2_publish.md similarity index 100% rename from devlog/_plan/260908_d_group_test_infra_stack/020_phase2_publish.md rename to devlog/_fin/260908_d_group_test_infra_stack/020_phase2_publish.md diff --git a/devlog/_plan/260908_d_group_test_infra_stack/030_phase3_merge_and_settle.md b/devlog/_fin/260908_d_group_test_infra_stack/030_phase3_merge_and_settle.md similarity index 100% rename from devlog/_plan/260908_d_group_test_infra_stack/030_phase3_merge_and_settle.md rename to devlog/_fin/260908_d_group_test_infra_stack/030_phase3_merge_and_settle.md diff --git a/devlog/_fin/260908_d_group_test_infra_stack/090_closeout.md b/devlog/_fin/260908_d_group_test_infra_stack/090_closeout.md new file mode 100644 index 0000000000..12177be6ba --- /dev/null +++ b/devlog/_fin/260908_d_group_test_infra_stack/090_closeout.md @@ -0,0 +1,3 @@ +# closeout (recorded 2026-09-09) + +Landed via #3940 MERGED (`221617b80`); #3924/#3930 CLOSED. Unit docs stopped at the merge gate; terminal outcome recorded here during the 260909 post-2.49 reconciliation. diff --git a/devlog/_fin/260909_spark_short_quota_attribution/000_plan.md b/devlog/_fin/260909_spark_short_quota_attribution/000_plan.md new file mode 100644 index 0000000000..7af10b83ce --- /dev/null +++ b/devlog/_fin/260909_spark_short_quota_attribution/000_plan.md @@ -0,0 +1,160 @@ +# Spark short-quota attribution fix (#4122) — plan + +## Reader summary + +Problem: on a Pro pool account that served a GPT-5.3-Codex-Spark request, the dashboard shows an +account-level "5h" quota bar that does not exist at the account level; peer Pro accounts with +identical upstream limits show none. Answer: the response-header quota path learns which model the +response was for, and when that model belongs to the Spark limit family the 5h primary window is +filed under the model's custom windows instead of the account-level short slot. What changes: pool +accounts with identical limits display identically, and the main-account hard lock and five-hour +auto-refresh scheduling stop reading a model-specific window as account policy. + +## Loop spec + +- Loop archetype: satisfy-spec (single work-phase wp1, one PABCD cycle; not multi-cycle, so no + docs-first roadmap cycle). +- Trigger: user directive to fix lidge-jun/opencodex#4122 and land it on dev via PR. +- Goal: header-observed 5h windows on Spark-model responses no longer occupy the account-level + short-quota slot; genuine account-level 5h windows (Plus/Team, non-Spark models) unchanged; PR + merged to dev with green final-head CI. +- Non-goals: WHAM parseUsageQuota mapping (already correct); GUI changes; docs-site; migration or + cleanup of already-polluted cache entries (on disk they expire via the six-hour hydration TTL; + in a long-running process the tuple persists in memory until restart or the next genuine short + write — cleanup stays explicitly out of scope); release or promotion; no local + test/typecheck/build runs (standing user rule: push with --no-verify, remote CI is the gate — + recorded NOT RUN below). Accepted consequence (reviewer finding 2, folded): once Spark responses + stop writing the account-level short slot, a Spark-saturated account is no longer preemptively + avoided for Spark-routed requests — routing evidence reads only the account slot + (src/routing/quota.ts:40-61, src/codex/quota.ts:67-80, src/codex/routing.ts:373-379). Bounded: + Spark requests only, the 429 quota-rotation path absorbs it, and WHAM-only accounts already + behave this way. Spark-aware exhaustion from customWindows is a tracked follow-up, not this PR. +- Verifier: remote CI on the PR's exact final head — .github/workflows/ci.yml pull_request trigger + (line 7), job test (line 263, gated on the changes filter at line 266 which covers src/** and + tests/** at lines 191/193) runs bash scripts/ci/run-bun-test-batches.sh (line 325) whose + `find tests -type f` selection (script line 197) includes + tests/codex-integration/codex-quota-parser-parity.test.ts; job platform-macos (line 475, gate + 478) runs the macOS suite sharded over tests (line 615). (Audit correction: the lines this plan + first cited, 706/839, belong to workflow_dispatch-gated jobs and were wrong.) + Conditional paths and their activation: the Spark-attribution branch activates when + parseUpstreamQuotaHeaders receives modelId in the Spark family plus a sub-day primary window — + proven by a regression row asserting customWindows gain and short* absence; the non-Spark branch + activates with the same headers and a non-Spark modelId — proven by a row asserting short* is + still written; the label-merge branch activates when existing customWindows hold a Spark Weekly + entry — proven by a row asserting it survives the header update. +- Stop condition: PR merged into dev and landing verified via fetched dev ancestry, or a + BLOCKED/NEEDS_HUMAN outcome with evidence. +- Memory artifact: this unit directory plus the session goalplan + (.codexclaw/goalplans/hotl-fix-lidge-jun-opencodex-4122-and-land-it-on/). +- Expected terminal outcomes: DONE = goal criteria c1-c4 met with fresh evidence; BLOCKED = + irreducible CI failure or merge-policy denial; NEEDS_HUMAN = authority gap. +- Escalation: any requirement to run local suites, push without --no-verify, change merge policy, + or expand scope beyond the listed files returns to the user. Reviewer-subagent FAIL after one + fold/rebut cycle returns to P with a revised plan rather than forcing B. +- HOTL resource bounds: tools = this worktree shell/git/gh and read-only upstream GETs; + credentials = existing gh auth only; write scope = this worktree plus issues/PRs on + lidge-jun/opencodex; token/wall-clock budget = none set by user. + +## Root cause (evidence) + +Two writers share one per-account snapshot in src/codex/quota.ts: + +1. WHAM path: parseUsageQuota maps the account primary window by duration and files + additional_rate_limits (metered_feature codex_bengalfox) under customWindows only + (src/codex/quota.ts:723+). Correct for Pro: weekly-only primary. +2. Header path: parseUpstreamQuotaHeaders (src/codex/quota.ts:410) files any sub-day primary + window as account-level shortPercent/shortResetAt/shortWindowSeconds. It has no model context, + so on a Spark-model response — where upstream reports the Spark governing limit as the primary + window — the model-specific 5h window lands in the account slot. + +Live proof on 2026-09-09: three Pro accounts returned identical wham/usage shapes (primary +604800s; Spark 5h/weekly in additional_rate_limits), yet only the Spark-serving account's cache +carried shortPercent 4 / shortWindowSeconds 18000, with shortResetAt exactly equal to the Spark 5h +reset_at. Downstream consumers that trust the account slot: main-account hard lock +(src/codex/main-account-hard-lock.ts:30-34 prioritizes short over weekly), five-hour auto-refresh +scheduling (src/codex/quota-auto-refresh.ts:62,88), pool evidence (src/routing/quota.ts). + +## File-change map (diff level) + +1. src/codex/quota.ts + - Add module-local helper isCodexSparkModel(modelId): modelId.includes("codex-spark"). + Necessity search: existing inline equivalents at src/adapters/openai-responses.ts:343,492 and + src/responses/hosted-tool-policy.ts:6; no shared helper exists. Reusing those call sites is + out of scope; the helper is introduced where the new branch needs it. + - parseUpstreamQuotaHeaders(headers, options?: { modelId?: string }): in the primaryIsShort + branch, when options.modelId is Spark-family, emit + customWindows: [{ label: "GPT-5.3-Codex-Spark 5h", percent, resetAt }] instead of account-level + short*; weekly continues to come from the secondary window. Label matches the WHAM parser's + Spark 5h label so both wires write the same slot. + - applyAccountQuotaFromUpstreamHeaders(accountId, headers, writerGeneration?, mainWriter?, + options?: { modelId?: string }): forward options to the parser; when the parsed quota carries + customWindows, merge label-wise with the account's existing cached customWindows (replace same + label, keep others) before setAccountQuotaFromParsed, so a header update never drops the + WHAM-recorded Spark Weekly window (#4007 retention preserved; mergeAccountQuota semantics + unchanged — an explicit list still replaces). Provenance amendment (reviewer finding 3, + folded): call hydrateAccountQuotasFromDisk() before reading the cached entry so the first + call in a process does not merge against an empty map; the pre-merged list goes ONLY into the + legacy quota argument — the policyQuota argument keeps the UNMERGED parse result so + legacy-cache customWindows are never injected into the identity-bound policy snapshot + (setAccountQuotaFromParsed's legacy/policy split at src/codex/quota.ts:245-252 stays intact). + - Refresh the stale comment ("Pro stays weekly-only") to describe the model-specific case. + - Field chain (PLAN-FIELD-CHAIN-01): the options value is created at the three callers below, + consumed only inside parseUpstreamQuotaHeaders/applyAccountQuotaFromUpstreamHeaders; no + serialization, no persistence, no enum. Consumers of the written slots (hard lock, + auto-refresh, routing evidence, CLI/GUI display) are behaviorally affected only by the + corrected attribution — no signature changes. +2. src/server/responses/core.ts — FOUR write paths, all with route in scope: + a. line ~1380 (429/402 quota-refresh-before-rejection path): pass { modelId: route.modelId }. + b. line ~5660 (post-response capture for forward-pool responses, behind + isCodexWsQuotaObservedResponse dedup): pass { modelId: route.modelId }. + c. line ~1022 (codexWsQuotaObserver callback for WebSocket quota frames): the FACTORY + codexWsQuotaObserver(authCtx, provider) gains a third parameter modelId and forwards it as + options; the CodexWsQuotaObserver TYPE (src/server/responses/codex-ws-metadata.ts:8) is + unchanged so direct observer constructions in tests/responses/responses-account-label.test.ts + keep compiling; all six factory call sites (lines ~1476, 5053, 5131, 5237, 5357, 5457) pass + route.modelId. + Found during the main agent's own audit pass after plan v1 named only two core.ts sites — + folded into the plan before the reviewer round closed (AUDIT-LOOP-01 amendment). +3. src/server/responses/compact.ts (line ~1013): pass { modelId: route.modelId } (route in scope; + selectedModelId = route.modelId at line 582). +4. src/codex/quota-auto-refresh.ts — NO CHANGE, recorded with reason: warmCodexAccount there sends + DEFAULT_MODEL gpt-5.4-mini (src/codex/warmup.ts:284), never a Spark model, so the warmup header + path cannot observe a Spark limit; omitting modelId preserves current behavior. +5. tests/codex-integration/codex-quota-parser-parity.test.ts — add regression rows (existing file + already owns #4007 Spark/header interaction and parser parity; no new file, so no layout.json + or fixture registration needed): + a. Spark model + 5h primary headers → account-level short* absent; customWindows contains + "GPT-5.3-Codex-Spark 5h" with the header percent/reset; weekly taken from secondary. + b. Same update with a pre-existing "GPT-5.3-Codex-Spark Weekly" customWindow → weekly entry + survives (label-merge), Spark 5h replaced. + c. Non-Spark model + identical 5h headers → account-level short* still written (Plus/Team + behavior preserved). + d. No modelId (legacy caller shape) → behavior identical to today. + Red evidence: rows a/b fail against the unpatched parser (short* is written today). Local + red-green execution is NOT RUN per the standing user rule; the assertion construction is + verified by review against the current code path and the remote CI run is the green gate. + +## Scope + +IN: the five items above. OUT: parseUsageQuota WHAM mapping, mergeAccountQuota global semantics, +GUI, docs-site, cache migration, warmup model selection, release/promotion. + +## SoT sync (C phase) + +structure/08_openai-provider-tiers.md documents quota windows (5h/weekly application around lines +96-99, 132, 140, 177). In C, check whether any statement now contradicts model-attributed short +windows and patch that file if so; otherwise record "checked, no contradiction". + +## Enforcement / bypass (PLAN-BYPASS-NAMED-01) + +This plan adds no enforcement layer. Tier: N/A; executing surface: none; known bypass: a future +caller can omit modelId and reproduce the old misattribution — mitigated only by the regression +tests and code review; residual risk accepted; final layer: none. + +## Verification gate mapping + +- Local bun test / typecheck / build: NOT RUN (standing user rule: never run local suites; push + with --no-verify). +- Remote: PR CI (ci.yml, pull_request trigger, sharded bun test over tests/) must be green on the + exact final head before merge; merge per MAINTAINERS.md maintainer PR-only path with the decision + and exact-head CI evidence recorded in the PR. diff --git a/devlog/_fin/260909_spark_short_quota_attribution/090_closeout.md b/devlog/_fin/260909_spark_short_quota_attribution/090_closeout.md new file mode 100644 index 0000000000..061ec107e6 --- /dev/null +++ b/devlog/_fin/260909_spark_short_quota_attribution/090_closeout.md @@ -0,0 +1,27 @@ +# closeout (recorded 2026-09-09) + +Landed via #4128 MERGED (`b2142586a`, merge commit `91db6c2f2`, both ancestors of origin/dev); +issue #4122 CLOSED. Remote CI on the exact head `b2142586a` was green: 28 successful check runs, +0 failures, 2 skipped by their own matrix gates (`macos control`, `windows ${{ matrix.shard }}/6`). + +Delivered exactly the `000_plan.md` file-change map: the optional routed-model hint on +`parseUpstreamQuotaHeaders` / `applyAccountQuotaFromUpstreamHeaders` +(src/codex/quota.ts:411-538), all four `src/server/responses/core.ts` write paths plus the six +`codexWsQuotaObserver` factory call sites, the compact path +(src/server/responses/compact.ts:1018), and the four regression rows in +tests/codex-integration/codex-quota-parser-parity.test.ts. +src/codex/quota-auto-refresh.ts stayed unchanged as planned. + +Carried forward, not regressions: + +- Already-polluted account entries keep the stale account-level `short*` tuple until the six-hour + hydration TTL expires them on disk, or until a restart or a genuine non-Spark short write + replaces them in memory. Declared out of scope in the `000_plan.md` non-goals and unchanged by + the merge. +- A Spark-saturated account is no longer preemptively avoided for Spark-routed requests, because + routing evidence reads only the account-level slot (src/routing/quota.ts:40-61). Bounded to + Spark requests and absorbed by the existing 429 pool rotation. Spark-aware exhaustion sourced + from `customWindows` remains a follow-up. + +The unit was committed into the product PR rather than kept separate; moving it here is that +correction, made after the merge closed the unit. diff --git a/devlog/_plan/260909_codex_credential_health_chain/000_plan.md b/devlog/_plan/260909_codex_credential_health_chain/000_plan.md new file mode 100644 index 0000000000..9c764929a8 --- /dev/null +++ b/devlog/_plan/260909_codex_credential_health_chain/000_plan.md @@ -0,0 +1,73 @@ +# Codex credential health chain (#4120, #3848, #3777) — plan + +## Reader summary + +Problem: a Codex pool credential whose OAuth grant was revoked upstream keeps +`lastCodexValidationStatus: "ok"` in `codex-accounts.json` and is presented as healthy for as long +as the install lives. Answer: a revoked/expired refresh grant is the strongest terminal evidence +available, so the guardian now persists that verdict on the account record instead of dropping it +into an in-memory backoff map, and the health projector reads it. What changes: an account with a +dead grant reports "Reauthentication required" on the dashboard, in `ocx status` and in +`ocx doctor`, and keeps reporting it across restarts until a re-login or a successful refresh +disproves it. + +## Loop spec + +- Loop archetype: satisfy-spec, three work-phases delivered as a bottom-up manual branch chain + (wp1 -> wp2 -> wp3), so this unit opens with the diff-level roadmap below and each decade doc is + revalidated at its own P. +- Trigger: maintainer directive to deliver #4120, shepherd #3848 and build #3777 as one chain. +- Goal: each layer is a non-draft, mergeable PR whose exact-head remote CI is green. +- Non-goals: no merging (the dispatching session owns merge order); no rebase of any layer unless + that session asks for one; no release or promotion; no default-on background warmup; and no + local product suite, typecheck, build, lint or install — the standing maintainer rule is that + remote CI on the PR's exact final head is the only gate, and every skipped local check is + recorded NOT RUN. +- Verifier: `.github/workflows/ci.yml` on `pull_request`; the `test` job selects + `tests/**` through the changes filter, so the appended regression rows in + `tests/codex-integration/` and `tests/oauth/` are in the selected set on Linux, macOS and + Windows. +- Stop condition: all three PRs non-draft with green exact-head CI, or a BLOCKED outcome naming + the blocker. wp1 must be able to land alone if wp2 stalls. +- Escalation: a required rebase, a merge conflict against `dev`, or any need to run a local suite + returns to the dispatching session rather than being resolved unilaterally. + +## Root cause (#4120, evidence) + +`guardianSweep`'s pool branch decides whether to sweep an account at +`src/oauth/token-guardian.ts:210-215`: + + const needsRefresh = cred.expiresAt <= nowMs + horizonMs; + const needsWarmup = opts.codexWarmupEnabled && (...); + if (!needsRefresh && !needsWarmup) continue; + +and decides what to persist on failure at `src/oauth/token-guardian.ts:238-241`: + + const permanent = err instanceof TokenRefreshError && (err.reason === "revoked" || err.reason === "expired"); + if (needsWarmup && !(err instanceof TokenRefreshError)) { + markCodexAccountValidationFailed(id, codexWarmupFailureReason(err)); + } + +`permanent` is computed and then used only to widen the in-memory backoff delay +(`recordFailure`, `:100-115`), which does not survive a restart and is not what any health +surface reads. The persisted-verdict branch requires `needsWarmup`, which is false in the default +configuration because `codexWarmupEnabled` defaults to `false` (`:86`), and it additionally +excludes every `TokenRefreshError`. So the one class of failure that proves the credential is dead +is the one class that never reaches the account record. + +The second half of the defect is on the read side: `src/oauth/health.ts` never consults the +validation metadata at all. `projectCodexAccountHealth` (`:196-210`) reads only the in-memory +reauth flag and the cooldown snapshot, so a record carrying a stale login-time `"ok"` projects +`{ status: "healthy" }`. + +## Work-phase roadmap + +| Phase | Doc | Layer | Base | +|---|---|---|---| +| wp1 | `010_wp1_terminal_verdict.md` | persist + project the terminal verdict (#4120) | `origin/dev` | +| wp2 | `020_wp2_quota_registration.md` | shepherd #3848 onto the chain | wp1 head | +| wp3 | `030_wp3_anthropic_plan.md` | Anthropic subscription tier (#3777) | wp2 head | + +The order is a dependency order, not an effort order: wp3 edits `src/cli/account-api.ts`, which +wp2 already rewrites, and wp2 touches the same account-store and guardian surfaces wp1 changes. +Each layer stands alone for review and carries its own tests. diff --git a/devlog/_plan/260909_codex_credential_health_chain/010_wp1_terminal_verdict.md b/devlog/_plan/260909_codex_credential_health_chain/010_wp1_terminal_verdict.md new file mode 100644 index 0000000000..20058f2c4d --- /dev/null +++ b/devlog/_plan/260909_codex_credential_health_chain/010_wp1_terminal_verdict.md @@ -0,0 +1,98 @@ +# wp1 — persist and project the terminal validation verdict (#4120) + +Class C4: OAuth credential handling, the credential store, and a health surface. + +## Decision 1 — an extra optional key, not a new status value + +The obvious shape for "this credential is dead" is a third value in the persisted status union +(`"ok" | "failed" | "revoked"`). It is unsafe here. `isCredentialRecord` +(`src/codex/account-store.ts:60`) admits only `undefined | "ok" | "failed"`; a record carrying an +unrecognized value fails that predicate, so `normalizeRecord` (`:74-88`) falls through to +`isCredential`, which also fails because a record has no top-level `accessToken`, and returns +`undefined`. `loadCodexAccountRecordStore` (`:96-101`) then silently omits the record. An +operator who writes a terminal verdict on 2.51 and rolls back to 2.50 would lose the whole +account entry, credential included. + +An additional optional key has the opposite property: `normalizeRecord` returns +`{ ...value, refreshGrantFingerprint }`, so an unknown key is carried through untouched by a build +that has never heard of it. So the record gains: + + lastCodexValidationTerminal?: boolean; + +## Decision 2 — the marker clears itself + +A terminal verdict that can only be set is worse than no verdict: one spurious `invalid_grant` +from upstream would brand a live account dead forever, since background warmup is off by default +and nothing else would revisit it. The marker therefore has exactly two ways to disappear, and both +are structural rather than remembered: + +- `markCodexAccountValidated` clears it explicitly, alongside the error string it already clears. +- Every credential write drops it for free. `saveCodexAccountCredential` (`:145`), + `saveCodexAccountCredentialIfGeneration` (`:216`) and + `commitRefreshedCodexCredentialWithAliases` (`:275`, `:303`) rebuild the record from an + explicit field list plus `preservedValidationMetadata` (`:121-128`) and never spread + `...current`. Keeping the new key out of that pick list is what makes a successful refresh or a + re-login erase the verdict, which is correct: a refresh that succeeds disproves "grant revoked". + +Those five sites plus the tombstone at `:324` are every record writer in the codebase — +`loadCodexAccountRecordStore` is module-private and no other module writes +`codex-accounts.json`. + +## Decision 3 — the generation fence + +`markCodexAccountValidationFailed` gains an options bag with `expectedGeneration` and +`terminal`, and returns whether it wrote. The guardian passes the generation it actually observed: +`record.generation` before the refresh, replaced by `token.generation` once a refresh has +committed, because a successful refresh bumps the generation and a warmup failure after it belongs +to the new credential. + +A failed refresh never follows a commit inside the same call: `resolveCodexToken` returns on the +freshness shortcut (`:717-721`), on same-grant adoption (`:465-475`) and on the CAS commit +(`:975-985`), and the `TokenRefreshError` throw (`:948`) is reached only from a `!res.ok` +token response with no prior write. If a different writer replaced the credential between the +guardian's read and the locked re-read, the fence declines to write. That is a deliberate false +negative: the failure cannot be attributed to the credential the sweep observed, and refusing to +write is always safer than branding a freshly installed credential dead. + +## Decision 4 — project onto the existing health member, with no GUI diff + +A terminal verdict maps to `{ status: "reauth_required", reason: "refresh_failed" }`, which +already exists in `OAuthAccountHealth` (`src/oauth/health.ts:14-18`). That is not a shortcut, it +is the accurate statement: only a re-login fixes a revoked grant, and `actionFor` (`:88-95`) +already attaches `CODEX_REAUTH_ACTION` — "reauthenticate via the dashboard Codex account pool" — +for the `codex` provider. + +Reusing it also means the dashboard needs no change at all. The GUI does not render the server's +`healthLabel`; it recomputes the badge from the `health` object through +`gui/src/oauth-health-display.ts`, so `reauth_required` already turns the row amber +(`codex-account-pool-cards.tsx:81,88`), prints "Reauthentication required" and shows the action. +A new warning reason would have required a GUI enum, nine i18n locales and a dashboard screenshot, +for strictly worse copy. + +`collectLocalCodexEntries` (`:265-282`) currently inlines a copy of the projector's body rather +than calling it, which is how the CLI path would have silently missed this fix. It is folded onto +`projectCodexAccountHealth` so the two cannot drift again. + +Precedence note: `projectOAuthAccountHealth` checks reauth before cooldown, so an account that is +both revoked and quota-cooled now reports reauth. That is the right order — telling an operator to +wait out a cooldown on a credential that will never work again is a false promise. + +## Out of scope + +Issue expectation 3 (revalidate stored pool credentials on a bounded schedule even with warmup +disabled) is declined here: it means a default-on inference probe, which this change is explicitly +not allowed to introduce. Showing `lastCodexValidatedAt` as a first-class dashboard column is also +deferred — it is a GUI change with no server-side defect behind it. + +## Verification + +Appended to existing test files, because a new test file additionally requires entries in +`scripts/test-layout/layout.json` and `tests/fixtures/test-layout-expected.json`: + +- `tests/codex-integration/token-guardian.test.ts` — a revoked grant persists + `failed` + terminal with no warmup enabled; a transient (`unknown`) refresh failure persists + nothing; a credential replaced mid-refresh is not clobbered. +- `tests/codex-integration/codex-account-store.test.ts` — the generation fence declines a stale + write, `markCodexAccountValidated` clears the marker, and a credential write drops it. +- `tests/oauth/oauth-health.test.ts` — a terminal record projects `reauth_required` with the + Codex reauth action, and an ordinary record still projects healthy. diff --git a/devlog/_plan/260909_codex_credential_health_chain/020_wp2_quota_registration.md b/devlog/_plan/260909_codex_credential_health_chain/020_wp2_quota_registration.md new file mode 100644 index 0000000000..dbd6364117 --- /dev/null +++ b/devlog/_plan/260909_codex_credential_health_chain/020_wp2_quota_registration.md @@ -0,0 +1,24 @@ +# wp2 — shepherd #3848 onto the chain (quota-exhausted registration, #3846) + +Author: @shaun0927 (Junghwan). This layer is carried, not reimplemented, so the original author's +`Co-authored-by` trailers are preserved on the branch commits — `missing_coauthor_credit` in +`.github/scripts/pr-carry-attribution.cjs` reads the trailer, and a sentence in a commit body is +read by nothing. + +Substance (unchanged from the original PR): a Codex account whose weekly allowance is exhausted +cannot complete the mandatory inference warmup, so registration fails outright. The change persists +it as validation-pending, keeps it out of routing and manual selection, and requires a human +dashboard "Refresh quotas" click to finish validation, because finishing it spends model quota. + +Work in this phase: + +- Retarget the PR base from `dev` onto the wp1 head branch. +- Resolve the conflict against current `dev`. This is a conflict inside this lane's own chain, + which is the one case the no-rebase rule does not cover; a cross-lane rebase still returns to the + dispatching session. +- Preserve the GUI evidence screenshots already in the description — the PR touches `gui/`, so + `missing_ui_screenshot` (`.github/scripts/pr-quality.cjs:531`) requires them. +- Restore repository hygiene: no vendored reference clones, no tracked gitlink, no security triage + under `devlog/` (`tests/ci-workflows/repo-hygiene.test.ts`). + +Class C4 — authentication, account store, guardian and GUI in one change. diff --git a/devlog/_plan/260909_codex_credential_health_chain/030_wp3_anthropic_plan.md b/devlog/_plan/260909_codex_credential_health_chain/030_wp3_anthropic_plan.md new file mode 100644 index 0000000000..56dc7093f0 --- /dev/null +++ b/devlog/_plan/260909_codex_credential_health_chain/030_wp3_anthropic_plan.md @@ -0,0 +1,23 @@ +# wp3 — expose the Anthropic subscription tier (#3777) + +The OpenAI provider already reports a per-account `plan` string, so a consumer can weight each +account's remaining quota by its tier. The Anthropic provider reports rich quota and no tier at +all, so a six-account Claude pool has no meaningful aggregate capacity number. + +Surface, bottom to top: + +- `src/providers/quota.ts` `fetchAnthropicUsageQuota` — read the subscription tier from a real + field in the upstream usage/billing response. +- `src/oauth/index.ts` — add `plan: string | null` to the OAuth account summary. +- `src/server/management/oauth-account-routes.ts` — carry it on the management DTO. +- `src/cli/account-api.ts` — carry it on the CLI DTO. This file is why the layer chains on wp2, + which already rewrites it. +- The Anthropic GUI rows. + +Hard constraint from the issue and from the maintainer: if the upstream response carries no tier +field, land `plan: null` plus documentation saying so. Do not infer a Max x5 / x20 mapping from +quota percentages — the issue reporter already established that percentages are normalized per +account and carry no tier information, so a guess would be indistinguishable from data. + +The explicit `null` matters: it lets a consumer tell "unknown tier" apart from "OpenCodex too old +to report one". diff --git a/devlog/_plan/260909_mid_thread_agent_task_recovery/000_plan.md b/devlog/_plan/260909_mid_thread_agent_task_recovery/000_plan.md new file mode 100644 index 0000000000..b5ebf51467 --- /dev/null +++ b/devlog/_plan/260909_mid_thread_agent_task_recovery/000_plan.md @@ -0,0 +1,148 @@ +# Mid-thread encrypted agent-task recovery (#4089) — plan + +## Reader summary + +Problem: switching a live Codex Desktop thread from a native ChatGPT model to a routed provider +model bricks the thread with `unreadable_encrypted_agent_task`. The thread's history already +contains a backend-minted `encrypted_content` agent message, every later turn replays it, and +`agentTaskRecovery` — even when explicitly enabled — never runs, because the direct recovery +block is gated on `isThreadSpawnRequest(req.headers)` and a mid-thread model switch is not a +thread spawn. Answer: `threadSpawn` was never the trust boundary; `recoveryAdmission()` is. +What changes: the direct gate drops the `threadSpawn` conjunct, so any routed Responses request +carrying an unreadable encrypted agent envelope gets one recovery attempt under the same +unchanged admission checks. The combo gate keeps its spawn requirement. + +## Loop spec + +- Loop archetype: satisfy-spec (single work-phase wp1, one PABCD cycle; not multi-cycle, so no + docs-first roadmap cycle). +- Trigger: lidge-jun/opencodex#4089, delegated as one lane of the post-2.49 round. +- Class: C4. This widens a trust boundary — a code path that spends the caller's stored native + ChatGPT session becomes reachable from a request shape that previously could not reach it — so + it gets a durable evidence record and expects security review before merge. +- Goal: a mid-thread native-to-routed model switch attempts recovery; the discriminator + `recovery_reason` is present on the non-spawn error body; the admission checks are untouched; + PR on `dev` with green exact-head CI. +- Non-goals: #2495 (opt-in plaintext V2 rewrite) and #3661 (spawn-path recovery failures) are + explicitly out of this lane and are not grouped in. No widening of `recoveryAdmission()`. No + change to the combo path. No change to `canPassThroughEncryptedV2AgentTask()`, so an + OAuth-mode routed provider still has no ciphertext passthrough. No early client-side rejection + of the model switch (suggested direction 2 in the issue) — that is a product/UX decision for a + separate unit. +- Local verification: NOT RUN by standing maintainer instruction for this round (no product + suite, no typecheck, no build, no lint, no `bun install`). Remote CI on the PR's exact final + head is the only gate: `.github/workflows/ci.yml` job `test` (line 263), gated on the + `changes` filter that covers `src/**` and `tests/**` (lines 191, 193), runs + `bash scripts/ci/run-bun-test-batches.sh` (line 325) whose `find tests -type f` selection + includes `tests/server/agent-task-recovery.test.ts`. +- Stop condition: PR non-draft, mergeable, exact-head CI green. Merging is the main session's. +- Expected terminal outcomes: DONE = PR open with green exact-head CI; BLOCKED = irreducible CI + failure or a conflict the main session must sequence. + +## Root cause (evidence) + +`src/server/responses/core.ts:3616-3624` — the direct (non-combo) recovery block: + +``` + inboundWire === "responses" + && threadSpawn + && agentTaskRecovery + && !isCanonicalOpenAiForwardProvider(route.provider) + && !options.comboAttempt + && !canPassThroughEncryptedV2AgentTask(route, inboundWire) +``` + +`threadSpawn` is `isThreadSpawnRequest(req.headers)` (`core.ts:3503`), which +`src/server/effort-policy.ts:33` defines as true only for `x-openai-subagent: collab_spawn` +or turn metadata `subagent_kind === "thread_spawn"`. A mid-thread model switch carries neither, +so the whole block is skipped: `restoreCachedEncryptedAgentTasks()` never runs, the recovery +call never runs, and `recoveryFailureReason` stays `undefined`. The request then falls through +to `core.ts:3762-3769` and returns `unreadable_encrypted_agent_task` with no +`recovery_reason` field at all (`core.ts:1988-1999` only attaches the field when a reason +exists). That absence is exactly the discriminator the reporter observed on loopback. + +A thread started on the routed model never carries backend-minted ciphertext, which is why it +never reproduces. Once one native-minted `encrypted_content` agent message is in the history it +is replayed on every subsequent turn, so the thread is permanently unusable on that provider. + +`canPassThroughEncryptedV2AgentTask()` (`core.ts:1990ff` comment block) requires +`authMode === "key"` plus `allowEncryptedV2AgentTasks` plus an `openai-responses` wire, so +an OAuth-mode routed provider has no passthrough escape either. Recovery is the only path. + +## Change + +One conjunct deleted from `src/server/responses/core.ts:3618-3619`, plus a comment recording +why. The cache restore (`restoreCachedEncryptedAgentTasks`) lives inside the same `if`, so it +moves with the gate — which also fixes the reporter's third observation, that a proxy restart +loses any chance of reusing a previously recovered plaintext for the thread. + +## Why the trust boundary is unchanged + +`recoveryAdmission()` (`src/server/responses/agent-task-recovery.ts:249-283`) is untouched. +Every one of its checks still runs on the widened path, and each one independently refuses: + +1. `isApiAuthRequired(config)` — a proxy with inbound API auth configured is never admitted. +2. `CODEX_ORIGINATORS` membership on the `originator` header. +3. No `x-opencodex-api-key` and no `x-api-key` on the inbound request — remote/shared proxy + callers are refused outright, because caller-controlled Codex metadata is not strong enough + to authorize spending a stored ChatGPT session. +4. A `Bearer` token that passes `isNativeChatGptAccessToken()`: RS256 + `kid`, an issuer in + `OPENAI_TOKEN_ISSUERS`, the `https://api.openai.com/v1` audience, the Codex OAuth + `client_id`/`azp`, unexpired, `nbf` honoured, and a present auth claim object. +5. The account id extracted from that token must equal the explicit `chatgpt-account-id` + header. +6. A proxy admission secret presented as the bearer is rejected before anything else + (`isProxyAdmissionSecret`). + +The recovery cache is keyed by an HMAC over the token and account id +(`agent-task-recovery.ts:275-279`), so a widened entry point cannot read another caller's +recovered plaintext. `restoreCachedEncryptedAgentTasks()` re-runs `admittedRecovery()` per +item before touching the cache (`agent-task-recovery.ts:559-561`). + +The population that gains reachability is therefore: a loopback request from a Codex originator, +holding a live native ChatGPT bearer for the same account named in `chatgpt-account-id`, with no +inbound API key, on a proxy that does not require inbound API auth — that is, the same user whose +session would be spent, on the same machine. `threadSpawn` narrowed *which of that user's own +requests* could use their own session; it did not keep anyone else out. + +## Plaintext-oracle bound + +`src/server/responses/encrypted-payload.ts:181-192` records the reason recovery is +`NEW_TASK`-only: `MESSAGE` is matched for the unreadability CHECK so a reply envelope whose +whole body is one Fernet token is not forwarded verbatim, but decrypting a `MESSAGE` on the +parent's behalf would build a plaintext oracle out of a payload the parent's session may not be +entitled to read. That asymmetry is enforced in +`src/server/responses/agent-task-recovery.ts:508` and is not touched here. Widening the *entry* +gate does not widen *what* may be decrypted: an unreadable `MESSAGE` still fails closed with a +refusal reason, and the only envelope that reaches an actual decrypt attempt is a `NEW_TASK` +the admitted caller's own session is entitled to read. + +## Why the combo gate stays + +`core.ts:2691-2694` keeps `!isThreadSpawnRequest(req.headers)`. The combo path has its own +native-target filtering and per-attempt failover semantics +(`canDecryptUnreadableAgentTask`, `payloadEligible`, `comboPayloadReadable`), and the +reported defect is on the direct path. Widening both at once would mean two behavior changes +under one security review; the combo path can be reconsidered separately with its own evidence. + +## Regression + +`tests/server/agent-task-recovery.test.ts` gains the reporter's A/B pair: two `post()` calls +with an identical body — one `agent_message` carrying a routing header plus a structurally valid +Fernet-shaped `encrypted_content` slot — differing only by the `x-openai-subagent: collab_spawn` +header, against a routed provider. Both must fail with `unreadable_encrypted_agent_task`, and +both must now carry `recovery_reason`, because `recovery_reason` is attached only when recovery +actually ran. Before this change the non-spawn arm has no `recovery_reason` field. + +## Docs + +`docs-site` framed this feature as spawn-only ("a native ChatGPT parent spawning a routed v2 +child"). The reference page and the sub-agent surface guide now name both qualifying request +shapes, and the combo paragraph states explicitly that combo recovery is still spawn-only. The +same one-clause precision is applied to the seven translated locales so they do not contradict +the English source, matching what #3754 did for the combo-recovery change. The `docs-site` build +was not run under the same standing instruction; the edits are prose-only inside existing pages. + +## Terminal outcome + +Tracked on lidge-jun/opencodex#4135. This unit moves to `devlog/_fin/` once the PR is on `dev`. diff --git a/devlog/_plan/260909_post249_scope_cleanup/000_plan.md b/devlog/_plan/260909_post249_scope_cleanup/000_plan.md new file mode 100644 index 0000000000..048260e265 --- /dev/null +++ b/devlog/_plan/260909_post249_scope_cleanup/000_plan.md @@ -0,0 +1,32 @@ +# 260909 post-2.49 scope cleanup + interview + +## Context + +v2.49.0 shipped 2026-09-09T12:53Z (main `2f3f73629`, dev reopened at 2.50.0 via #4115). +Before choosing the next bug/improvement round, the user asked for a full +reconciliation pass and then an interview to settle scope. + +## Work items + +1. Reconcile devlog/_plan units (~150) against public git history; move + terminal+landed units to _fin. Evidence: parallel read-only recon lanes + (dispatched as separate Codex tasks, model xai/grok-4.6). +2. Reconcile branches (local + origin) and worktrees against live PR state; + remove only provably-merged/clean ones; ambiguous ones go to a review list. +3. Build the open-issue disposition table: CLEAR-FIX / LIVE-PROBE / POLICY / + IMPROVEMENT / STALE-FIXED / DUPLICATE. +4. Interview with the user to pick this round's scope: clear bugs + minor + UX improvements in; live-probe items deferred unless measurable via + computer-use; policy items decided one by one. + +## Known starting state (verified this session) + +- CLEAR-FIX, no open PR: #4112 (non-streaming 413 bypasses overflow mapping), + #4120 (revoked pool credential stays "ok"), #4089 (agentTaskRecovery gated + on threadSpawn). +- CLEAR-FIX with draft PRs already open: #3926->#4068, #4083->#4084, + #4110->#4114. +- POLICY candidates: #3846 (warmup gate registration), #3761 (passthrough + hosted-search bridge). +- LIVE-PROBE: #3782 (Claude Desktop env), #3781 residual (TUN/Fake-IP), + #3775 residual (YYLJ gateway/Desktop). diff --git a/devlog/_plan/260909_post249_scope_cleanup/010_reconciliation.md b/devlog/_plan/260909_post249_scope_cleanup/010_reconciliation.md new file mode 100644 index 0000000000..5b0958b1b8 --- /dev/null +++ b/devlog/_plan/260909_post249_scope_cleanup/010_reconciliation.md @@ -0,0 +1,32 @@ +# 010 — devlog reconciliation (_plan -> _fin) + +Method: five read-only recon lanes (spawned agents, xai/grok-4.6) reconciled all +158 _plan units against fetched origin/dev 57077ca32 and origin/main 2f3f73629 +(v2.49.0). CLOSEOUT-READY requires a recorded terminal outcome in the unit AND +landed ancestry evidence. Spot checks by main session: c9a202e38 ancestry, +PR #3662/#2080/#3966/#3942/#3785 all MERGED — lane claims verified. + +## Moved to _fin (101 this round; 260908_provider_runtime_stack was already there) + +- 260724-260822: 19 closeout-ready + 4 abandoned (260813_bun_canary_dogfood, + 260814_bug_resolution_campaign, 260814_usage_memory_roadmap, + 260822_260822-bun14-followup-memory — each with a new 090_terminal.md). +- 260823-260904: 64 closeout-ready + 2 abandoned (260827_remote_hub superseded + by 260901_remote_hub_restack; 260904_dashboard_minimal reverted by #3415). +- 260905-260909: 6 closeout-ready + 7 landed-but-unrecorded (new 090_closeout.md + citing merge evidence: 260905_external_image_roundtrip #3586-#3596, + 260905_fast_default_exports #3674, 260905_grok_responses_default #3670, + 260906_opaque_transport_finality #3753/#3754, 260906_release_244_publish + #3785+v2.44.0, 260908_a_stack_responses_compat #3942, + 260908_d_group_test_infra_stack #3940). + +Result: _plan 158 -> 57 units, _fin 432 -> 533. + +## Still open (57 units) + +Genuinely incomplete or design-only units remain in _plan, including: +260801_monorepo_git_blobless_strategy, 260816_codexrs_multiagent_v2_and_history_perf +(phase 1 landed, phases 2-5 open), 260817_windows_stability_program, +260818_megafile_split_program, 260819_next_roadmap, 260821_bug_merge_train, +260822_senpi_cursor_transfer, and the 260905-260909 working units +(33 per lane A3, mostly active campaign/merge-train units). diff --git a/devlog/_plan/260909_post249_scope_cleanup/020_branch_worktree_cleanup.md b/devlog/_plan/260909_post249_scope_cleanup/020_branch_worktree_cleanup.md new file mode 100644 index 0000000000..268b9bccd8 --- /dev/null +++ b/devlog/_plan/260909_post249_scope_cleanup/020_branch_worktree_cleanup.md @@ -0,0 +1,45 @@ +# 020 — branch and worktree cleanup + +Evidence standard: local branches deleted only with PR MERGED (gh) or +ancestor-of-origin/dev proof (recon lane B + main-session spot checks). +Branches checked out in surviving worktrees were kept. + +## Worktrees + +- Removed 54 clean /private/tmp worktrees (248-a/248-b lane workers, axis1-5, + bug6, track1-3, release-246/247 workers, prs-stack, sponsors, etc.). Clean + worktree removal loses nothing: every branch ref was retained unless itself + merged (below). +- Kept: main checkout (dirty local dev, user-owned), this session's ae6a, + all ~/.codex/worktrees app-managed slots (app owns their lifecycle), + ~/.cursor/worktrees/opencodex/njhf. +- Dirty merged worktrees left for owner review (uncommitted devlog notes): + 8fd91167 (voice-contract-0908), cb55 (pr3997), d974cb89 (bug6-01a07e9d-close), + plus 5 detached /var/folders tmp dirs. + +## Local branches: 144 deleted + +Merged-PR branches (squash, anc=0 but gh MERGED) and ancestor-merged branches: +248-a/248-b/248-c lanes, 260904-260909 wp/train lanes, 260907 a-e letters, +axis1/2/3/5, bug6-01a07e9d set, a/b/c-track, prs-l1-l6, rt-m1-m8, +release-244/246/247/248/249 lines, pr-39xx/40xx/41xx aliases, sponsors/*, +codex/providers-home-and-quota-refresh, codex/security-pr-lane-20260909 +(same SHA as stale local dev), etc. 208 local branches remain (orphan +roadmap/diagnostic/rb-*/jrb-*/*-evidence set with no PR and no ancestry — +kept pending owner review, they hold the only copy of that work). + +## Remote branches (origin): 8 deleted + +- codex/models-provider-head-uniform-row (#3096 MERGED) +- codex/providers-home-and-quota-refresh (#3472+#3466 MERGED) +- codex/release-245-candidate-519b (ancestor of origin/dev) +- fix/post-layout-guard-regressions (#3532 CLOSED, carried via #3865 MERGED) +- ingw/fix-chat-json-sse-parity (#3779 CLOSED) +- ingw/fix-container-codex-volume (#3747 CLOSED) +- ingw/fix-reasoning-envelope-budget (#3862 CLOSED, superseded by #3879 MERGED) +- ingw/type-safety-registry-modularization (#2805 CLOSED) + +Kept on origin: open-PR heads (improved-remote-control #3458, +ingw/fix-4110-owned-root-guidance #4114) and ~40 orphan evidence branches +(assets/*, media/*, *-evidence, diagnose-*, track*) — no PR ref backup exists +for these, so deletion would be unrecoverable; owner decision required. diff --git a/devlog/_plan/260909_post249_scope_cleanup/030_issue_disposition.md b/devlog/_plan/260909_post249_scope_cleanup/030_issue_disposition.md new file mode 100644 index 0000000000..c9f76b24d2 --- /dev/null +++ b/devlog/_plan/260909_post249_scope_cleanup/030_issue_disposition.md @@ -0,0 +1,56 @@ +# 030 — open-issue disposition (post-2.49) + +Source: recon lane C, verified against gh on 2026-09-09 (57 open issues, +origin/dev 57077ca32, v2.49.0 = 2f3f73629). No STALE-FIXED, no DUPLICATE. + +## CLEAR-FIX (8) + +| issue | title | linked PR | +|---|---|---| +| #4122 | Spark 5h header window stored as account-level short quota on Pro | — | +| #4121 | opencode-free Zen rejects without x-opencode-session | — (#3954 related, not closing) | +| #4120 | revoked-but-time-valid pool credential stays "ok" | — | +| #4112 | non-streaming 413 never hits context-overflow mapping | #4119 (follow-up, not closing) | +| #4110 | client-compaction status treats operator-owned URL as non-proxy | #4114 draft (Closes) | +| #4089 | mid-thread native→routed switch; agentTaskRecovery gated on threadSpawn | — | +| #4083 | Codex WS 30s response-prelude cuts multi-image slow starts | #4084 draft (Closes) | +| #3926 | Google AI Studio discovery rejects models[] envelope | #4068 draft (Closes) | + +No closing PR yet: #4122, #4121, #4120, #4089, #4112. + +## POLICY (8) — interview decisions + +| issue | question | +|---|---| +| #4076 | remove the ChatGPT quota overlay that blocks picking other models? | +| #4073 | private follow-up path for pending security reports (process/docs) | +| #3978 | allow client compaction without disabling V2 routing (Design B) | +| #3859 | make email masking optional (privacy gate change) | +| #3846 | persist pool accounts as quota-limited instead of warmup-gating (draft #3848 exists) | +| #3761 | hosted-search bridge for raw Responses passthrough (contract choice) | +| #3506 | add a no-progress cutoff after #2600 (product decision) | +| #2495 | opt-in plaintext V2 rewrite for native→routed subagents (tracking) | + +## LIVE-PROBE (9) — deferred unless measurable via computer-use + +#3782 (Claude Desktop model switch), #3781 (TUN/Fake-IP), #3775 (gateway/Desktop), +#3765 (cache plateau attribution), #3719 (Anthropic replay + cache), +#3661 (V2 subagent encrypted task), #3522 (Windows spill), #3433 (Hermes cache), +plus #4083's field half already covered by draft #4084. + +## IMPROVEMENT (32) + +#4079 (quota-reset-first scheduling; draft #4080), #4075 (Gemini setup UX), +#4057 (account identity in logs), #4055 (persistent WebUI auth on remote binds), +#4038 (decode tok/s; draft #4040), #4024 (OpenRouter key rotation), +#3898 (headless hub reauth), #3777 (Anthropic plan exposure), #3774 (DnD picker order), +#3729 (remote catalog pull), #3705 (Guardrails; draft #4022), #3666 (free-model filter), +#3630 (catalog auto-refresh), #3573 (configurable body limit), #3494 (VS Code agents), +#3459 (request transform hook; draft #3463), #3417 (native main login profiles), +#3379 (dashboard gaps epic), #3377 (per-model capabilities), #3376 (quota history; +draft #4080), #3375 (OAuth pool lifecycle), #3191 (MSP adapter), #2894 (SOCKS5; +drafts #3901/#2921), #2834 (relay diagnostics), #2811 (update manager), +#2730 (alpha search auth), #2511 (image byte budget; #4119 open), #2358 (RFC), +#2279 (synthetic max; draft #2280), #1711 (zero-credit grey-out), +#1416 (Orca manifest), #1213 (Claude Desktop catalog mode), #95 (hosted multi-user). + diff --git a/devlog/_plan/260909_post249_scope_cleanup/040_dispatch_roadmap.md b/devlog/_plan/260909_post249_scope_cleanup/040_dispatch_roadmap.md new file mode 100644 index 0000000000..048e6a6099 --- /dev/null +++ b/devlog/_plan/260909_post249_scope_cleanup/040_dispatch_roadmap.md @@ -0,0 +1,61 @@ +# 040 — dispatch roadmap (post-2.49 delivery round) + +Loop: cxc-loop HOTL under an active host goal. Main session owns phase control, +merge order, and rebase timing. Eight parallel worktree threads own implementation. + +## Delivery contract (identical for every thread) + +- Skills: cxc-loop + cxc-dev. Classify each item C0-C5 and scale process to it. +- NEVER run the local product suite, typecheck, build, or install. Remote CI on the + exact PR head is the only gate. Label skipped local checks NOT RUN. +- Push with `--no-verify`. Branch prefix `codex/` (or keep an existing head branch + when shepherding someone else's PR). +- Every PR bases on `dev` at 57077ca32 unless it is a child inside its own chain, + in which case it bases on its parent's head branch. +- Do NOT rebase. The main session controls rebase timing after watching merge order. +- Threads may dispatch xai/grok-4.6 subagents (spawn_agent) for parallel bounded work. +- Carry `Co-authored-by` trailers when landing or shepherding another author's work. + +## Thread map + +| T | Owns | Kind | Base | +|---|---|---|---| +| 1 | #4114, #4084, #4068 | shepherd 3 open PRs to ready | dev (existing heads) | +| 2 | #4127 (issue #4112) -> #3573 | shepherd + child | dev, then #4127 head | +| 3 | #4128 (issue #4122) | shepherd | dev (existing head) | +| 4 | #4120 -> #3848 -> #3777 | new + shepherd + new | dev, then chain | +| 5 | #4089 | new (C4, security review before merge) | dev | +| 6 | #4057 | new | dev | +| 7 | #3761 Design A | new (largest) | dev | +| 8 | #4073, #4121 | docs only | dev | + +## Merge order (main session controls) + +Land bottoms as they go green; do not wait for tops. +#4114 -> #4068/#4084/#4073 -> #4127/#4128 (first green wins, loser rebases on +instruction) -> #4120 -> #4121-docs/#4057 -> #4089 -> #3573 -> #3777 -> #3848 -> #3761. + +Independent of any chain: #4114, #4068, #4084, #4073, #4127, #4128, #4120, #4057, #4121. +Chain-dependent: #3573 (needs #4127), #3777 (needs #3848 for cli/account-api.ts), +#3848 (needs #4120 for the account store/guardian fields), #3761 (wants a settled core.ts). + +## Shared-file risk + +`src/server/responses/core.ts` is the common trunk for #4127, #4128, #4089, #3573, +#3761, and part of #4057. Parallel implementation is fine; merging is serial and the +main session issues the rebase instruction to whichever PR loses the race. + +`token-guardian.ts` / `account-store.ts` / `types/accounts.ts` are the second trunk, +shared by #4120 and #3848 — which is why they are one chain rather than two lanes. + +## Probes (main session, no PR) + +#3782 Claude Desktop in-conversation model switch, #3765 Astra cache plateau, +#3719 Anthropic thinking replay. Evidence attaches to the issues. + +## Out of this round + +#4076 closed (transient overlay; registration half covered by #3848). +#3506 direction comment posted (translation fidelity, not a proxy-side progress cutoff). +#2495 dropped after a feasibility study: not redundant with #4089, needs its own cycle. +#3978 deferred until the compaction status contract settles. diff --git a/devlog/_plan/260909_post249_scope_cleanup/050_delivery_record.md b/devlog/_plan/260909_post249_scope_cleanup/050_delivery_record.md new file mode 100644 index 0000000000..d806dfd4a3 --- /dev/null +++ b/devlog/_plan/260909_post249_scope_cleanup/050_delivery_record.md @@ -0,0 +1,68 @@ +# 050 — delivery record (post-2.49 round) + +Every scoped item landed on `dev`. Fifteen pull requests merged; fourteen issues closed. + +## Landed + +| PR | Issue | Lane | What landed | +|---|---|---|---| +| #4128 | #4122 | T3 | Spark 5h header windows attribute to the model limit, not the account short slot | +| #4132 | — | T3 | moved the closed Spark unit out of the product PR into _fin | +| #4114 | #4110 | T1 | client-compaction status compares the operator-owned root URL instead of inferring from a missing marker | +| #4127 | #4112 | T2 | non-streaming provider input overflow reaches the terminal context-overflow mapping | +| #4138 | #3573 | T2 | configurable inbound body admission limit, default 256 MiB, hard-ceilinged | +| #4133 | #4073 | T8 | SECURITY.md private follow-up path, no SLA published | +| #4136 | #4121 | T8 | documents the opencode-free Zen lock-in instead of forging x-opencode-session | +| #4084 | #4083 | T1 | 90s Codex WebSocket response prelude | +| #4068 | #3926 | T1 | Google AI Studio native models[] envelope accepted by catalog discovery | +| #4134 | #4057 | T6 | routed account label surfaced in Logs and an --account CLI filter | +| #4135 | #4089 | T5 | agentTaskRecovery runs on a mid-thread native-to-routed switch | +| #4140 | #4120 | T4 | terminal validation verdict persists for a revoked credential | +| #3848 | #3846 | T4 | quota-exhausted registration saved as validation-pending | +| #4146 | #3777 | T4 | explicit account plan field; Anthropic lands plan: null with the upstream gap recorded | +| #4142 | #3761 | T7 | opt-in provider-level webSearchBridge for key-auth passthrough destinations | + +Also closed by decision: #4076 (transient overlay; the registration half is #3848). +#3506 received a direction comment: translation fidelity, not a proxy-side progress cutoff. +#2495 was dropped after a feasibility study found it needs its own cycle rather than +riding on #4089. + +## What the parallel structure actually bought + +Three findings would not have surfaced from a single serial pass. + +T4 found that a textually clean git auto-merge of #3848 against `dev` produced two +`const needsReauth` declarations in one scope — valid text, invalid TypeScript — and a +collision on positional slot 5 of `fetchPoolAccountQuota` where taking either side alone +silently passes the wrong value at the other call site. That is the concrete reason the +account work was a chain and not two lanes. + +T1 found that #4084 and #4068 sat 106 commits behind `dev`, which the readiness gate +unticks past a 10-commit threshold, so neither could stay non-draft without a branch +update. It brought them current by merge rather than rebase, preserving contributor +authorship. + +T7's own audit of its diff caught four real defects before the final head: tool-call +leakage on a failed turn, search-cell ordering, abort handling, and the continuation +body ceiling. + +## Evidence standard held + +No local product suite, typecheck, build, or install was run in any lane. Every merge +cites exact-head remote CI. Cancelled runs were never counted as passing: where a +concurrency group cancelled a gate with no successful counterpart at the same SHA, the +run was re-triggered and a real conclusion waited for. + +#3848's readiness checklist was cleared by the maintainer rather than by author +attestation, because the local-CI box is an attestation the gate cannot disprove and +repository CI at the exact head is stronger evidence. The delivering lane declined to +tick it rather than assert a run it had not made. + +T2 disclosed that it did not drive the orchestrate FSM, because the C-to-D edge requires +a test receipt it could not honestly produce under the no-local-runs rule. It left the +session at IDLE rather than fabricate one. + +## Not done + +Probes #3782, #3765 and #3719 remain open; they need live client observation rather than +a code change. #3978 stays deferred until the compaction status contract settles. diff --git a/devlog/_plan/260910_250_regression_audit_release/000_plan.md b/devlog/_plan/260910_250_regression_audit_release/000_plan.md new file mode 100644 index 0000000000..604eaa2a17 --- /dev/null +++ b/devlog/_plan/260910_250_regression_audit_release/000_plan.md @@ -0,0 +1,57 @@ +# 2.50.0 regression audit and release — scope + +## Baseline and candidate + +- Released baseline: `v2.49.0`, `main` at `2f3f736299dca38861f8fb9c4326a4b4d7c664bc`. +- Audit candidate: `origin/dev` at `12c248f52bed88ea13be5b284c79a238feb592d1`, `package.json` version `2.50.0`. +- Delta: 127 commits (`git rev-list --count 2f3f73629..origin/dev`), 1066 changed files + (`git diff --name-only 2f3f73629...origin/dev | wc -l`). + +### File counts, by `git diff --name-only 2f3f73629...origin/dev | cut -d/ -f1 | sort | uniq -c` + +| Group | Files | Note | +| --- | --- | --- | +| `devlog` | 885 | No runtime. Not audited. | +| `src` | 62 | Audited: L1-L6 (L4 owns `src/server/management/*`, `src/server/{management-api,auth-cors,index}.ts`, `src/service.ts`) | +| `tests` | 42 | Read as evidence by every lane, not a lane of its own | +| `docs-site` | 28 | Not release-blocking on its own | +| `gui` | 27 | Audited: L4 | +| `readme` + root docs | 13 | `readme/` 8, plus `README.md`, `AGENTS.md`, `AGENTS_INSTALL.md`, `SECURITY.md`, `package.json` | +| `skills` | 3 | Audited: L6 (`skills/ocx` surface map) | +| `.github` | 3 | PR assets only, no workflow change | +| `structure` | 2 | Maintainer invariants | +| `scripts` | 1 | `scripts/test-layout/layout.json` | + +`git diff --shortstat 2f3f73629...origin/dev -- src gui docs-site scripts .github` is +121 files / +3322 / -280. That figure excludes `tests` and `package.json`; the full +non-`devlog` set is 181 files. An earlier revision of this doc attributed 121 to a +different folder set and derived the devlog count by subtraction; the reviewer +contradicted both with the commands above. + +## What this unit does + +Audit the product delta for release-blocking regressions, remediate anything blocking, +then run the 2.50.0 train: pre-move `dev`, promote the frozen candidate to `main`, +publish to npm, and verify the artifacts independently. + +## Authorization in force + +The user authorized parallel `xai/grok-4.6` subagents, a regression-audit PABCD cycle, +and the release itself. Subagents are read-only verifiers; the main session owns every +PABCD transition, every write, and every external action. + +## Out of scope + +- Landing unrelated open pull requests. 74 are open against `dev` + (`gh api "repos/lidge-jun/opencodex/pulls?state=open&base=dev&per_page=100" --jq 'length'`); + none is a release prerequisite, and pulling one in moves the candidate mid-audit. +- Re-auditing anything already released in 2.49.0. +- Any change to `devlog/` history or to third-party accounts. + +## Terminal outcomes + +- `DONE` — 2.50.0 on npm `latest` with `gitHead` matching the promoted `main` SHA, a + git tag, a GitHub release, and a recorded triage for every audit finding. +- `BLOCKED` — a release-blocking regression that cannot be fixed inside this scope, + or a missing external permission (npm trusted publishing, workflow dispatch). +- `NOOP` — the candidate is already published and verified. diff --git a/devlog/_plan/260910_250_regression_audit_release/010_audit_lanes.md b/devlog/_plan/260910_250_regression_audit_release/010_audit_lanes.md new file mode 100644 index 0000000000..f469ec2e9d --- /dev/null +++ b/devlog/_plan/260910_250_regression_audit_release/010_audit_lanes.md @@ -0,0 +1,98 @@ +# Audit lanes + +Six read-only lanes, each dispatched to an independent `xai/grok-4.6` subagent with a +fresh context. Read scopes are stated per lane so a finding traces to one owner; the +lanes never write, and the main session de-duplicates the returns. + +Every lane compares `2f3f73629...origin/dev` and must return exact `path:line` anchors. +A lane that finds nothing returns "no blocker" with the files it actually read. + +Lane coverage is checked mechanically: every path in +`git diff --name-only 2f3f73629...origin/dev -- src gui scripts skills package.json` +belongs to at least one lane. The first revision of this map left +`src/cli/{capabilities,index,models-runtime,observe}.ts` unowned, which is why L6 exists. + +## L1 — Responses and request pipeline + +`src/server/responses/{core,compact,context-overflow,policy-fallback,codex-ws-wire}.ts`, +`src/server/{chat-completions,chat-native,claude-messages,images,search,request-decompress,request-log}.ts`, +`src/claude/inbound.ts`, `src/web-search/{passthrough-bridge,ollama-executor}.ts`. + +Highest-risk lane: the new hosted web-search bridge (`passthrough-bridge.ts` +761), the +non-streaming context-overflow classification, agent-task recovery on mid-thread model +switches, and the configurable inbound body admission limit. + +## L2 — Codex accounts, quota, OAuth + +`src/codex/{account-runtime-state,account-store,account-usability,auth-api,auth-context,inject,quota,quota-auto-refresh}.ts`, +`src/oauth/{health,index,token-guardian}.ts`, `src/cli/{account,account-api,account-auth,account-extended}.ts`. + +Deferred validation, revoked pool grants, reauth-state clearing, the new account plan +field, and the token guardian. + +## L3 — Catalog, providers, combos, config + +`src/codex/catalog/{parsing,provider-fetch,sync}.ts`, `src/providers/{registry,quota,google-ai-studio-model-discovery,opencode-zen-rate-limit}.ts`, +`src/combos/{index,resolve}.ts`, `src/config.ts`, `src/types.ts`, `src/types/{accounts,config,provider}.ts`, +`src/clients/config-export/zcode.ts`, `src/lib/errors.ts`. + +Free-model pricing classification and filtering, quota-exhausted inactive marking, AI +Studio discovery restoration, cross-provider blocked-model redirects. + +## L4 — Management API, service, GUI + +`src/server/management/*`, `src/server/{management-api,auth-cors,index}.ts`, `src/service.ts`, +`gui/src/**`, `gui/tests/**`. + +The routed-account log label, the decode-rate column, management auth, the stale launchd +bootout recovery, and nine i18n locale files that must not contradict `en`. + +## L5 — Security, privacy, release surface + +Cross-cutting read of `src/lib/privacy.ts`, the body-size admission path, web-search +bridge egress, `package.json`, +`scripts/test-layout/layout.json`, `structure/{02_config-and-codex-home,04_transports-and-sidecars}.md`, +and the repository invariants in `AGENTS.md`: the Lab/core import boundary, the +synchronous `startServer` window, no tracked gitlink, and no request-body or credential +logging. + +The email-masking opt-out is the specific item to scrutinize: it deliberately weakens a +privacy default, so it must be off by default, must survive `bun run privacy:scan`, and +its CLI application in `src/cli/index.ts` (L6) must agree with the library default. + +## L6 — Operator CLI surface + +`src/cli/{capabilities,index,models-runtime,observe}.ts`, `skills/ocx/**`, and the +generated surface map that `tests/ci-workflows/skill-ocx.test.ts` asserts. + +`src/cli/index.ts` applies `privacy.maskEmails` to `ocx status` and is a +`service-lifecycle.yml` gate path. `capabilities.ts` adds a mutating `ocx account refresh` +with a consent warning. `models-runtime.ts` adds `--free-only`, which filters on +`pricingStatus === "free"` and therefore drops entries with no status. `observe.ts` adds +`--account` log filtering. + +## Blocker definition + +A finding blocks the release when any of these hold. + +1. **Regression against 2.49.0** — behavior that worked in the released tree and does not now. +2. **Crash, hang, or unbounded resource use** on any path a default install can reach. +3. **New-path functional breakage.** A feature introduced in this delta that does not do + what it claims still blocks, even though it is not a regression. This covers the + web-search bridge returning wrong or empty results, `--free-only` silently dropping + models with an absent `pricingStatus`, and a no-op `ocx account refresh`. +4. **Security or privacy weakening**, including a default that becomes less private, a + credential or request body reaching a log, or a loosened auth boundary. +5. **User-consent or identity-spend bypass**, per `AGENTS.md` "User-consent actions": any + path that spends the user's identity, credits, or reputation without the code-level + gate, including a CLI path that mints its own dashboard session. +6. **Core invariant violation**, per `AGENTS.md`: a Lab import reaching `src/router.ts`, + `src/server/lifecycle.ts`, or `src/server/responses/core.ts`, an `await` inside the + synchronous `startServer` activation window, or a tracked gitlink. +7. **Upgrade-path breakage**, not only first-run. An existing 2.49.0 install that keeps a + stale launchd job, a stale config, or a stale service unit after upgrading blocks. +8. **Broken release, packaging, or operator-surface contract**, including a `skills/ocx` + map that names a command the registry does not have. + +Style, missing coverage for unchanged code, and defects that already shipped in 2.49.0 +do not block; they are recorded as non-blockers with the evidence that they predate the delta. diff --git a/devlog/_plan/260910_250_regression_audit_release/020_release_plan.md b/devlog/_plan/260910_250_regression_audit_release/020_release_plan.md new file mode 100644 index 0000000000..a96f9faca2 --- /dev/null +++ b/devlog/_plan/260910_250_regression_audit_release/020_release_plan.md @@ -0,0 +1,77 @@ +# 2.50.0 release plan + +Derived from `.github/workflows/release.yml` as it exists on `dev`, not from precedent. +The gates below are what the workflow actually enforces. + +## What release.yml requires + +| Gate | Line | Requirement | +| --- | --- | --- | +| Branch | `release.yml:153-170` | Must run from `refs/heads/main` or `refs/heads/preview`. `main` refuses any version containing `-`; `preview` refuses any version that is not `*-preview.*`. | +| dist-tag | `release.yml:174-177` | `main` -> `latest`, `preview` -> `preview`. | +| CI | `release.yml:179-197` | A **successful `ci.yml` run with `--event push` on the release branch for `$GITHUB_SHA`**. A pull-request run is explicitly rejected, and a `workflow_dispatch` run on `dev` does not qualify. | +| Service lifecycle | `release.yml:225-237` | If any of `src/service.ts`, `src/cli.ts`, `src/cli/index.ts`, `src/lib/bun-runtime.ts`, `package.json`, `bun.lock`, `service-lifecycle.yml`, `release.yml` changed since the previous tag, a successful `service-lifecycle.yml` run for `$GITHUB_SHA` is required. This delta changes `src/cli/index.ts` and `package.json`, so the gate is armed. | +| dev ahead | `release.yml:242-249` | `bun scripts/version-line.ts assert-ahead `. `dev` is currently `2.50.0`, so publishing 2.50.0 fails until `dev` is pre-moved. | +| Publish | `release.yml:22-26` | `dry-run` defaults to **true**. A real publish needs `dry-run=false`. `expected-sha` is required and must equal the branch head at dispatch. | + +`$GITHUB_SHA` on `main` is the **promotion merge commit**, not the frozen `dev` SHA. +2.49.0 published from merge `2f3f73629`, not from its promoted tree commit `62849dfa6`. +Both `ci.yml` (`push: branches: [main, preview, dev]`, `paths: src/**, gui/**, ...`) and +`service-lifecycle.yml` (`push`, paths including `package.json` and `src/cli/index.ts`) +fire automatically on that merge, so the required runs appear without a dispatch — but +they must be waited for on that exact SHA. + +## Order + +1. **Freeze the candidate.** Record the exact `dev` SHA. A `workflow_dispatch` `lane=all` + run on `dev` is audit evidence for the tree, not the release gate; it tells us whether + the tree is green before we spend a promotion on it. +2. **Land blockers first.** Any wp3 fix goes to `dev` through a pull request, which moves + the candidate. Re-freeze and re-verify on the new SHA; old-head green is not evidence. +3. **Pre-move `dev`.** Dispatch `dev-version-bump.yml` with `intended-version=2.50.0`, + `mode=pre-move`. It is `on: workflow_dispatch`, but `dev-version-bump.yml:79` refuses + a non-default ref, so dispatch it with `--ref main`. It opens a pull request and does + **not** push to `dev`, because the `Protect dev` ruleset requires review. Merge that PR + so `dev` reads 2.51.0 before the publish reaches `assert-ahead`. Use the workflow rather + than a hand-written one-file PR so its tag/npm/version-line proofs run. +4. **Promote the frozen SHA to `main`, not current `dev`.** After step 3, `origin/dev` is + 2.51.0 and is no longer the candidate. Promotion always names the recorded freeze SHA + explicitly. + + The freeze SHA is **not** an ancestor of `main`, and `main` carries commits `dev` does + not, so there is nothing to fast-forward. Replicate the 2.49.0 method: branch from + `main`, merge the freeze SHA into that branch as a single + `release: promote verified 2.50.0 product tree to main` commit, then open the PR into + `main`. For 2.49.0 that was branch `codex/release-249-main-01a08498`, promote commit + `62849dfa6` (parents `9a27e8699` = old `main`, `ad36c7be8` = the dev freeze), merged by + PR #4117 as `2f3f73629`. + + The gate on this step is **tree equality**, not a green diff: after promotion, + `git rev-parse
^{tree}` must equal `git rev-parse ^{tree}`. + For 2.49.0 all three of the promote commit, the dev freeze, and the merged `main` tip + resolved to tree `66294fb3eb15592afd732f8b8e29d0bcc644fe9e`. Any conflict resolution + that changes that tree means a different product shipped than the one audited. + Record the merge SHA. +5. **Wait for the release-branch gates on the merge SHA.** Push-event `ci.yml` and + `service-lifecycle.yml` on `main` for that exact SHA, both successful. +6. **Dry-run, then publish.** Dispatch `release.yml` with `--ref main`, + `version=2.50.0`, `tag=latest`, `expected-sha=`, first with + `dry-run=true`, then with `dry-run=false` once the dry run is green. +7. **Verify artifacts.** `npm view @bitkyc08/opencodex dist-tags`, the `2.50.0` + `gitHead` against the promoted `main` SHA, the git tag, the GitHub release, tarball + integrity, and SLSA provenance. npm propagation lag returns 404 or a stale `latest`; + poll, never republish. +8. **`preview` is a separate line and is not part of this stable train.** + `origin/preview` is `2.49.0-preview.20260909`, and `release.yml:161-165` refuses a + preview publish whose version is not `*-preview.*`. Promoting the plain `2.50.0` tree + onto `preview` would break that branch's version line. If `preview` should carry this + tree, it needs its own `2.50.0-preview.` commit, decided after the stable + release lands. A branch sync and a preview npm publication are distinct operations. + +## Known failure modes to expect + +- Branch-keyed CI concurrency cancels an older run when a newer commit lands. A cancelled + aggregate is neither a product failure nor passing evidence. +- The registry-availability smoke can time out after npm already accepted the publish. + Inspect metadata, provenance, and tarball before considering a retry. +- `dev-version-bump.yml` rejects a dispatch from a non-default ref as an early warning. diff --git a/devlog/_plan/260910_250_regression_audit_release/030_evidence.md b/devlog/_plan/260910_250_regression_audit_release/030_evidence.md new file mode 100644 index 0000000000..5d70374aea --- /dev/null +++ b/devlog/_plan/260910_250_regression_audit_release/030_evidence.md @@ -0,0 +1,146 @@ +# Evidence ledger + +Filled as the cycles complete. Every row names the source of the claim. + +## Frozen facts + +| Item | Value | Source | +| --- | --- | --- | +| Released baseline | `v2.49.0` / `main` `2f3f736299dca38861f8fb9c4326a4b4d7c664bc` | `git log origin/main` | +| Audit candidate | `dev` `12c248f52bed88ea13be5b284c79a238feb592d1` | `git rev-parse origin/dev` | +| Candidate version | `2.50.0` | `package.json` | +| Commits in delta | 127 | `git rev-list --count 2f3f73629..origin/dev` | +| Changed files | 1066 total, 885 `devlog`, 181 non-`devlog` | `git diff --name-only 2f3f73629...origin/dev` | +| `preview` version line | `2.49.0-preview.20260909` | `git show origin/preview:package.json` | +| Open PRs against `dev` | 74 | `gh api "repos/lidge-jun/opencodex/pulls?state=open&base=dev&per_page=100" --jq 'length'` | + +## wp1 — roadmap audit (A gate) + +Reviewer: `xai/grok-4.6`, agent `01a08a86-1352-77a2-98bd-5c167b5479c8`, read-only, fresh context. +Verdict: **FAIL**. Every finding was verified independently by the main session before folding. + +| # | Finding | Verified by | Fold | +| --- | --- | --- | --- | +| R1 | `src/cli/{capabilities,index,models-runtime,observe}.ts` belonged to no lane | `git diff --name-only` vs the lane map | Lane **L6** added | +| R2 | Blocker definition missed new-path breakage, consent/identity-spend bypass, `AGENTS.md` core invariants, upgrade-path recovery, and operator-surface drift | `AGENTS.md:43-83`, `AGENTS.md:150-169` | Definition rewritten to 8 clauses | +| R3 | `release.yml:179-197` needs a push-event `ci.yml` run on the release branch for `$GITHUB_SHA`; a `dev` dispatch does not qualify | `sed -n '179,197p' .github/workflows/release.yml` | Order rewritten: gates run on the `main` merge SHA | +| R4 | `service-lifecycle.yml` is gated on `$GITHUB_SHA`, and this delta arms it via `src/cli/index.ts` + `package.json` | `sed -n '225,237p' .github/workflows/release.yml` | Made an explicit step on the merge SHA | +| R5 | `preview` refuses a non-`*-preview.*` version, and `origin/preview` is `2.49.0-preview.20260909` | `sed -n '161,165p' release.yml`; `git show origin/preview:package.json` | `preview` removed from the stable train | +| R6 | `dev-version-bump.yml` is `on: workflow_dispatch`, not `workflow_call`-only | `sed -n '24,45p' .github/workflows/dev-version-bump.yml` | Pre-move now uses the workflow, not a hand PR | +| R7 | `dry-run` defaults to `true` and the run must come from `refs/heads/main` | `sed -n '22,26p'`, `sed -n '153,170p'` `release.yml` | Dry-run-then-publish made explicit | +| R8 | Scope doc misattributed the 121-file figure, derived the devlog count by subtraction, and said 20 open PRs | `git diff --shortstat`; `gh api ... --jq 'length'` -> 74 | Counts table rewritten from the real command | + +Round 2 verdict: **GO-WITH-FIXES**. R1-R8 all confirmed FIXED with anchors, and the +mechanical lane-coverage check over the 94 changed product paths returned zero unlaned. +Three new findings were raised and folded: + +| # | Finding | Verified by | Fold | +| --- | --- | --- | --- | +| R9 | After the pre-move, `origin/dev` is 2.51.0; promoting current `dev` would publish the wrong version. The plan never pinned the promotion source to the freeze SHA | `020_release_plan.md:37` as written | Step 4 now names the recorded freeze SHA explicitly | +| R10 | The freeze SHA is not an ancestor of `main` and `main` carries commits `dev` lacks, so a naive `base=main head=` PR is a 127-commit history merge rather than a tree promotion | `git merge-base --is-ancestor 12c248f52 origin/main` -> 1 | Step 4 documents the 2.49.0 branch-and-merge method and makes **tree equality** the gate: promote tree, dev freeze tree, and merged `main` tree all resolved to `66294fb3eb15592afd732f8b8e29d0bcc644fe9e` for 2.49.0 | +| R11 | `000_plan.md` said `src` is audited by L1-L3, L5, L6, but L4 owns `src/server/management/*` and `src/service.ts` | `010_audit_lanes.md:44` | Counts table corrected to L1-L6 | + +Also folded from the round-2 residual: `dev-version-bump.yml:79` refuses a non-default +ref, so the pre-move dispatch must use `--ref main`; and L5 no longer names OrcaRouter +key-exchange bounds, which are not in this delta. + +## Audit findings (wp2) + +Six `xai/grok-4.6` lanes, dispatched in one round, fresh context each, read-only. +**All six returned `NO-BLOCKER`.** No finding matched any of the eight blocker clauses. + +| Lane | Agent | Verdict | Files read | +| --- | --- | --- | --- | +| L1 responses / web-search | `01a08aa6-5954-76f1-a205-f4a85b76457f` | NO-BLOCKER | 31 | +| L2 accounts / quota / OAuth | `01a08aa6-59f5-79d3-a567-556a80d05c84` | NO-BLOCKER | 23 | +| L3 catalog / providers / config | `01a08aa6-5aa1-7132-b053-776bb02b0fe7` | NO-BLOCKER | 36 | +| L4 management / service / GUI | `01a08aa6-5b57-7343-9e1c-b4b3ea186478` | NO-BLOCKER | 41 | +| L5 security / privacy / release | `01a08aa6-5c11-7c52-b4db-6ec685159a33` | NO-BLOCKER | 40 | +| L6 operator CLI | `01a08aa6-5ccd-76c0-af89-83cf0ea80e28` | NO-BLOCKER | 34 | + +### Non-blocking findings, with dispositions + +| ID | Lane | Anchor | What it is | Disposition | +| --- | --- | --- | --- | --- | +| F1 | L1 | `src/web-search/passthrough-bridge.ts:503` | If the upstream emits a `web_search` function call and then `response.failed`/`incomplete`, `decide()` ends without `searchEndFrames`, so a client can keep a "Searching the web" cell open under a failed turn. The explicit `kind === "fail"` path does close it. Opt-in bridge only, default off. | `SHIP` — cosmetic, on a feature that must be explicitly enabled | +| F2 | L3 | `src/codex/catalog/provider-fetch.ts:1415` | Classification reads `pricing.prompt`/`completion` only, so a row with both at zero plus a paid `pricing.request`/`image`/`web_search` key would classify `free`. No in-tree fixture has that shape. | `SHIP` — `RUNTIME-CHECK` resolved: `pricingStatus` is consumed only by `src/cli/models-runtime.ts:68` and `gui/src/pages/models-shared.ts:85` as a display filter. It gates no routing and no spend, so the worst case is a mislabelled row, not a charge | +| F3 | L3 | `src/codex/catalog/provider-fetch.ts:1996` | A custom google-adapter gateway returning both `data[]` and a non-Google `models[]` would take the AI Studio parser with zero `generateContent` rows and publish an authoritative empty catalog. The `data[]`-only case is covered by `tests/adapters/google/google-models-listing.test.ts`. | `SHIP` — requires a dual-envelope body no known gateway sends | +| F4 | L2 | `src/oauth/token-guardian.ts:257` vs `src/codex/auth-api.ts:1265` | `isCodexAccountUsable` does not read the persisted terminal flag, so after a restart routing can attempt a dead grant once more. | `PRE-EXISTING` — same process-lifetime pattern as 2.49.0; the guardian that writes the flag is opt-in and default off | +| F5 | L2, L5 | `src/oauth/health.ts:231` | A revoked grant with no persisted terminal and no in-memory reauth can still project healthy after a restart when the guardian never ran. | `PRE-EXISTING` — 2.49.0 behavior; 2.50.0 only adds the `validation_pending` projection, which is strictly more informative | +| F6 | L4 | `src/service.ts:2386` | Install/repair bootout evicts the loaded job, including one that is currently serving, after the plist has been rewritten. | `SHIP` — this is the intended #4141 repair; `startLaunchd` at :2422 still refuses that eviction on the ordinary start path | +| F7 | L6 | `src/cli/capabilities.ts` | `ocx models live --free-only` is a real new flag that is not a declared capability, so it does not reach the generated surface map. | `SHIP` — documentation gap, not the map/registry split that once shipped a phantom `ocx request-history` | +| F8 | CI | `tests/codex-integration/codex-log-guard-maintenance-coderabbit.test.ts` | `classifies continuous progress stopped by MAX_ITERATIONS as bounded work` timed out at 60s on Windows shard 5/6 of run 34457689927 attempt 1, taking 112.8s. | `PRE-EXISTING` — proved by byte identity against the released tree: `src/codex/log-guard/maintenance.ts` (`81b3a465b`), the test file (`54e83bba2`), and `tests/helpers/remove-tree.ts` (`53e36a584`) are the same blobs at `2f3f73629` and `origin/dev`. Nothing in this delta can have caused it | + +F8 note: `tests/preload.ts` is the one file on that failure path this delta does touch, +and its diff is a comment block with no statement change, so the byte-identity argument is +complete rather than merely true. Attempt 2 of the run passed on rerun. The full reasoning, +including why the timeout is not hardened before the release, is in `060_release_readiness.md`. + +### Independent re-derivation by the main session + +Nothing was accepted on a lane's authority. Re-checked directly: + +| Claim | Command | Result | +| --- | --- | --- | +| Email masking on by default | `rg -n maskEmails src/lib/privacy.ts` | `config?.privacy?.maskEmails !== false` — absent, malformed, and non-boolean all mask | +| Inbound body limit safe default | `rg -n MAX_DECOMPRESSED_BODY_BYTES src/server/request-decompress.ts` | 256 MiB, returned when the configured value is undefined | +| No Lab import in the three core files | `rg -n 'from "[./]*lab/' src/router.ts src/server/lifecycle.ts src/server/responses/core.ts` | no match | +| `startServer` still synchronous | `rg -n 'function startServer' src/server/index.ts` | `export function startServer(...): Server` — not `async` | +| No tracked gitlink | `git ls-files -s \| grep -c '^160000'` | 0; `.gitmodules` absent | +| i18n keys in every locale | per-locale `rg -c` for the four new keys | 6 matches in each of en, ko, de, fr, ja, ru, tr, zh, zh-TW | +| Web-search bridge opt-in | `rg -n webSearchBridge src/` | armed only by `providers..webSearchBridge.enabled` | + +## Release artifacts (wp4) + +| Gate | Evidence | Status | +| --- | --- | --- | +| Candidate-tree CI (`dev` dispatch, audit evidence only) | run 34457689927, `lane=all` on `12c248f52`, attempt 2 conclusion `success` | done | +| Freeze tree to reproduce on `main` | `git rev-parse 12c248f52^{tree}` = `d8f5a7143bcd6cb86185c4e8d4c6a6c4ad0fa822` | recorded | +| `dev` pre-move to 2.51.0 | `dev-version-bump.yml` run 34463313646 opened PR #4194; merged; `origin/dev` = `cf44f6fe887d19f53ede1e09abfe0fe3cf137059`, `package.json` 2.51.0 | done | +| Promotion commit | `3a3de889b6ef3217497f6c5029acf08aec09c0cf`, parents `2f3f73629` (old `main`) and `12c248f52` (freeze), tree `d8f5a7143bcd6cb86185c4e8d4c6a6c4ad0fa822` | done | +| `main` promotion merge SHA | PR #4195 merged; `origin/main` = `2d4d7a22381a2e497c2442902104619e25f937c7`, tree `d8f5a7143bcd6cb86185c4e8d4c6a6c4ad0fa822`, version 2.50.0 | done | +| Push-event Cross-platform CI on merge SHA | run 34464454730, conclusion `success` | done | +| Service lifecycle on merge SHA | run 34464454609, conclusion `success` | done | +| `release.yml` dry run | run 34465317829, `validate-dispatch` and `publish` both `success` | done | +| `release.yml` publish | run 34465442114, `dry-run=false`, `expected-sha=2d4d7a223`; `npm publish --tag latest --access public` printed `+ @bitkyc08/opencodex@2.50.0` | done | +| npm `latest` = 2.50.0 | `npm view @bitkyc08/opencodex dist-tags` -> `{"preview":"2.48.0-preview.20260908","latest":"2.50.0"}` | done | +| `gitHead` matches promoted `main` | `npm view @bitkyc08/opencodex@2.50.0 gitHead` = `2d4d7a22381a2e497c2442902104619e25f937c7`, identical to `origin/main` | done | +| git tag + GitHub release | `git rev-list -n1 v2.50.0` = `2d4d7a223`; release `v2.50.0` published 2026-09-10T10:21:15Z, not a draft, not a prerelease | done | +| Tarball integrity | Downloaded tarball hashes to `sha512-lrcM1sBfjbjqB3h5i2q7A6FbPOXxrdxqhWC7S+w0+oCOZ+9f8ucCgXPt9D2p81dS78ZfYYSJZuDWbU1Ov0VOhQ==`, equal to `dist.integrity`; manifest version 2.50.0; 1094 files, 23,923,744 bytes unpacked | done | +| Published source bytes | `src/lib/privacy.ts`, `src/web-search/passthrough-bridge.ts`, and `src/cli/models-runtime.ts` inside the tarball are SHA-256 identical to the same paths at `2d4d7a223` | done | +| Provenance | Registry attestations are `npm/attestation/tree/main/specs/publish/v0.1` and `slsa.dev/provenance/v1` | done | + +### The registry smoke timed out, and why nothing was republished + +`npm publish` printed `+ @bitkyc08/opencodex@2.50.0` at 10:20:46, and the workflow's +own `Post-publish registry smoke` then failed to read the version back through six bounded +attempts over roughly 27 seconds. It emitted +"npm publish succeeded, but registry verification remains pending; continuing GitHub +release creation without republishing" and proceeded, which is the correct behavior: the +publication receipt already existed. + +The registry served 2.50.0 about 20 minutes after the publish. It was polled, never +republished. This is the documented failure mode — a timed-out availability smoke is not a +failed publish, and republishing on it is how a release gets damaged. + +### `preview` is intentionally not part of this release + +`origin/preview` remains `2.49.0-preview.20260909` and the npm `preview` dist-tag remains +`2.48.0-preview.20260908`. `release.yml:161-165` refuses a preview publish whose version is +not `*-preview.*`, so promoting the plain 2.50.0 tree onto that branch would break its +version line. Bringing `preview` forward needs its own `2.50.0-preview.` commit and +is a separate decision. + +### Gates that failed by design on the promotion PR + +`enforce-target` failed #4195 with "wrong base (main); missing UI screenshot". That gate is +written for contributor pull requests: `main` receives only release promotions, and a +promotion necessarily carries dashboard files while changing no UI of its own. The 2.49.0 +promotion PR #4117 failed the same check and was merged the same way. `AGENTS.md` records +the maintainer promotion exception, and the gates that actually decide are the push-event +runs on the merge SHA, which `release.yml` independently requires. + +Local `prepush` was skipped on the promotion branch. It runs the full ~850-file suite +against a tree byte-identical to one already green on Linux, macOS, and Windows +(`lane=all` run 34457689927), and it was additionally blocked waiting on another Bun test +lock. The remote push-event runs on `2d4d7a223` are the evidence that counts. diff --git a/devlog/_plan/260910_250_regression_audit_release/040_triage_protocol.md b/devlog/_plan/260910_250_regression_audit_release/040_triage_protocol.md new file mode 100644 index 0000000000..fcc64b9143 --- /dev/null +++ b/devlog/_plan/260910_250_regression_audit_release/040_triage_protocol.md @@ -0,0 +1,71 @@ +# Triage and remediation protocol (wp3) + +wp2 returns six lane reports. This is how they become a release decision. + +## 1. Normalize + +Each lane return is split into individual findings. A finding is only admitted with an +exact `path:line` anchor or a literal command and its output. An unanchored or misanchored +assertion is recorded as **unsubstantiated** and the main session **must** re-derive it +against the tree. Dropping it undecided is not an option: a real blocker described with a +wrong line number is still a real blocker, and the anchor rule exists to make triage cheap, +not to discard findings. + +Findings from different lanes that name the same defect are merged, keeping every anchor. + +## 2. Classify + +Apply the eight-clause blocker definition in `010_audit_lanes.md`. Each finding gets +exactly one disposition. + +| Disposition | Meaning | Action | +| --- | --- | --- | +| `BLOCK` | Matches a blocker clause | Must be fixed and landed on `dev` before promotion | +| `SHIP` | Real but does not match a clause | Recorded here, filed as an issue if it deserves one, released as is | +| `PRE-EXISTING` | The same user-visible failure was reachable on `2f3f73629` | Not this release's problem; requires the proof below | +| `RUNTIME-CHECK` | Plausible but only decidable by running something | Must be resolved before promotion, by a targeted test, a CI job, or a reasoned rebuttal — never left as a confidence label | +| `WRONG` | The lane misread the code | Rebutted with the anchor that disproves it | + +A finding is `PRE-EXISTING` only when the **user-visible failure** was reachable on the +baseline — not merely that some function it touches already existed. Showing that an old +helper is unchanged proves nothing when a new caller reaches it under new conditions; +clause 3 exists precisely for that case. Acceptable proof is byte identity of every file on +the failure path (`git rev-parse 2f3f73629:` equal to `git rev-parse origin/dev:` +for each), a test that fails on the baseline, or a baseline CI run showing the same failure. + +`BLOCK` may never be downgraded to `SHIP`, and it may only become `PRE-EXISTING` under the +proof above. Weak-proof downgrade is the same evasion as reclassifying to `SHIP`, taken by a +longer route. + +## 3. Remediate + +Every `BLOCK` fix follows the repository's normal contribution path — a branch off the +current `dev`, a focused regression test next to the existing tests for that subsystem, +a pull request against `dev` using `.github/PULL_REQUEST_TEMPLATE.md`, and the exact-head +CI evidence the branch policy requires. No direct push to `dev`; the ruleset rejects it +regardless of `--no-verify`. + +Landing a fix **moves the candidate**. When that happens: + +1. Record the new `dev` SHA as the freeze SHA, superseding `12c248f52`. +2. Re-run the candidate-tree CI dispatch on the new SHA. Green on the old head proves + nothing about the new one. +3. Re-run only the lanes whose read scope intersects the fix, not all six. + +## 4. Escalate rather than weaken + +A `RUNTIME-CHECK` finding that cannot be resolved is treated as a `BLOCK`, not as a +`SHIP`. An unfalsified hang or teardown risk is not evidence of safety. + +If a `BLOCK` cannot be fixed inside this scope — it needs a design decision, an external +credential, or a change the user has not authorized — the release stops and the outcome is +`BLOCKED`. Reclassifying a blocker to `SHIP` to reach a release is the one move this +protocol forbids. The alternative that *is* allowed: revert the offending commit range from +the candidate and release without that feature, which is a smaller change than shipping a +known defect. + +## 5. Record + +Every finding lands in the wp2 findings table in `030_evidence.md` with its ID, lane, +anchor, failure mode, disposition, and — for `BLOCK` — the PR and merge SHA that resolved +it. A finding with no row in that table did not happen. diff --git a/devlog/_plan/260910_250_regression_audit_release/050_lane_packets.md b/devlog/_plan/260910_250_regression_audit_release/050_lane_packets.md new file mode 100644 index 0000000000..bb4706771a --- /dev/null +++ b/devlog/_plan/260910_250_regression_audit_release/050_lane_packets.md @@ -0,0 +1,116 @@ +# Lane dispatch packets (wp2) + +Six `xai/grok-4.6` subagents, dispatched in one round, fresh context each, read-only. +They run concurrently because their questions are independent; none reads another's output. + +## Shared packet frame + +Every packet carries the same frame, with only `SCOPE` and `QUESTIONS` differing. + +- **Repository:** `/Users/jun/.codex/worktrees/b53a/opencodex`, on branch + `codex/260910-250-regression-audit-release`. That branch adds `devlog/` commits on top of + the freeze SHA `12c248f52`; every `src`, `gui`, `tests`, and `scripts` file is identical + to the freeze. **Do not `git checkout` the freeze SHA** — it would detach HEAD on the + worktree we are releasing from. Read `origin/dev` through `git show` if an exact freeze + read is needed. +- **Comparison:** `git diff 2f3f73629...origin/dev -- `. `2f3f73629` is + released `v2.49.0`; the right side is the 2.50.0 candidate. +- **Read the current tree, not only the diff.** A change is often half in the diff and half + in an unchanged caller. Following a symbol into a file outside the lane's diff is expected. +- **MUST NOT:** no writes, edits, commits, pushes, stashes, branch changes, or + `git checkout`/`git switch`/`git restore` of any kind; no test suite, typecheck, build, or + install; no mutating `gh` call. Read-only `git` and `gh api`/`gh run list` only. Do not + fix anything found — report it. +- **PROOF:** every finding needs an exact `path:line` on the candidate side, or a literal + command with its output. Unanchored claims are re-derived by the main session, so an + approximate anchor costs a round trip rather than being silently dropped. +- **RETURN FORMAT:** `VERDICT` (`NO-BLOCKER` or `BLOCKERS-FOUND`), then one numbered entry + per finding with `ANCHOR`, `WHAT BREAKS` (the concrete user-visible failure and the input + that triggers it), `CLAUSE` (a number from the list below, or `non-blocking`), and + `CONFIDENCE` (`certain` / `likely` / `needs-runtime-check`). Then `FILES READ`. +- **DECISION BOUNDARY:** the lane reports evidence and unresolved judgments. It does not + decide whether the release proceeds, does not rank against other lanes, and does not + weaken a finding because it looks hard to fix. + +### The eight blocker clauses, carried inline + +A lane cannot answer `CLAUSE` from a file it was not given, so the list travels with the +packet: (1) regression against 2.49.0; (2) crash, hang, or unbounded resource use on a +reachable default path; (3) new-path functional breakage — a feature added in this delta +that does not do what it claims, even though it is not a regression; (4) security or +privacy weakening; (5) user-consent or identity-spend bypass; (6) core invariant violation +(Lab reaching `src/router.ts`, `src/server/lifecycle.ts`, or `src/server/responses/core.ts`; +an `await` in the synchronous `startServer` activation window; a tracked gitlink); +(7) upgrade-path breakage for an existing 2.49.0 install; (8) broken release, packaging, +or operator-surface contract. + +A lane that finds nothing returns `NO-BLOCKER` and its `FILES READ`. A short honest +return beats a long speculative one. + +## Per-lane questions + +**L1 — responses and request pipeline.** Does the hosted web-search bridge arm only when +opted in, and does a failure fall back rather than hang or leak? Is the search cell placed +in stream order, and are bridge continuations bounded? Does `src/web-search/ollama-executor.ts` +bound its own errors and timeouts? Does the non-streaming context-overflow classification +return a classified reply on every exhausted-target path? Does agent-task recovery on a +mid-thread model switch preserve encrypted content? Does the configurable body admission +limit still have a safe default and reject rather than buffer? In `src/claude/inbound.ts`, +does emitting mid-conversation `role:"system"` as chronological `developer` items change +what the model obeys on an ordinary Claude Code turn? In `src/server/responses/codex-ws-wire.ts`, +what is the cost of the 30s to 90s prelude timeout when the upstream is actually hung? +Does the new `account` filter in `src/server/request-log.ts` match the value that is +actually stored, including when masking is on? Does the OpenCode Zen free-tier message +rewrite in `src/server/chat-native.ts` alter a paid-tier request? + +**L2 — codex accounts, quota, OAuth.** Can a deferred validation leave an account neither +usable nor visibly failed? Does a revoked pool grant reach a terminal verdict instead of +retrying forever? Does clearing reauth state ever clear it for the wrong account? Does the +new account plan field ever carry a value that identifies the user into a log or the wire? +Does `src/oauth/health.ts` report healthy for an account that cannot actually serve? Does +the quota-header dual-write in `src/codex/quota.ts` ever attribute one account's window to +another? + +**L3 — catalog, providers, combos, config.** Does free-model classification ever mark a +paid model free, or drop a model whose `pricingStatus` is absent rather than `"free"`? +Does quota-exhausted inactive marking recover when quota returns? Does the AI Studio +discovery restoration change behavior for custom gateways? Can a cross-provider blocked +model redirect cycle? Does the keyless free-tier `MissingSessionID` rewrite in +`src/providers/opencode-zen-rate-limit.ts` mask a real auth failure? In `src/config.ts` and +`src/types/config.ts`, what do `privacy.maskEmails` and the inbound body limit resolve to +when the key is absent or malformed — does the schema degrade to a safe default or to +`undefined`? Does the `zcode` config export leak anything it did not before? + +**L4 — management API, service, GUI.** Does any management route lose its auth check? Does +the routed-account label reach a response a browser can read without a session? Does the +launchd bootout recovery ever tear down a healthy job? In `gui/src/pages/models-shared.ts` +and `Models.tsx`, can `freeOnlyInForce` stay true after the control disappears and leave +the user with an empty model list? Does the decode-rate column +(`src/server/management/shared.ts`, `gui/src/pages/Logs.tsx`) stay out of request history +as intended, and is the rate meaningful when the sample is tiny? Do the nine non-English +locales carry the keys this delta actually added — `models.freeOnly`, +`models.inactiveNoCredit`, `logs.detail.decodeTokPerSec`, +`pws.healthLabel.validationPending` — and does any translation invert the meaning of the +English source? Does the account-pool `validationPending` copy tell the operator what to do? + +**L5 — security, privacy, release surface.** Is email masking on by default in the resolved +config, and does the opt-out require an explicit operator action? Read every log call site +added in this delta and name any that can emit an address, token, request body, or account +identifier — static reading only, do not run the scan. Does any `src/lab/` module now reach +`src/router.ts`, `src/server/lifecycle.ts`, or `src/server/responses/core.ts` through any +import chain? Is there any `await` in the synchronous `startServer` activation window in +`src/server/index.ts`, and does the bind-time `maxRequestBodySize` wiring there agree with +the configured limit and its default? Is any gitlink tracked? + +**L6 — operator CLI surface.** Does `ocx status` apply the same masking default as the +library? Does `ocx account refresh` mint or reuse a dashboard session, and does it spend +the user's identity without the code-level gate? Does `--free-only` drop models with an +absent `pricingStatus`? Does `--account` filtering match on a value that is masked in the +stored log? Does the committed `skills/ocx` surface map name any command +`src/cli/capabilities.ts` does not register? + +## What the main session does with the returns + +Nothing is accepted on the lane's authority. Every `BLOCKERS-FOUND` entry is re-derived +against the tree before it enters the wp2 findings table, exactly as the round-1 roadmap +findings were. `040_triage_protocol.md` governs from there. diff --git a/devlog/_plan/260910_250_regression_audit_release/060_release_readiness.md b/devlog/_plan/260910_250_regression_audit_release/060_release_readiness.md new file mode 100644 index 0000000000..37f429193b --- /dev/null +++ b/devlog/_plan/260910_250_regression_audit_release/060_release_readiness.md @@ -0,0 +1,63 @@ +# Release-readiness decision (wp3) + +wp3 was scoped to triage and remediate release-blocking findings. **The audit produced +none**, so there is nothing to remediate and this cycle is a decision record instead. + +## The decision + +Promote and publish freeze SHA `12c248f52bed88ea13be5b284c79a238feb592d1` as 2.50.0. + +## What the decision rests on + +| Evidence | Detail | +| --- | --- | +| Six independent lanes | All returned `NO-BLOCKER` against the eight clauses, 23-41 files read each, covering all 94 changed product paths | +| Candidate-tree CI | Run `34457689927`, `ci.yml` with `lane=all` on `12c248f52`, attempt 2 conclusion **success** | +| Focused local suites | 84 pass / 0 fail across the web-search bridge, Lab/core boundary, privacy masking, `skills/ocx` surface, body-size limit, live service-manager guard, and context-overflow | +| Main-session re-derivation | Seven invariants re-checked directly rather than accepted from a lane | +| Independent decision audit | Reviewer round 4 returned **GO** and confirmed the release sequence has no defects | +| Freeze tree | `git rev-parse 12c248f52^{tree}` = `d8f5a7143bcd6cb86185c4e8d4c6a6c4ad0fa822`, the value the promotion merge must reproduce | + +## The CI flake, and why it is not being fixed first + +Attempt 1 of run `34457689927` failed one job. One test — +`CodeRabbit Log Guard reclaim regressions > classifies continuous progress stopped by +MAX_ITERATIONS as bounded work` — exceeded the suite-wide `--timeout 60000` after taking +112,853.92 ms on Windows shard 5/6. Everything else passed: 4058 pass, 15 skip, 1 fail. + +Every file on that failure path is byte-identical to the released 2.49.0 tree: + +| File | Blob at `2f3f73629` and at `origin/dev` | +| --- | --- | +| `src/codex/log-guard/maintenance.ts` | `81b3a465b5dbddc11c7431b99fec52012b61cf65` | +| `tests/codex-integration/codex-log-guard-maintenance-coderabbit.test.ts` | `54e83bba2a62b9fffd39f88839f3c339e1c26080` | +| `tests/helpers/remove-tree.ts` | `53e36a584c627b75a3c3b58a28e2bd17d7636b8b` | + +`tests/preload.ts` is the one file on that path the delta does touch, and the change is +a comment block only — no statement changed. The round-4 reviewer caught that the first +version of this proof enumerated three blobs and called it "every file on the failure +path"; the diff is recorded here so the claim is complete rather than merely true. + +Rerunning the failed job produced attempt 2 with conclusion `success`, which also +demonstrates the mechanic the release gate depends on: `release.yml` searches +`gh run list --workflow ci.yml --commit "$GITHUB_SHA" --event push` and reads the run's +conclusion, and a rerun updates that conclusion in place. + +Hardening the timeout would move the freeze SHA, void this audit, and reopen every gate +for a test that 2.49.0 already shipped with the same bytes and the same limit. The +mitigation is the rerun, applied again on the promotion merge if it recurs. + +## Recorded limits of the audit + +The round-4 reviewer named three, and they are recorded rather than argued away. + +1. **Every lane was a static reader.** `NO-BLOCKER` means no clause matched a read, not + that the new SSE bridge cannot hang at runtime. The 84 focused tests and the full + `lane=all` CI run are what cover the dynamic half; the lane verdicts alone are not. +2. **The re-derivation table checks invariants, not the packet questions.** It confirms + masking, the body limit, the Lab boundary, `startServer`, gitlinks, i18n keys, and the + bridge opt-in. It does not independently re-answer the inbound `developer` remap, the + 90-second WS prelude, or `freeOnlyInForce`; those rest on the lane read plus CI. +3. **Treating an unresolved `RUNTIME-CHECK` as a `BLOCK` creates pressure to under-report + it.** Exactly one finding carried that label and it was resolved by tracing consumers. + A lane that quietly downgrades rather than raising the label would not be visible here. diff --git a/devlog/_plan/260910_250_regression_audit_release/070_release_execution.md b/devlog/_plan/260910_250_regression_audit_release/070_release_execution.md new file mode 100644 index 0000000000..d5c03912cb --- /dev/null +++ b/devlog/_plan/260910_250_regression_audit_release/070_release_execution.md @@ -0,0 +1,103 @@ +# 2.50.0 execution runbook (wp4) + +The exact sequence, with the value each step must record. `020_release_plan.md` says why; +this says what to run. Every SHA below is written down as it is produced, because the next +step verifies against it rather than against "current". + +## Fixed inputs + +| Name | Value | +| --- | --- | +| Freeze SHA | `12c248f52bed88ea13be5b284c79a238feb592d1` | +| Freeze tree | `d8f5a7143bcd6cb86185c4e8d4c6a6c4ad0fa822` | +| Version | `2.50.0` | +| Previous release | `v2.49.0` at `main` `2f3f736299dca38861f8fb9c4326a4b4d7c664bc` | +| Default branch | `main` | + +## Step 1 — pre-move `dev` + +```sh +gh workflow run dev-version-bump.yml --ref main \ + -f intended-version=2.50.0 -f mode=pre-move +``` + +The workflow opens a pull request; it cannot push to `dev` because the `Protect dev` +ruleset requires review. Merge that PR, then confirm: + +```sh +git fetch origin dev +git show origin/dev:package.json | head -3 # must read 2.51.0 +``` + +Record: the bump PR number and the merged `dev` SHA. + +Why this is first: `release.yml:242-249` runs +`bun scripts/version-line.ts assert-ahead 2.50.0`, which fails while `dev` +is still 2.50.0. Doing it after the promotion would strand a published-but-refused release. + +## Step 2 — promote the freeze SHA to `main` + +```sh +git fetch origin main +git switch -c codex/release-250-main origin/main +git merge --no-ff 12c248f52 -m "release: promote verified 2.50.0 product tree to main" +git rev-parse HEAD^{tree} # must equal d8f5a7143bcd6cb86185c4e8d4c6a6c4ad0fa822 +``` + +If the tree does not match, a conflict resolution changed the product and the audit no +longer describes what would ship. Stop and re-derive rather than adjusting the expectation. + +Open the PR into `main` using `.github/PULL_REQUEST_TEMPLATE.md`, merge it, then: + +```sh +git fetch origin main +git rev-parse origin/main # record as MERGE_SHA +git rev-parse origin/main^{tree} # must still equal the freeze tree +``` + +Record: the promotion PR number, `MERGE_SHA`, and the confirmed tree. + +## Step 3 — wait for the release-branch gates on `MERGE_SHA` + +Both fire automatically on the merge push — `ci.yml` because `main` is in its push +branches and `src/**`/`gui/**` changed, `service-lifecycle.yml` because `package.json` and +`src/cli/index.ts` are in its push paths. + +```sh +gh run list --workflow ci.yml --commit "$MERGE_SHA" --event push --json conclusion,url +gh run list --workflow service-lifecycle.yml --commit "$MERGE_SHA" --json conclusion,url +``` + +Both must reach `success`. If Windows shard 5/6 times out on the Log Guard reclaim test +again, rerun that job in place with `gh run rerun --failed`; the gate reads the run's +conclusion, which a rerun updates. That is the recorded mitigation, not an improvisation. + +## Step 4 — dry run, then publish + +```sh +gh workflow run release.yml --ref main \ + -f version=2.50.0 -f tag=latest -f expected-sha="$MERGE_SHA" -f dry-run=true +``` + +A dry run still executes `prepublishOnly` (typecheck plus the GUI build), so a green dry +run is real evidence about the package, not a formality. Only then: + +```sh +gh workflow run release.yml --ref main \ + -f version=2.50.0 -f tag=latest -f expected-sha="$MERGE_SHA" -f dry-run=false +``` + +## Step 5 — verify the artifacts independently + +```sh +npm view @bitkyc08/opencodex dist-tags --json +npm view @bitkyc08/opencodex@2.50.0 version gitHead dist.integrity --json +git ls-remote --tags origin | grep v2.50.0 +gh release view v2.50.0 --json tagName,isDraft,isPrerelease,createdAt +``` + +`gitHead` must equal `MERGE_SHA`. npm propagation lag shows a 404 or a stale `latest` +for a while; poll. **Never republish because a smoke step timed out** — inspect metadata, +provenance, and the tarball first, because npm may already have accepted the publish. + +Record every value into the release-artifacts table in `030_evidence.md`. diff --git a/devlog/_plan/260910_250_regression_audit_release/080_delivery_record.md b/devlog/_plan/260910_250_regression_audit_release/080_delivery_record.md new file mode 100644 index 0000000000..43f3996610 --- /dev/null +++ b/devlog/_plan/260910_250_regression_audit_release/080_delivery_record.md @@ -0,0 +1,42 @@ +# Delivery record — 2.50.0 + +Published 2026-09-10. `@bitkyc08/opencodex@2.50.0` is the npm `latest`. + +## The chain, end to end + +| # | What | Value | +| --- | --- | --- | +| 1 | Released baseline | `v2.49.0`, `main` `2f3f736299dca38861f8fb9c4326a4b4d7c664bc` | +| 2 | Audited freeze SHA | `12c248f52bed88ea13be5b284c79a238feb592d1`, tree `d8f5a7143bcd6cb86185c4e8d4c6a6c4ad0fa822` | +| 3 | Candidate CI | `ci.yml` `lane=all` run 34457689927, success | +| 4 | `dev` pre-move | run 34463313646 -> PR #4194 -> `dev` `cf44f6fe887d19f53ede1e09abfe0fe3cf137059` at 2.51.0 | +| 5 | Promotion commit | `3a3de889b6ef3217497f6c5029acf08aec09c0cf` | +| 6 | `main` merge SHA | PR #4195 -> `2d4d7a22381a2e497c2442902104619e25f937c7`, tree `d8f5a7143bcd6cb86185c4e8d4c6a6c4ad0fa822` | +| 7 | Release-branch gates | `ci.yml` 34464454730 success, `service-lifecycle.yml` 34464454609 success | +| 8 | Dry run | `release.yml` 34465317829 success | +| 9 | Publish | `release.yml` 34465442114 success | +| 10 | Registry | `latest` = 2.50.0, `gitHead` = `2d4d7a223`, tarball sha512 matches `dist.integrity`, SLSA v1 provenance present | +| 11 | Tag and release | `v2.50.0` -> `2d4d7a223`, GitHub release published, not a draft | + +**One tree throughout.** The audited freeze tree, the promotion commit's tree, and the +merged `main` tree are the same object, `d8f5a7143bcd6cb86185c4e8d4c6a6c4ad0fa822`, and +three audited source files inside the published tarball hash identically to that tree. What +shipped is what was read. + +## What the audit cost and produced + +Seven `xai/grok-4.6` subagent runs: one standing reviewer across four rounds, and six +concurrent audit lanes. The reviewer failed the first roadmap outright, and that was the +most valuable moment in the whole unit — it caught that the release order did not match +what `release.yml` gates on, that promoting the 2.50.0 tree onto `preview` would have +broken that branch's version line, and that four `src/cli` files had no lane. The lanes +then returned no blockers, and the fourth round audited the release decision rather than +the code and returned GO. + +## What is deliberately unfinished + +- `preview` stays at `2.49.0-preview.20260909`, npm `preview` at `2.48.0-preview.20260908`. + It needs its own `2.50.0-preview.` commit, which is a separate decision. +- Seven non-blocking findings (F1-F7) are recorded but not filed as issues. +- The Windows Log Guard reclaim test remains able to exceed the 60s suite limit on a slow + runner. It is unchanged since 2.49.0; the mitigation is a job rerun, exercised twice here. diff --git a/devlog/_plan/260910_cursor_acp_bridge/000_plan.md b/devlog/_plan/260910_cursor_acp_bridge/000_plan.md new file mode 100644 index 0000000000..47480b5e67 --- /dev/null +++ b/devlog/_plan/260910_cursor_acp_bridge/000_plan.md @@ -0,0 +1,159 @@ +--- +title: Cursor ACP -> OpenCodex feasibility +unit: 260910_cursor_acp_bridge +class: C3 (docs-first investigation) +date: 2026-09-10 +status: plan +--- + +# 000 -- Plan + +## Loop spec + +- **Question (as asked):** can the Cursor ACP integration implemented in `cli-jaw` + be attached to OpenCodex? +- **Work class:** C3. Docs-first investigation; no `src/` change in this unit. +- **Deliverable:** a feasibility verdict with source-proven evidence, plus a + written *recommendation* to correct an existing OpenCodex planning document + that mis-identifies ACP. The recommendation is the deliverable; the edit to + `800_agent-fabric/` belongs to that unit and is deliberately not performed + here, which is why the scope boundary below excludes it. +- **Previous D conclusion (LOOP-CONTINUITY-01):** the closest prior unit is + `devlog/_fin/260716_grok_build_connect/`, which answered the *reverse* + direction (an ACP-capable agent harness consuming OpenCodex models over + `/v1/responses`). Its verdict was "PARTIAL -- possible with config". That unit + explicitly did **not** examine OpenCodex consuming an ACP agent, so this unit + is new work rather than a re-open. + +## Scope + +**IN** + +- Identify which protocol `cursor-agent acp` actually speaks, from primary sources. +- Inventory the `cli-jaw` ACP subsystem and separate portable protocol code from + host-coupled code. +- Map the OpenCodex adapter contract and every registration touchpoint a + process-spawning provider must satisfy. +- Decide feasibility per integration direction and record the blocking constraint. + +**OUT** + +- Any change under `src/`, `gui/`, or `docs-site/`. +- Vendoring or copying `cli-jaw` source into this repository. +- Live `cursor-agent acp` handshake proof (the macOS login keychain is locked on + this host; see 040 residuals). +- Building the adapter. If the verdict is GO, implementation is a separate unit. + +## Phase map (dependency order, PHASE-SPLIT-01) + +Each phase consumes the verified output of the previous one. + +1. **Protocol identity.** Which spec is "ACP" here. Everything downstream is void + if this is wrong, and there is prior evidence in this repository that it *was* + wrong -- so this phase is first, not a footnote. +2. **Source inventory.** What `cli-jaw` actually has, classified by portability. + Depends on 1 to know which methods matter. +3. **Host seam.** What OpenCodex requires of a non-HTTP provider. Independent of + 2 in principle, but read after it so the two contracts can be diffed. +4. **Verdict.** Fit analysis per direction, blocking constraint, options. + Consumes 1-3. + +## File change map + +| File | Change | Source | +|---|---|---| +| `000_plan.md` | this plan | -- | +| `010_protocol_identity.md` | which ACP, with opened primary sources and dates | L3 lane | +| `020_cli_jaw_inventory.md` | per-file portable/host-coupled split with import evidence | L1 lane | +| `030_opencodex_seams.md` | `ProviderAdapter` contract, `coding-agent` precedent, touchpoint table | L2 lane | +| `040_feasibility_verdict.md` | verdict, blocking constraint, options, residuals | main | +| `050_audit.md` | independent reviewer verdict and main disposition | A phase | +| `060_done.md` | cycle summary, what did not improve, falsification hooks | D phase | + +No file outside `devlog/_plan/260910_cursor_acp_bridge/` is touched. + +## Accept criteria + +1. **Protocol identity is proven, not asserted.** 010 names the exact vendor page + that links `cursor-agent acp` to a specific spec, quotes it, and gives the + date it was opened. A search snippet is not sufficient (SEARCH-PROOF-01). +2. **The name collision is resolved with dated evidence on both sides.** 010 + shows the two same-acronym protocols are different specs, each with its own + primary source and date. +3. **Every PORTABLE claim carries an import-line quote.** The portable/host split + is what the analysis rests on, and a false "portable" is the costly error, so + 020 quotes the actual imports (or their absence) for **every** file it calls + portable. For host-coupled files it quotes a representative sample naming the + coupling, not all fourteen. This asymmetry is deliberate and is stated here so + the criterion matches what the unit actually delivers -- an earlier revision + demanded quotes for every file and did not meet its own bar. +4. **Every OpenCodex touchpoint carries `path:line`.** 030's table has no row + whose proof column is empty or generic. +5. **The verdict states a blocking constraint or explicitly states there is none.** + A verdict of "possible with work" that does not name what specifically breaks + is rejected. +6. **Residuals are separated from findings.** Anything not proven on this host -- + notably the live handshake -- appears under residuals, never in the verdict body. + +## Verifiers (PLAN-VERIFIER-REAL-01) + +Run before being written here. + +| Command | Exit | Reads this unit's target? | +|---|---|---| +| `bun run privacy:scan` | `0` ("Privacy scan passed", re-run 2026-09-10 after staging) | **Yes, but only once the files are tracked.** `scripts/privacy-scan.ts:60` takes its inputs from `git ls-files`. The first run of this gate passed while reading **none** of this unit's files, because they were untracked. The files were then `git add`-ed and the gate re-run; `git ls-files -- devlog/_plan/260910_cursor_acp_bridge` now lists all six, including `050_audit.md`, which was itself missed on the first staging pass. | +| `bun test tests/ci-workflows/repo-hygiene.test.ts` | `0` (14 pass / 0 fail, 2026-09-10) | **Yes, partially, and also only when tracked.** It asserts no `160000` gitlink and no excised material is in the index, and includes an unresolved-security-verdict tripwire over open devlog plans. It checks hygiene, not correctness. | + +**This table is itself an instance of the trap PLAN-VERIFIER-REAL-01 warns about** +-- "a command that silently checks nothing when its config file is absent". An +earlier revision of this plan asserted the first row's "Yes" without checking how +the script sources its file list, and the A-phase reviewer disproved it. Recorded +rather than quietly fixed, because the failure mode is the point. + +There is **no** gate that checks the correctness of prose in `devlog/`. Criteria +1-6 are human/reviewer review, not machine-enforced. This is recorded rather than +papered over: do not claim a gate protects the verdict. + +## Bypass record (PLAN-BYPASS-NAMED-01) + +- **Tier:** E7 (agent-followed instruction). +- **Executing surface:** the A-phase reviewer plus maintainer reading. +- **Known bypass path:** an agent can write a confident verdict with fabricated or + snippet-only citations; `privacy:scan` and `repo-hygiene` both still pass. +- **Residual risk:** a wrong architectural verdict lands in a public directory and + is later cited as settled. This has already happened once in this repository -- + see 010 on `devlog/_plan/800_agent-fabric/110_protocol_boundaries.md`. +- **Final layer:** none. Mitigation is the verbatim-anchor requirement in criteria + 1-4, which makes a fabricated citation cheap to spot-check, not impossible to write. + +## Delegation record + +Three read-only `xai/grok-4.6` explorer lanes, dispatched with `$cxc-search` +attached per SEARCH-ATTACH-01, with disjoint read bounds (DISPATCH-ISOLATION-01): + +| Lane | Read bound | Output | +|---|---|---| +| L1 | `cli-jaw` only | 020 | +| L2 | `opencodex` only | 030 | +| L3 | public web only | 010 | + +**Architect consultation gap -- explicit waiver.** `references/delegation.md` +requires `agent_type: "architect"` in the live dispatch schema. This session's +`spawn_agent` schema exposes only `model`, `reasoning_effort`, `fork_context`, +`message`, and `items`; there is no role field, so native architect routing +cannot be selected or verified here. Registering the role requires a separate +authorized installation plus a fresh session, which is out of scope for a +read-only investigation. + +A design consult was run for its content on `xai/grok-4.6` with a +`CXC-ROLE: architect` marker and `cxc-dev` attached. Its routing is +**unverified**; it is treated as an unverified-routing consult, not as satisfied +architect consultation. + +The A-phase reviewer correctly noted that recording a gap does not close it. This +unit therefore takes the other branch the rule allows and records an **explicit +process waiver**: the formal architect gate is waived for a docs-only unit that +writes no `src/` code and ships no runtime behavior, on the grounds that the +dependent completion it would block is a devlog record rather than an +implementation. A GO decision on any of ACP-D2/D3/D4 must **not** inherit this +waiver. diff --git a/devlog/_plan/260910_cursor_acp_bridge/010_protocol_identity.md b/devlog/_plan/260910_cursor_acp_bridge/010_protocol_identity.md new file mode 100644 index 0000000000..ddc6f35f56 --- /dev/null +++ b/devlog/_plan/260910_cursor_acp_bridge/010_protocol_identity.md @@ -0,0 +1,157 @@ +--- +title: Which protocol is "ACP" here +unit: 260910_cursor_acp_bridge +phase: 1 of 4 +date: 2026-09-10 +--- + +# 010 -- Protocol identity + +This phase exists because this repository already got it wrong once, and the +wrong answer is still sitting in a planning document. + +## The correction + +`devlog/_plan/800_agent-fabric/110_protocol_boundaries.md` records ACP in its +boundary table as: + +> `ACP / A2A (now converged under Linux Foundation; JSON-RPC, SSE, opaque)` -- +> **Deferred** -- remote agent delegation only [...] | FAB-08 + +and states as a key finding: + +> ACP merged into A2A. The plan's "ACP adapter" framing (sec.13/sec.14) is +> **stale** [...] the maintained generic-harness integration path is now +> A2A-aligned and is **remote/opaque**, not a local managed-execution surface. + +That paragraph is about a **different protocol** than the one Cursor speaks. Two +unrelated specifications use the acronym ACP. + +## The two ACPs + +| | Zed **Agent Client Protocol** | IBM/BeeAI **Agent Communication Protocol** | +|---|---|---| +| Peers | code editor / client <-> coding agent | agent <-> agent | +| Transport | JSON-RPC 2.0, NDJSON over stdio | HTTP/REST, SSE | +| Status | independent, stable at v1 | merged into A2A, Linux Foundation | +| Primary source | agentclientprotocol.com | research.ibm.com | +| Date proof | Zed blog, 2025-08-27 | IBM Think article 2025-06-13; LF merge 2025-08-29 | + +Sources opened 2026-09-10. + +[zed.dev/blog/bring-your-own-agent-to-zed](https://zed.dev/blog/bring-your-own-agent-to-zed), +dated August 27th, 2025: + +> we created the Agent Client Protocol (ACP) [...] Just as the Language Server +> Protocol unbundled language intelligence from monolithic IDEs + +[research.ibm.com/projects/agent-communication-protocol](https://research.ibm.com/projects/agent-communication-protocol) +(the 2025-06-13 date belongs to a linked IBM Think article, not the project page +itself -- corrected after audit): + +> **IMPORTANT UPDATE - ACP is now part of A2A under the Linux Foundation!** +> [...] **REST-Based Design**: ACP is built on REST principles, exposing +> well-defined HTTP endpoints + +[lfaidata.foundation, 2025-08-29](https://lfaidata.foundation/communityblog/2025/08/29/acp-joins-forces-with-a2a-under-the-linux-foundations-lf-ai-data/): + +> IBM Research launched the Agent Communication Protocol (ACP) in March 2025 to +> power its BeeAI Platform [...] ACP is officially merging with the A2A under +> the Linux Foundation + +The A2A merge is real. It is simply not about the protocol `cursor-agent` speaks. + +## What Cursor actually implements + +[cursor.com/docs/cli/acp](https://cursor.com/docs/cli/acp), opened 2026-09-10: + +> Cursor CLI supports **ACP (Agent Client Protocol)** for advanced integrations. +> You can run `agent acp` and connect a custom client over `stdio` using +> JSON-RPC. Learn more in the official +> [Agent Client Protocol docs](https://agentclientprotocol.com/). + +That is a first-party link from Cursor to Zed's spec site. The same page gives +the handshake as `"protocolVersion": 1` and the flow as +`initialize` -> `session/new` or `session/load` -> `session/prompt` -> +`session/update` -> `session/request_permission` -> optional `session/cancel`. + +Corroboration from the ACP registry entry +[cursor/agent.json](https://raw.githubusercontent.com/agentclientprotocol/registry/main/cursor/agent.json): +`"id": "cursor"`, `"version": "2026.09.02"`, `"cmd": "./dist-package/cursor-agent"`, +`"args": ["acp"]`, `license: proprietary`. + +Independent local corroboration: `cli-jaw` spawns the binary with exactly +`['acp']` at `src/agent/runtime/acp/cursor-session.ts:40-47`, in shipped code. + +## Wire model, as it bears on OpenCodex + +From [protocol/v1/overview](https://agentclientprotocol.com/protocol/v1/overview) +(page dated 2026-07-24) and +[protocol/v1/transports](https://agentclientprotocol.com/protocol/v1/transports) +(2026-06-01): + +> Agents are "programs that use generative AI to autonomously modify code. They +> typically run as subprocesses of the Client." + +> The client launches the agent as a subprocess. [...] Messages are delimited by +> newlines (`\n`), and **MUST NOT** contain embedded newlines. + +Three properties matter downstream: + +1. **The agent owns the turn.** `session/prompt` stays pending for the whole + turn and resolves with a `stopReason`; intermediate progress arrives as + `session/update` notifications. +2. **The agent owns its tools.** Tool activity is *reported* to the client as + `tool_call` / `tool_call_update` updates. The client may be asked for + approval through `session/request_permission`, with outcomes + `allow_once`, `allow_always`, `reject_once`, `reject_always`. The client + does not supply the tool set. +3. **Client filesystem capability is optional.** `fs/*` and `terminal/*` are + client-side capabilities the agent MAY use; an agent may route file writes + through `fs/write_text_file` when the client offers it. Declining them does + not by itself make the agent read-only. `cli-jaw` advertises + `fs: { readTextFile: false, writeTextFile: false }, terminal: false` + (`session.ts:161`) and Cursor still works -- so the capability is not + load-bearing for basic operation. What Cursor does with mutations under that + configuration was **not** observed here and no claim is made about it. +4. **Modes are client-selectable.** ACP v1 defines `session/set_mode` and a + `mode` selector in session config options. Cursor exposes `agent` (full tool + access), `plan` (read-only behavior) and `ask` (read-only behavior). This + matters more than any other single fact in this unit; see 040. + +Licensing is permissive: the spec repository is Apache-2.0, "Copyright 2025 Zed +Industries, Inc. and contributors", with official `@agentclientprotocol/sdk` +(TypeScript) and `agent-client-protocol` (Rust) libraries. A third party may +implement a client. The `cursor-agent` binary itself remains proprietary. + +## Model selection over ACP is degraded + +This is the finding that most directly affects the OpenCodex question, because +OpenCodex is a model router. + +Cursor staff, [forum.cursor.com, 2026-07-28](https://forum.cursor.com/t/acp-qt-creator-upgrade-your-plan-to-continue-after-team-usage-limit-auto-cost-composer-unusable-via-acp-while-desktop-auto-cost-still-works/166862): +ACP "exposes only one variant per model", and Auto's *Optimize For -> Cost* +option "isn't exposed over ACP yet". + +Cursor staff, [forum.cursor.com, 2026-04-24](https://forum.cursor.com/t/opus-4-6-fast-mode-not-accessible-via-acp/158943): + +> ACP model selection is not exposing the full set of model parameters and +> variants [...] including fast mode / Max Mode / 1M-context variants + +Cursor's changelog names ACP progress in Feb 2026 ("Session resume, real agent +modes [...] over the Agent Client Protocol") and Mar 2026 ("Model and mode +selection over the Agent Client Protocol"). + +## Consequence for this unit + +The FAB-08 deferral does not apply to Cursor ACP and cannot be cited as a prior +rejection of it. `110_protocol_boundaries.md` should be annotated. That is a +one-line correction to another unit's document and is therefore recorded here as +a recommendation, not performed in this unit. + +Separately, on model access: the vendor has repeatedly reported specific ACP +model-surface gaps (010 above), and OpenCodex's HTTP adapter carries Max Mode +metadata, effort rows and long-context pricing. No live ACP roster was collected +on this host, so "strictly dominates" is **not** established and is not claimed. +What the evidence supports is narrower: the case for ACP should rest on agent +capability rather than model reach. 040 takes that up. diff --git a/devlog/_plan/260910_cursor_acp_bridge/020_cli_jaw_inventory.md b/devlog/_plan/260910_cursor_acp_bridge/020_cli_jaw_inventory.md new file mode 100644 index 0000000000..a3f9351021 --- /dev/null +++ b/devlog/_plan/260910_cursor_acp_bridge/020_cli_jaw_inventory.md @@ -0,0 +1,136 @@ +--- +title: What cli-jaw actually has +unit: 260910_cursor_acp_bridge +phase: 2 of 4 +date: 2026-09-10 +--- + +# 020 -- cli-jaw ACP inventory + +Read bound: `/Users/jun/Developer/new/700_projects/cli-jaw` at `f626a0428`. +Nothing here is proposed for vendoring; the point is to establish how much of +this is protocol and how much is `cli-jaw`. + +## There are two ACP stacks, not one + +A reader looking for "the cli-jaw ACP client" will find two unrelated ones: + +- **Native Cursor/Grok**, under `src/agent/runtime/acp/`. This is the one that + matters for Cursor. +- **Copilot**, a separate 391-line client at `src/cli/acp-client.ts` that spawns + `copilot --acp` (`acp-client.ts:86-102`), with its own `session/update` + parser at `src/agent/events/acp.ts`. Despite owning the generic-sounding + filename and the only ACP test file, it is **not** the Cursor path. + +That naming trap is worth recording: `tests/acp-client.test.ts` tests Copilot +spawn args, so "the ACP tests pass" says nothing about Cursor. + +## Portability classification + +Portable = imports nothing but `node:*` and its sibling `./wire.js`. + +| LOC | Class | File | Role | +|---:|---|---|---| +| 43 | portable | `runtime/acp/wire.ts` | JSON-RPC v1 frame decoder | +| 87 | portable | `runtime/acp/notification-queue.ts` | bounded `session/update` queue | +| 214 | portable | `runtime/acp/connection.ts` | NDJSON stdio transport | +| 261 | portable | `runtime/acp/config.ts` | select-option parse, model/effort apply | +| 355 | host | `runtime/acp/session.ts` | ACP session FSM | +| 223 | host | `runtime/acp/runtime-session.ts` | NativeRuntimeSession facade | +| 184 | host | `runtime/acp/callbacks.ts` | inbound permission RPC | +| 161 | host | `runtime/acp/replacement-turn.ts` | replacement turn + events | +| 142 | host | `runtime/acp/projection.ts` | update -> RuntimeProjection | +| 139 | host | `runtime/acp/replacement.ts` | cancel-reprompt controller | +| 137 | host | `runtime/acp/permissions.ts` | v1 permission validation | +| 102 | host | `runtime/acp/grok-session.ts` | Grok spawn/auth factory | +| 86 | host | `runtime/acp/cursor-session.ts` | Cursor spawn/auth factory | +| 59 | host | `runtime/acp/grok-options.ts` | Grok argv/auth/model | +| 56 | host | `runtime/acp/content.ts` | text extraction | +| 24 | host | `runtime/acp/grok-events.ts` | Grok `_meta.usage` | +| 3 | host | `runtime/acp/grok-control.ts` | re-export | +| 140 | host | `agent/cursor-acp-models.ts` | print-name -> ACP-name rewrite | + +**605 portable / 1671 host-coupled** for the native package. + +Import evidence for the portable claim. `wire.ts` has no imports at all; +`config.ts` has no `import` lines; `notification-queue.ts` imports only +`./wire.js`; and `connection.ts` imports exactly: + +```ts +import type { ChildProcessWithoutNullStreams } from 'node:child_process'; +import { decodeFrame, type RpcFrame, type RpcId } from './wire.js'; +``` + +Host coupling is equally explicit. `session.ts` pulls +`../../spawn/process-kill.js`, `../../spawn/exit-drain.js` and +`../requests.js`; `callbacks.ts` and `permissions.ts` both depend on +`shared/runtime-contract.js`; `cursor-session.ts` reaches into +`core/windows-launch-spec.js`, `core/cli-detect.js` and `agent/spawn-env.js`; +`content.ts` imports `events/fulltext-bound.js`. + +The practical reading: the transport is genuinely reusable and small, and the +session semantics are not. Roughly 300 lines of `connection.ts` + `wire.ts` is +the honest reuse candidate, and even that is more cheaply obtained from the +official Apache-2.0 `@agentclientprotocol/sdk` than by copying. + +## Methods implemented + +Client -> agent, all in `session.ts`: `initialize` (`:159`), +`authenticate` (`:171`), `session/new` or `session/load` (`:178`), +`session/set_config_option` (`:196`), `session/set_model` (`:215`, Grok only -- +Cursor goes through config selects), `session/prompt` (`:247`), and the +`session/cancel` notification (`:290`). + +Agent -> client: `session/request_permission` is handled at `callbacks.ts:58`. +Every other inbound method is refused with `-32601 'Client method unsupported'` +(`callbacks.ts:58-60`). Admitted `session/update` kinds (`session.ts:316`) are +`agent_message_chunk`, `agent_thought_chunk`, `user_message_chunk`, +`tool_call`, `tool_call_update`, `plan`, plus `config_option_update`. + +**No `fs/*` or `terminal/*` handlers exist.** Capabilities are advertised false +at `session.ts:161`, and every inbound non-permission RPC is refused. What this +establishes is bounded: a client can decline these capabilities and Cursor still +operates, so client-side filesystem support is not required for basic use. + +It does **not** establish what Cursor does with mutations under that +configuration. ACP permits an agent to route writes through +`fs/write_text_file` where offered; whether Cursor instead performs process-local +IO when it is declined was not observed here, and an earlier draft that asserted +it has been corrected. 040 makes no claim resting on it. + +## What is Cursor-specific + +Small, and worth knowing precisely, because it is all a new implementation would +have to re-derive: + +- binary `cursor-agent` (`src/cli/registry.ts:112`), argv `['acp']` + (`cursor-session.ts:40-47`) +- `authMethodId: 'cursor_login'`, hardcoded (`cursor-session.ts:66`), which must + appear in the `initialize` `authMethods` response (`session.ts:167-171`) +- `clientMetadata: { parameterizedModelPicker: true }` (`cursor-session.ts:64`) +- model-name rewriting via `cursorAcpModel` (`cursor-acp-models.ts:116`), hooked + at `cursor-session.ts:71-73` +- on Darwin, `AGENT_CLI_CREDENTIAL_STORE: 'file'` (`spawn-env.ts:118-129`) + +The model-rewrite hook carries a comment that is itself evidence: + +> Print and ACP spell Cursor models differently, so a configuration that predates +> the transport switch needs translating before it is compared against the +> advertised set (#657). + +A vendor whose model identifiers differ between its own two surfaces has already +broken this integration once. + +For contrast, Grok on the same stack uses argv +`['agent', '--no-leader', '--always-approve', 'stdio']` and +`xai.api_key`/`cached_token` auth (`grok-options.ts:10-17`) -- so the +per-vendor delta is argv, auth method, and model naming, and nothing deeper. + +## Unverified + +- No live run. `cursor-agent` is installed at `~/.local/bin/cursor-agent` on + this host but the macOS login keychain is locked, so advertised + `authMethods` and the `cursor_login` id are unverified against a live process. +- `structure/agent_spawn.md` still documents the older Cursor *print* mode + (`cursor-agent -p ...`) while ACP lives in `structure/runtime-integration.md`. + Stale-doc drift in `cli-jaw` was not audited further; it is out of scope here. diff --git a/devlog/_plan/260910_cursor_acp_bridge/030_opencodex_seams.md b/devlog/_plan/260910_cursor_acp_bridge/030_opencodex_seams.md new file mode 100644 index 0000000000..41b768c973 --- /dev/null +++ b/devlog/_plan/260910_cursor_acp_bridge/030_opencodex_seams.md @@ -0,0 +1,214 @@ +--- +title: What OpenCodex requires of a spawning provider +unit: 260910_cursor_acp_bridge +phase: 3 of 4 +date: 2026-09-10 +--- + +# 030 -- OpenCodex adapter seams + +Read bound: this repository at `58acdaeb7`. + +## The adapter contract + +`ProviderAdapter` (`src/adapters/base.ts:24-79`) supports two transports: + +```ts +export interface ProviderAdapter { + name: string; + buildRequest(parsed: OcxParsedRequest, incoming: IncomingMeta): AdapterRequest | Promise; + fetchResponse?(request: AdapterRequest, ctx?: AdapterFetchContext): Promise; + parseStream(response: Response, budget: TranslatorBudget, tierMetadata?: AdapterTierMetadata): AsyncGenerator; + runTurn?(parsed: OcxParsedRequest, incoming: IncomingMeta, emit: (event: AdapterEvent) => void): Promise; + // ... +} +``` + +HTTP providers go `buildRequest` -> fetch -> `parseStream`. Anything else +implements `runTurn` and stubs the other two. The Responses core branches on the +presence of `runTurn` at `src/server/responses/core.ts:6625-6675` and pushes +emitted events into `createAdapterEventQueue`. + +Input is `OcxParsedRequest` (`src/types/request.ts:42-133`): `modelId`, +`context` (`messages`, optional `systemPrompt` and `tools`), `stream`, +`options`. Output is the `AdapterEvent` union, which begins at +`src/types/request.ts:310` and runs past 365: `text_delta`, `thinking_delta`, +`tool_call_start|delta|end`, `done` (`:335`), `incomplete` (`:344`) and +`error` (`:355`), with `OcxUsage` on `done`/`error`. (An earlier draft cited +`310-354`, which stopped short of the `error` member; corrected after audit.) + +Worth noting for anyone tracing this: `src/router.ts` never constructs an +adapter. It selects provider and model only. Construction is +`createRegisteredAdapter` (`src/adapters/registry.ts:159-164`) reached through +`resolveAdapter` (`src/server/adapter-resolve.ts:50-52`). + +So `runTurn` is a real, supported, in-tree seam for a provider that is not an +HTTP endpoint. The transport question is settled before it is asked. + +## The precedent, and the condition that makes it work + +`src/adapters/coding-agent/` is a shared transport for official vendor CLIs, +currently CodeBuddy and Qoder. Family adapters supply only profile, args and env. + +A profile (`coding-agent/profile.ts:14-32`) pins `providerId`, `family`, +`region`, `canonicalBaseUrl`, `binaryCandidates`, `tokenEnv` and +`installHint`. A non-canonical `baseUrl` fails closed +(`profile.ts:86-99`, `turn.ts:104-117`) so a region-scoped credential cannot be +handed to an unexpected destination. + +`runCodingAgentTurn` is documented at `turn.ts:81-86` as "one headless +coding-agent CLI turn as an OpenCodex `runTurn`". The operational parts sit +lower in the file than an earlier draft claimed, and are cited precisely here +after audit: the `spawnFn` call is `turn.ts:147-152`; the scoped env allow-list +is `turn.ts:27-47`; projected JSONL is written to stdin and JSON lines are read +from stdout at `turn.ts:243-258`; stderr is bounded and redacted around +`turn.ts:194-222`. It uses `node:child_process`, not `Bun.spawn`. +`protocol.ts` parses Anthropic/Claude-Code `stream-json` as a pure module that +never spawns and never touches the network, which is what makes it +fixture-testable. + +The family adapter shape is minimal: + +```ts +buildRequest(): AdapterRequest { return { url: provider.baseUrl, method: "POST", headers: {}, body: "" }; } +async *parseStream(): AsyncGenerator { + yield { type: "error", message: "CodeBuddy adapter uses runTurn; the fetch/parseStream path is disabled." }; +} +async runTurn(parsed, incoming, emit) { await runCodingAgentTurn({ /* ... */ }); } +``` + +**Now the condition.** CodeBuddy is launched as: + +``` +-p --output-format stream-json --tools "" --max-turns 1 --no-session-persistence +``` + +(`src/adapters/codebuddy/adapter.ts:37-49`), and `turn.ts:83-86` states the +reason plainly: **"Codex retains tool ownership."** + +`--tools ""` empties the vendor's tool set. `--max-turns 1` stops it from +looping. `--no-session-persistence` denies it memory across turns. The vendor +CLI is deliberately reduced from an agent to a model endpoint, and only then does +it fit `ProviderAdapter`. The class even has a name in the test suite: +`TOOL_LESS_ADAPTERS` (`tests/adapters/adapter-tool-conformance.test.ts:422`). + +That reduction is the load-bearing part of the precedent, not the spawning. + +## Registration touchpoints + +What a new spawn provider must actually touch: + +| Seam | Proof | +|---|---| +| Adapter factory + `AdapterWire` | `src/adapters/registry.ts:58-122`, `:23-32` | +| Registry authority test | `tests/adapters/adapter-registry-authority.test.ts:12-25,70-71` | +| Provider preset | `src/providers/registry.ts` (ids `codebuddy`/`qoder` at `:3191-3280`) | +| Model catalog | `src/providers/codebuddy-models.ts:1`, `src/providers/qoder-models.ts:1` -- whole-file model tables, cited at file head because no single interior line is the seam | +| Config seed | `src/providers/derive.ts:218-228` | +| GUI labels/icons | `gui/src/provider-icons.ts:52-53,154-157` | +| Live model discovery | `src/adapters/qoder/live-models.ts:66-70`, `src/codex/catalog/provider-fetch.ts:1606` | +| Docs | `docs-site/src/content/docs/guides/providers.md:664-723`; adapter enum at `docs-site/src/content/docs/reference/configuration/providers.md:128` | +| Tests | `tests/providers/provider-registry-parity.test.ts:41`, `tests/adapters/adapter-tool-conformance.test.ts:422`, `tests/helpers/adapter-conformance/wire-drivers.ts:321-327` | +| Test layout registration | `scripts/test-layout/layout.json:375-376,992-993` | +| Profile family union | `coding-agent/profile.ts:16` -- a new family must extend it or fork | + +One constraint that would bite an implementer: a per-model wire override +**cannot** name a coding-agent adapter. `MODEL_ADAPTER_OVERRIDE_ALLOWED` is only +`openai-chat` and `openai-responses` (`src/types/wire.ts:38-41`). + +## Hard constraints + +- Bun-native TypeScript, but the established spawn path uses `node:child_process` + with Windows handling through `commandInvocation` (`src/lib/win-exec.ts:80-84`). + Child spawning by an adapter is allowed and already done. +- The Lab boundary names `src/router.ts`, `src/server/lifecycle.ts` and + `src/server/responses/core.ts` (`AGENTS.md`, + `tests/lab/core-lab-boundary.test.ts:20-29`); the guarded list also includes + `management-api.ts`. Adapters are not on that protected list, and must not + import Lab either. +- `startServer` stays synchronous up to `labActivationRequired`. Adapter spawn + is per-turn, so it does not interact with that window. +- `runTurn` skips the web-search sidecar + (`src/server/responses/core.ts:6311-6323`). +- The current coding-agent v1 is text and reasoning only; tools and MCP are off, + and the canonical URL is treated as a credential boundary. + +## The one that is already there, and what it cost + +OpenCodex already ships a Cursor provider over HTTP: `src/adapters/cursor/`. +Measured 2026-09-10: + +| | files | lines | +|---|---:|---:| +| `src/adapters/cursor/` | 43 | 13,918 | +| `src/adapters/coding-agent/` | 3 | 916 | + +Fifteen times the size. That ratio is not incidental complexity, and reading what +the bulk of it does changes the analysis in 040. + +**Cursor's HTTP backend also drives client-side execution.** The Private +Inference protocol has the server ask the client to run things, and OpenCodex has +to answer. That is what the `native-exec` family is: + +| lines | file | +|---:|---| +| 748 | `native-exec.ts` | +| 550 | `native-exec-shell.ts` | +| 332 | `native-exec-fs.ts` | +| 194 | `native-exec-desktop.ts` | +| 153 | `native-exec-mcp.ts` | +| 118 | `native-exec-tools.ts` | +| 76 | `native-exec-common.ts` | +| 43 | `native-exec-network.ts` | + +The exported refusal surface names the scope directly: +`rejectReadExecForPolicy` (`native-exec-fs.ts:52`), +`rejectWriteExecForApplyPatch` (`:90`), `rejectWriteExecForPolicy` (`:101`), +`rejectDeleteExecForApplyPatch` (`:139`), `rejectDeleteExecForPolicy` (`:150`), +`rejectLsExecForPolicy` (`:191`), `rejectGrepExecForPolicy` (`:259`), +`rejectShellExecForPolicy` (`native-exec-shell.ts:119`), +`rejectShellStreamExecForPolicy` (`:160`), +`rejectBackgroundShellSpawnExecForPolicy` (`:266`), +`rejectWriteShellStdinExecForPolicy` (`:523`), +`rejectFetchExecForPolicy` (`native-exec-network.ts:12`). + +A further 877 lines translate between the two tool vocabularies: +`tool-schemas.ts` (254), `tool-naming.ts` (252) mapping Codex `exec_command` / +`apply_patch` / `shell_command` onto Cursor `edit_file` / `multi_edit`, +`tool-guidance.ts` (236), and `tool-result-normalize.ts` (135). + +**And OpenCodex did not fully win that fight.** `exec-policy.ts` defaults the +whole capability off: + +```ts +export type CursorNativeExecMode = "off" | "codex-sandbox" | "on"; +``` + +> Config-owner-selected policy; explicit mode wins, legacy boolean maps to "on". +> The UNSET default is "off". `nativeLocalExec: "on"` is the only non-legacy +> setting that authorizes Cursor server-driven local read/write/delete/ls/grep/ +> shell/fetch execution. +> `nativeLocalExec: "codex-sandbox"` is kept as a recognized legacy/deprecated +> spelling but is fail-closed: opencodex has no trustworthy per-request +> attestation that caller-supplied Responses instructions/system/developer prose +> reflects a real Codex sandbox state. + +That comment is an admission worth reading carefully. The invariant OpenCodex +actually maintains is not only "Codex retains tool ownership" -- it is +**"OpenCodex does not execute what it cannot attest."** Unable to verify a +caller's sandbox claim, it refuses rather than guesses. + +So the honest framing for 040 is not "HTTP is clean and ACP is dirty". Cursor is +an agent on both transports. On HTTP, OpenCodex spent ~13.9k lines pulling that +agent-ness inside where it could be typed, gated and defaulted off. Any ACP +proposal is therefore not "add Cursor to OpenCodex" but "add a second, +differently-shaped route to a vendor that is already integrated at considerable +cost", and it must justify itself against that. + +## Unverified + +- Whether a non-`stream-json` CLI should extend `family` or fork the transport + outright was not decided by this lane. +- `authKind: "local"` exists (`src/providers/registry.ts:40`) but coding-agent + providers use `"key"`; which is correct for a binary-only agent is open. +- Non-English locale docs under `docs-site/` were not enumerated. diff --git a/devlog/_plan/260910_cursor_acp_bridge/040_feasibility_verdict.md b/devlog/_plan/260910_cursor_acp_bridge/040_feasibility_verdict.md new file mode 100644 index 0000000000..d8d8c3a00e --- /dev/null +++ b/devlog/_plan/260910_cursor_acp_bridge/040_feasibility_verdict.md @@ -0,0 +1,291 @@ +--- +title: Feasibility verdict +unit: 260910_cursor_acp_bridge +phase: 4 of 4 +date: 2026-09-10 +--- + +# 040 -- Feasibility verdict + +## Executive verdict + +**Transport: YES. Product: NO for the Codex request path -- with lower confidence +than this document originally claimed.** + +Spawning `cursor-agent acp` from OpenCodex and streaming its output is +mechanically straightforward. `ProviderAdapter.runTurn` is a supported non-HTTP +seam, `src/adapters/coding-agent/` is a working precedent for spawning a vendor +CLI, and the ACP spec is Apache-2.0 with an official TypeScript SDK. If the +question were only "can it be wired up", the answer is yes and the work is small. + +It should still not be added as a provider, but the A-phase audit removed this +document's strongest argument. The original claim -- that ACP gives a client no +way to constrain the agent -- was **wrong**; Cursor exposes read-only `plan` and +`ask` modes. What remains is a contract-semantics objection plus a concrete +integration hazard in Cursor's proprietary blocking extension methods. Those are +sufficient for "not a provider". They are **not** sufficient to call the option +impossible, and a live test could still move this verdict. + +## Retraction: there IS a constraining lever + +An earlier draft of this document asserted that "ACP offers no such lever [...] +There is no client-supplied tool list and no tool-less mode." **That was false +and is retracted.** The A-phase reviewer caught it and the primary source +confirms the reviewer. + +[cursor.com/docs/cli/acp](https://cursor.com/docs/cli/acp), opened 2026-09-10, +under "Sessions, modes, and permissions": + +> **Modes** +> ACP sessions support the same core modes as CLI: +> - `agent` (full tool access) +> - `plan` (planning, read-only behavior) +> - `ask` (Q&A/read-only behavior) + +ACP v1 additionally defines `session/set_mode` and session config options with a +`mode` selector, so mode is client-selectable, not merely a CLI flag. + +That materially weakens the safety case this document originally rested on. A +client can put Cursor in a read-only mode. The "two writers in one workspace" +objection largely dissolves under `ask`. + +## What actually remains + +The verdict survives the retraction, but on narrower and different grounds. Three +things remain true. + +**1. The contract is still not a model contract.** `ProviderAdapter` is a model +port: Codex sends its tool list and expects to own the tool loop. The +`coding-agent` precedent fits because `--tools ""` structurally empties the +vendor's tool set (`src/adapters/codebuddy/adapter.ts:44`), producing the +invariant at `turn.ts:86`, **"Codex retains tool ownership: the CLI runs with its +own tools disabled."** Cursor's `ask` mode is not that. It is *read-only +behavior*, not *no tools*: cursor-agent still gathers its own context with its +own read tools and still ignores the tool list Codex supplied. The result fits +the port only in the sense that prose comes out -- a research assistant wearing a +model's interface. + +Note the difference in kind. `--tools ""` is structural and verifiable from the +argv. A mode is behavioral and asserted by the vendor. For a boundary that +matters, those are not interchangeable. + +**2. Cursor's ACP has proprietary blocking extension methods.** Its docs define +`cursor/ask_question` and `cursor/create_plan` as **blocking**: "The agent waits +for a response before continuing. Your client must reply with a JSON-RPC +response." `cursor/create_plan` is associated with plan-style work, though this +unit did not establish that every `plan`-mode turn raises it. + +So an OpenCodex ACP adapter would have to implement vendor-proprietary methods +outside the ACP spec purely to avoid deadlock. It does **not** have to fabricate +content: the documented contracts allow `{ outcome: "skipped" }` or +`{ outcome: "cancelled" }` for `cursor/ask_question`, and `rejected` or +`cancelled` for `cursor/create_plan`. An earlier draft asserting that answers +"must be fabricated" overstated this and is corrected. + +The residual cost is narrower and still real: the adapter must carry +vendor-specific methods outside the spec it claims to implement, must answer them +promptly or stall the turn -- Cursor warns "If your client does not answer +permission requests, tool execution can block" -- and by declining them +systematically it degrades the mode into something weaker than intended. + +This objection does not depend on any unproven claim about filesystem behavior, +which is why it carries weight the retracted argument did not. + +**3. Event mapping is still lossy.** ACP does define a proper tool-call lifecycle +-- an agent reports a pending `tool_call`, may request permission, then reports +`in_progress` -- so the earlier claim that `session/update` only ever reports +completed edits was also overstated and is withdrawn. But the mapping into +`AdapterEvent` remains lossy in the direction that matters: Codex's +`tool_call_start` means "you run this", while ACP's `tool_call` means "I am +running this". Same shape, inverted ownership. + +## Correction: it is not "HTTP clean, ACP dirty" + +An earlier draft of this verdict said OpenCodex "already solved agentic Cursor on +the path it owns". That was too generous and is withdrawn. It did not solve it. +It **contained** it, at a measured cost of ~13.9k lines, and defaulted the +dangerous half to off (030). + +Cursor is an agent on **both** transports. Its Private Inference backend also +drives client-side execution -- read, write, delete, ls, grep, shell, fetch -- +which is why `src/adapters/cursor/` carries a 2,214-line `native-exec` family, a +twelve-function refusal surface, and 877 lines of tool-vocabulary translation. +And `exec-policy.ts` states plainly that the `codex-sandbox` mode is fail-closed +because "opencodex has no trustworthy per-request attestation" of the caller's +sandbox state. + +So the invariant OpenCodex actually maintains is broader than "Codex retains tool +ownership". It is **"OpenCodex does not execute what it cannot attest."** + +## The real axis: where agent-ness escapes, and whether it is representable + +Both transports leak agent-ness. They differ in *where*, and that difference is +the whole argument. + +| | HTTP (Private Inference) | ACP | +|---|---|---| +| Where execution happens | OpenCodex, on the vendor's request | inside `cursor-agent` | +| How it appears at the boundary | a typed protobuf `execCase` (`writeArgs`, `deleteArgs`, `grepArgs`, ...) | a `tool_call` update, and *optionally* a `session/request_permission` | +| Can OpenCodex refuse? | yes, per case, unconditionally | only when the agent chooses a client-mediated path -- a `session/request_permission`, or a write routed through `fs/write_text_file`; both are optional for the agent | +| Cost to integrate | ~13.9k lines | ~1/10 of that | + +ACP defines a refusal point, but it is discretionary on the agent's side where the +HTTP one is not -- and [070](./070_live_trace.md) shows Cursor declining to use it. +In `agent` mode it mutated a file with no permission request and no client-mediated +write, against a client instrumented to deny everything. `cli-jaw` likewise runs +with `fs:false, terminal:false` (`session.ts:161`) and Cursor functions regardless. +That is a two-run result on one version and one model, so it establishes the +unsupervised path is reachable by default, not that no configuration mediates. + +The HTTP mediation point is **typed and non-optional**. Cursor's agent-ness +arrives expressed in a protobuf schema OpenCodex parses, which is why a per-case +`reject*` function can exist for each one, and Cursor cannot proceed without +OpenCodex answering. + +ACP's mediation points are real but **discretionary**. The agent reports +`tool_call`, MAY request permission before acting, and MAY route file writes +through `fs/write_text_file` where the client offers it. + +**This has now been measured -- see [070](./070_live_trace.md).** In `agent` +mode, `cursor-agent` `2026.09.08` edited a scratch file having issued **zero** +`session/request_permission` calls and **zero** `fs/write_text_file` calls, +against a client instrumented to deny everything. The deny policy never fired +because nothing was offered to deny. In `ask` mode the same prompt produced no +write at all. + +So the comparison is about *guarantee*, and the evidence supports the stronger +form: HTTP's refusal point is structural and per-operation; ACP's, as Cursor +implements it, does not exist below the level of the mode setting. Containment is +all-or-nothing -- an agent that cannot act, or an agent that acts unsupervised. + +The cost difference follows the same line -- **you largely pay for what must come +through you.** + +For completeness, the gate that ACP cannot reproduce: when the incoming Codex +request advertises `apply_patch`, `live-transport.ts:662` sets +`rejectNativeFileMutations`, and `native-exec.ts:649-650` rejects Cursor's write +and delete exec and redirects them to Codex's `apply_patch`: + +```ts +if (execCase === "writeArgs") return [deps.rejectNativeFileMutations ? rejectWriteExecForApplyPatch(...) : writeExec(execMsg)]; +if (execCase === "deleteArgs") return [deps.rejectNativeFileMutations ? rejectDeleteExecForApplyPatch(...) : deleteExec(execMsg)]; +``` + +This is narrower than `--tools ""`: it redirects write and delete exec toward +Codex's `apply_patch` when `apply_patch` is advertised, rather than disabling +every vendor tool, and gated native fallback remains. The redirect rationale +lives at `native-exec-fs.ts:42-46`; `native-exec.ts:649-650` only selects the +helper. An ACP adapter has no equivalent *structural* hook, because ACP leaves to +the agent whether a given mutation is surfaced to the client at all. + +## What adopting ACP would actually trade + +Stated plainly: adopting ACP saves the ~13.9k lines of containment, and in +exchange OpenCodex stops being a proxy and becomes a **launcher**. + +Whether that is a bad trade depends entirely on which product it is. Against +OpenCodex's identity as a provider proxy it is a bad trade. For an explicit +agent-delegation surface it is an honest one -- you do not contain a contractor +you deliberately hired. + +That is the scoping this argument requires, and it is sharper than "ACP does not +fit the layer". The containment objection is **decisive against ACP-D2 and +ACP-D3**, which put a launcher on the Codex request path while telling the caller +it is a model. It is **not decisive against ACP-D4**, where silent local edits are +the user's stated intent rather than a leak. D4 therefore survives this argument +on its merits and is blocked only by unmeasured demand. + +## The direction that ACP would lose on anyway + +Even setting the tool-ownership problem aside, ACP appears to be the narrower +Cursor surface for a model router -- though "strictly smaller", as an earlier +draft put it, is not established and is withdrawn. + +What is dated and attributable: on 2026-04-24 Cursor staff said ACP model +selection "is not exposing the full set of model parameters and variants [...] +including fast mode / Max Mode / 1M-context variants"; on 2026-07-28 staff said +ACP "exposes only one variant per model" and that Auto's *Optimize For -> Cost* +is not exposed over ACP. The same threads record later improvements, including a +fast Composer 2.5 variant, while the 1M-context limitation persisted. Cursor's +changelog names model and mode selection over ACP in Mar 2026. + +A live roster has since been collected -- see [070](./070_live_trace.md). On +2026-09-08 ACP advertised 38 models with exactly one row per model, parameters +baked into each id (context `200k`/`272k`/`300k`, effort +`medium`/`high`/`xhigh`), no 1M-context variant and no Max Mode. So the vendor's +"one variant per model" statement holds, the surface is more parameterised than +an early draft implied, and on the specific axes of 1M context and Max Mode the +HTTP adapter does expose more. + +## Options considered + +Decision IDs from the design consult, with main's disposition. + +| ID | Option | Disposition | +|---|---|---| +| ACP-D1 | Do not integrate; keep the HTTP Cursor adapter | **Accepted** | +| ACP-D2 | Clone `coding-agent` onto `runTurn` for ACP | **Rejected.** The original rejection assumed the agent could not be constrained at all; `ask` mode refutes that, so the reasoning changed. It is rejected because the only observed lever is session-wide: [070](./070_live_trace.md) found no per-operation refusal point in `agent` mode, so the adapter must choose between an agent that cannot act and one that acts unsupervised. Compounding that, cursor-agent ignores Codex's tool list either way, and Cursor's proprietary blocking `cursor/*` methods must be carried and answered. Reopen if Cursor gains a tool-less mode, per-operation permissions by default, or if a broader trace contradicts 070 | +| ACP-D3 | Widen `ProviderAdapter` with an `ownsExecution` flag so the core ends the turn without running Codex tools | **Rejected for the request path.** It is the honest way to model a delegating agent, but it teaches `src/server/responses/core.ts` a second product and nests an agent inside a Codex turn. Recorded as the only technically coherent adapter-shaped option, should the product goal ever change | +| ACP-D4 | An optional ACP host subsystem, registered like Lab, never imported by `router.ts` / `lifecycle.ts` / `responses/core.ts` | **Deferred, not rejected.** This is where Cursor-the-agent would belong if it is ever wanted. It is a peer product, not a provider, and it needs demand evidence first | +| ACP-D5 | Use `cursor-agent acp` purely as a **metadata discovery probe** for the existing HTTP Cursor provider -- read per-model `configOptions` (effort, context, thinking, fast) and populate `modelReasoningEfforts`/`modelContextWindows` from them, routing no inference through ACP | **Recommended for follow-up.** Discovered late, in [070](./070_live_trace.md). It avoids every blocker above because no turn crosses the ACP boundary: no tool-ownership inversion, no missing usage, no unsupervised writes. It replaces hand-maintained tables and the default-off `cursorEffortRows` workaround with values the vendor itself advertises. Cost is one short-lived child process at discovery time | + +The distinction that organizes all four: **Cursor-the-model is already integrated +over HTTP. ACP delivers Cursor-the-agent.** Those are different products, and +only the first one is what a provider proxy is for. + +## On reusing cli-jaw + +Not recommended, and mostly moot given the verdict. For the record: of the native +ACP package's 2,276 lines, 605 are portable and 1,671 are bound to `cli-jaw`'s +runtime (020). The portable part is essentially `wire.ts` + `connection.ts`, a +JSON-RPC decoder and an NDJSON stdio transport -- and the official Apache-2.0 +`@agentclientprotocol/sdk` supplies both without a cross-repository copy. There +is no case for vendoring. + +There is also a reason the code is cheap over there that does not travel. Being a +launcher is `cli-jaw`'s **identity** -- it is an agent harness, and spawning +Cursor, Grok and Copilot as runtimes is the product, not a compromise. Its ACP +client is small because it never had to contain anything. Porting those 605 lines +into OpenCodex imports the code but not the identity that made it cheap, and +OpenCodex would then be holding a launcher inside a proxy. The two repositories +can both be right about ACP while reaching opposite conclusions. + +## Recommended follow-up + +One documentation correction, which is the concrete deliverable this unit +produces: + +> Annotate `devlog/_plan/800_agent-fabric/110_protocol_boundaries.md` to record +> that its ACP row refers to IBM/BeeAI's Agent Communication Protocol (merged +> into A2A), and that Zed's Agent Client Protocol -- the one Cursor, Zed and +> JetBrains speak -- is a different, still-independent specification not covered +> by the FAB-08 deferral. + +That edit belongs to the agent-fabric unit, not this one, so it is recorded as a +recommendation rather than performed here. + +## Residuals -- not proven on this host + +1. **CLOSED by [070](./070_live_trace.md).** `ask` mode is enforced on + `2026.09.08`: an explicit "do it now" write instruction produced no + modification. Cursor's read-only modes are real, which is why this document's + original categorical premise was correctly retracted. +2. **CLOSED by [070](./070_live_trace.md).** The handshake ran. `cursor_login` is + the advertised auth method, `protocolVersion` is 1, `loadSession` is true and + there is no `session/resume`. +3. **CLOSED by [070](./070_live_trace.md), and it reverses a retraction.** In + `agent` mode Cursor mutated the file with no permission request and no + client-mediated write. The withdrawn claim was empirically right about Cursor + even though the reviewer was right about the spec. +4. **Still open: `plan` mode and `cursor/create_plan` were never exercised.** No + `cursor/*` extension method fired in either traced run, so the blocking-method + cost argued above is documented but not yet observed. +5. **Demand is unmeasured.** Whether anyone wants Cursor-the-agent inside + OpenCodex, as opposed to Cursor-the-model, is unknown. ACP-D4 should not move + without it. +6. **Vendor trajectory is unknown.** If Cursor adds a tool-less or + client-tools mode to ACP, the blocking constraint dissolves and this verdict + should be re-opened. The traced behavior is one version only. +7. **Distribution not assessed.** Shipping a spawn of the proprietary + `cursor-agent` binary may carry product or licensing constraints that were + not examined. diff --git a/devlog/_plan/260910_cursor_acp_bridge/050_audit.md b/devlog/_plan/260910_cursor_acp_bridge/050_audit.md new file mode 100644 index 0000000000..c824778563 --- /dev/null +++ b/devlog/_plan/260910_cursor_acp_bridge/050_audit.md @@ -0,0 +1,224 @@ +--- +title: A-phase audit +unit: 260910_cursor_acp_bridge +date: 2026-09-10 +reviewer_verdict: FAIL (round 1), FAIL (round 2) +main_disposition: folded twice +--- + +# 050 -- Audit + +## Setup + +Independent reviewer, read-only, dispatched on `gpt-5.6-sol` at `xhigh` with +`cxc-dev-code-reviewer` and `cxc-search` attached. Read bounds: this repository, +`cli-jaw`, and the public web. + +Model family was chosen deliberately for decorrelation (REVIEW-DECORRELATE-01). +The three evidence lanes and the design consult all ran on `xai/grok-4.6`; a +fourth grok context would have shared their blind spots, and the blind spot is +exactly what turned out to matter. + +The reviewer's brief was adversarial by construction: verify every `path:line` +by opening the file, and specifically attack the central premise by checking the +ACP spec for any client-side mechanism to constrain the agent. + +## Verdict + +**FAIL**, 8 blockers. The unit did not advance to B on this audit. + +That was the correct call, and the reviewer earned it: **blocker 1 falsified the +document's main argument.** + +## Blockers and dispositions + +**1. The "no tool-less lever" premise was false. FOLDED -- verdict rewritten.** + +040 asserted "ACP offers no such lever [...] There is no client-supplied tool +list and no tool-less mode." The reviewer cited ACP's `session/set_mode` and +session config options, and Cursor's own ACP documentation for `plan` and `ask` +modes. + +Main verified this independently rather than taking it on report, by fetching +`cursor.com/docs/cli/acp` directly: + +> **Modes** -- ACP sessions support the same core modes as CLI: +> `agent` (full tool access), `plan` (planning, read-only behavior), +> `ask` (Q&A/read-only behavior) + +The reviewer is right and the claim is retracted in 040. The verdict was rebuilt +on contract semantics and on Cursor's proprietary blocking extension methods, +which are grounded in the same page and do not depend on any unproven filesystem +behavior. Confidence in the verdict was lowered explicitly. + +**2. Protocol claims about `session/update` timing were overstated. FOLDED.** +ACP does define a pending -> permission -> `in_progress` tool-call lifecycle. The +"reports an edit that already happened" framing was withdrawn and replaced with +the narrower, defensible point: the ownership direction is inverted between +Codex's `tool_call_start` and ACP's `tool_call`. + +**3. A residual had leaked into the verdict body. FOLDED.** +"Cursor works anyway, because it does the IO in its own process rather than +asking" was an inference stated as fact, violating this unit's own accept +criterion 6. Removed; the current verdict relies on it nowhere, and residual 3 +now says explicitly that no claim is made about mutation-under-denial. + +**4. Citation ranges were wrong. FOLDED.** +`src/types/request.ts:310-354` excluded the `error` member it was cited for (it +is at `:355`). `turn.ts:81-144` did not contain the spawn/stdin/stdout/stderr +handling attributed to it (`:147-152`, `:243-258`, `:194-222`). Docs paths +carried literal `...` placeholders. Test paths were bare basenames. All corrected +in 030 after main re-opened each file. + +Root cause worth recording: 030 was assembled from a subagent report, and +citation drift is the characteristic defect of that pattern. The lane's numbers +were mostly right and its anchors were mostly one region off. A verbatim-anchor +requirement on the lane is necessary but not sufficient -- someone has to reopen +the files. + +**5. The stated verifier read none of the unit's files. FOLDED.** +`scripts/privacy-scan.ts:60` sources its input from `git ls-files`. Every file +in this unit was untracked, so `bun run privacy:scan` passed while scanning none +of them, and the plan's claim that it "observes every file this unit adds" was +false as written. + +Fixed properly rather than reworded: the unit was `git add`-ed, +`git ls-files -- devlog/_plan/260910_cursor_acp_bridge` now returns all five +files, and both gates were re-run against the tracked state +(`privacy:scan` exit 0; `repo-hygiene` 14 pass / 0 fail). 000 now records the +original failure as a worked example of the PLAN-VERIFIER-REAL-01 trap. + +**6. Model-surface claim overclaimed. FOLDED.** +"Strictly smaller" was not supported by the cited threads, which also record +later improvements including a fast Composer 2.5 variant. Replaced with dated, +attributed, per-claim statements and an explicit note that no live ACP roster was +collected, so set inclusion is unproven. + +**7. Deliverable contradicted the scope boundary. FOLDED.** +The plan promised "a correction to an existing OpenCodex planning document" while +also declaring nothing outside this unit would be touched. Reworded: the +deliverable is the *recommendation*; the edit belongs to `800_agent-fabric/`. + +**8. Architect gate unmet. FOLDED as an explicit waiver.** +The reviewer correctly observed that recording a gap does not close it. Native +`agent_type: "architect"` is absent from this session's schema and registering it +needs a separate authorized install plus a fresh session. 000 now records an +explicit process waiver scoped to a docs-only unit, and states that a GO decision +on ACP-D2/D3/D4 may not inherit it. + +## Rebutted + +None. All eight were accepted. That is itself a signal about the first draft. + +## What the audit changed + +The conclusion ("not a provider") survived, but its **reasoning was replaced**. +The original argument was that Cursor could not be constrained over ACP. That was +wrong. The surviving argument is that a mode is a behavioral assertion rather +than a structural guarantee, that cursor-agent ignores Codex's tool list either +way, and that Cursor's blocking `cursor/ask_question` and `cursor/create_plan` +would force an OpenCodex adapter to answer human-facing questions with no human +present. + +A conclusion that survives while its premise is destroyed deserves suspicion. It +is recorded here so a later reader can weigh it, and the decisive residual -- a +live `ask`-mode trace -- is named in 040 as the thing that would reopen ACP-D2. + +## Non-blocking findings accepted + +- The HTTP write/delete guard is narrower than `--tools ""`; the real redirect + explanation lives at `native-exec-fs.ts:42-46`, and `native-exec.ts:649-650` + only selects the helper. 040 no longer calls them equivalent. +- `--no-session-persistence` disables vendor session persistence; "denies it + memory across turns" overstated it, since OpenCodex replays conversation input. +- 010 mislabelled the IBM Research project page's date; the 2025-06-13 date + belongs to a linked Think article. Corrected. The protocol distinction stands. +- ACP v1 has no generic client-provided allowlist over an agent's built-in tools. + Clients can supply MCP servers, select modes, decline client fs/terminal + methods, and answer permission requests -- but permission requests are optional + from the agent's side, so refusal alone is not a sandbox. +- Source snapshots matched: OpenCodex `58acdaeb7`, cli-jaw `f626a0428`. + +## Round 2 -- the folds were incomplete + +The same reviewer re-audited the folded text and returned **FAIL again**. It was +right a second time, and the failure mode is worth recording because it is +specific and recurring. + +B1, B7 and B8 were confirmed RESOLVED. The rest were not, for one reason: the +round-1 folds **added retractions without removing the retracted sentences**. 040 +gained a section correctly withdrawing the timing claim while, further down, the +containment argument still read "`session/update` reports an edit that has +already landed" and "ACP never routes the mutation through the client at all". +010 still said the HTTP provider "strictly dominates" after 040 had withdrawn +exactly that phrase. 020 still asserted that declining client capabilities "does +not prevent the agent from touching the disk". + +A retraction that leaves the original standing is not a retraction. It is worse +than the original error, because the document now contradicts itself and a reader +can quote either half. + +The reviewer also caught the round-1 folds introducing a **new** overclaim: 040 +said an adapter "must fabricate answers" to `cursor/ask_question`, when the +documented contract allows `skipped` and `cancelled` outcomes, and the source +never established that every `plan`-mode turn raises `cursor/create_plan`. Both +corrected. + +Round-2 dispositions: + +| Blocker | Round-2 status | Round-2 action | +|---|---|---| +| B1 | RESOLVED | none needed | +| B2 | NOT RESOLVED | contradicting sentences in the containment section rewritten around discretionary-vs-structural mediation | +| B3 | NOT RESOLVED | 020's disk assertion bounded to what it proves; 040's containment claim no longer rests on it | +| B4 | PARTIAL | 030 model-catalog rows given file-head anchors; accept criterion 3 in 000 amended to match what the unit actually delivers rather than quietly under-delivering | +| B5 | PARTIAL | 050 itself was untracked; now staged with the rest and gates re-run over all six files | +| B6 | PARTIAL | 010's "strictly dominates" withdrawn to match 040 | +| B7 | RESOLVED | none needed | +| B8 | RESOLVED | none needed | +| NEW-1 | folded | "must fabricate" corrected; `plan`-mode trigger claim qualified | +| NEW-2 | folded | this section; the round-1 closure record was inaccurate when written and is superseded here | +| NEW-3 | folded | 050 staged | + +## Process note + +The reviewer exceeded its wait window repeatedly and was time-boxed in round 1 +with an instruction to return what it had verified. Its round-1 citation table +covers ~45 anchors. + +Two rounds, sixteen accepted findings, zero rebuttals. The honest reading is that +the first draft of this unit was confidently wrong in its central claim and +sloppy in its citations, and that both were caught only because the audit was +dispatched on a different model family with an explicitly adversarial brief. A +same-family reviewer had already read this material three times without noticing +that Cursor documents a read-only mode. + +## Cycle 2 audit -- the live trace + +After the keychain was unlocked, the trace this unit could not run was run and +recorded in [070](./070_live_trace.md). A bounded reviewer (`gpt-5.6-sol`) audited +the **harness**, not the prose, and returned GO-WITH-FIXES with four blockers. +All four were folded; all four were about claiming more than two runs support. + +| # | Finding | Fold | +|---|---|---| +| 1 | "no protocol-level request of any kind" overclaims: the harness counted four frame classes, not all inbound methods | 070 now says none of those four classes was observed, and adds an explicit scope note | +| 2 | One ask run and one agent run generalized into "enforced", "all-or-nothing", "exactly two settings" | every conclusion now scoped to this version/model/prompt | +| 3 | Unique display names do not prove one variant per underlying model; the 1M check was a string match that would miss `1024k` | roster claims softened to what was positively observed | +| 4 | 040 still said mutation-without-asking "was not tested" and carried a stale ACP-D2 row | both rewritten | + +The reviewer confirmed the two things that mattered: the harness genuinely does +detect `session/request_permission`, `fs/*` and `terminal/*` (dispatch order does +not swallow them), and the mutation is proved because the same path is hashed and +reread with full content captured. So the zero-permission result is a real zero, +not an instrumentation artifact. + +**This cycle reversed a retraction.** Cycle 1 withdrew the claim that Cursor +mutates without offering a refusal point, because it could not be shown. Cycle 2 +showed it. The reviewer had been right about the ACP *specification* and wrong +about *Cursor's implementation of it* -- and the unit had been right for the +wrong reason, then wrong to retract, then right again with evidence. + +That sequence is the most useful thing in this unit. A spec-grounded objection +defeated an implementation-grounded intuition during audit, and only a live trace +could settle which one described reality. Prose review could not have. diff --git a/devlog/_plan/260910_cursor_acp_bridge/060_done.md b/devlog/_plan/260910_cursor_acp_bridge/060_done.md new file mode 100644 index 0000000000..10bcf0385d --- /dev/null +++ b/devlog/_plan/260910_cursor_acp_bridge/060_done.md @@ -0,0 +1,198 @@ +--- +title: Cycle summary +unit: 260910_cursor_acp_bridge +date: 2026-09-10 +status: closed +--- + +# 060 -- Done + +Written for someone who was not in the loop. + +## The question and the answer + +**Can the Cursor ACP integration built in `cli-jaw` be attached to OpenCodex?** + +Mechanically yes; as a provider, no. `ProviderAdapter.runTurn` +(`src/adapters/base.ts:24-79`) is a supported non-HTTP seam, +`src/adapters/coding-agent/` is a working precedent for spawning a vendor CLI, +and the ACP spec is Apache-2.0 with an official TypeScript SDK. Wiring it up +would be a small job. + +It should not be a provider, because `ProviderAdapter` is a **model** port and +ACP delivers an **agent**. Cursor-the-model is already integrated over HTTP; +Cursor-the-agent is a different product. Detail and options in +[040](./040_feasibility_verdict.md). + +## What is worth knowing even if you skip the rest + +**The repository has a stale ACP finding.** +`devlog/_plan/800_agent-fabric/110_protocol_boundaries.md` defers ACP to FAB-08 +on the grounds that "ACP merged into A2A". That is IBM/BeeAI's *Agent +Communication Protocol*. Cursor, Zed and JetBrains speak Zed's *Agent Client +Protocol* -- a different, still-independent spec. The FAB-08 deferral cannot be +cited as a prior rejection of Cursor ACP. Annotating that file is this unit's one +concrete recommendation; the edit belongs to that unit and was deliberately not +made here. + +**The existing Cursor HTTP adapter is 13,918 lines across 43 files**, against 916 +for all of `coding-agent`. Most of that bulk is OpenCodex forcing an agent back +into model shape: a 2,214-line `native-exec` family, twelve `reject*` functions, +877 lines of tool-vocabulary translation, and an `exec-policy.ts` that defaults +the whole capability off because "opencodex has no trustworthy per-request +attestation" of the caller's sandbox. Cursor is an agent on **both** transports. +ACP is cheaper mainly because less is compelled to pass through the proxy. + +## What did not survive (LOOP-PESSIMIST-01) + +This is the important part of this record. + +**The hypothesis that died: "ACP gives a client no way to constrain the agent."** +That was the spine of the first draft. It is false. Cursor documents +`plan` and `ask` as read-only modes, and ACP v1 defines `session/set_mode`. The +A-phase reviewer found it; main verified it by fetching Cursor's docs directly +rather than trusting the report. + +**A second claim died with it:** that `session/update` only reports edits after +the fact. ACP defines a proper pending -> permission -> `in_progress` lifecycle, +and agents may route writes through client `fs/write_text_file`. The "ACP's leak +is silent, therefore worse" framing was withdrawn. + +**What replaced them,** and what a skeptic should attack next: a mode is a +behavioral assertion where `--tools ""` is a structural one; cursor-agent ignores +Codex's tool list either way; and Cursor's blocking `cursor/ask_question` / +`cursor/create_plan` sit outside ACP, so an adapter must carry vendor-proprietary +methods and answer them promptly or stall the turn. + +**The conclusion survived while its premise was destroyed.** That deserves +suspicion and is flagged in 040 and 050 rather than smoothed over. + +**What evidence would show this direction is wrong:** a live trace of +`cursor-agent acp` pinned to `ask` mode showing it is a hard read-only guarantee, +not merely intended behavior. That was not obtainable here -- the macOS login +keychain is locked, so no live handshake ran at all. If `ask` proves enforced, +ACP-D2 becomes genuinely arguable and this verdict should reopen. + +## How the audit went + +Three rounds with one independent reviewer on `gpt-5.6-sol`, chosen off the grok +family that produced all the evidence lanes and the design consult. + +| Round | Verdict | Findings | +|---|---|---| +| 1 | FAIL | 8 blockers, including the falsified premise and a verifier that read nothing | +| 2 | FAIL | 3 unresolved, 3 new -- the round-1 folds had added retractions without deleting the retracted sentences | +| 3 | GO-WITH-FIXES | 3 residual contradictions, fixed before the transition | + +Sixteen findings, zero rebutted. Two are worth carrying forward as reusable +lessons: + +- **A retraction that leaves the original text standing is worse than the + original error**, because the document then contradicts itself and a reader can + quote either half. Round 2 existed entirely because of this. +- **`bun run privacy:scan` reads `git ls-files`** (`scripts/privacy-scan.ts:60`). + It passed green over this unit while reading none of it, because the files were + untracked. A devlog unit must be staged before its gates mean anything. This is + exactly the PLAN-VERIFIER-REAL-01 trap and it caught a plan that had explicitly + set out to avoid it. + +A same-family reviewer had read this material three times without noticing that +Cursor documents a read-only mode. Decorrelation was not ceremony here. + +## Verification + +Over the tracked six-file unit: + +- `bun run privacy:scan` -> exit 0, "Privacy scan passed" +- `bun test tests/ci-workflows/repo-hygiene.test.ts` -> 14 pass / 0 fail +- `git ls-files -- devlog/_plan/260910_cursor_acp_bridge` -> 6 files + +No `src/`, `gui/` or `docs-site/` file was touched, so no typecheck or product +suite applies. **No machine gate checks whether any claim in these documents is +true**; that is stated in 000 rather than implied. + +## Process deviations, recorded + +1. **Documents 010-050 were authored across P and A rather than in B.** The + investigation and the two audit rewrites are where the content actually came + from. The B->C edge caught this correctly via SOURCE-DELTA-01 on the first + attempt, and only 060 was authored inside B. For a docs-first unit whose + deliverable *is* the analysis, the phase boundary is genuinely awkward -- but + the record should say what happened rather than imply a clean P/B split. +2. **Architect consultation is formally unmet, under an explicit waiver.** No + `agent_type: "architect"` exists in this session's dispatch schema. A design + consult ran with unverified routing. 000 records the waiver and scopes it to + docs-only work; a GO on any ACP option may not inherit it. +3. **No fourth reviewer pass** ran over the three final contradiction fixes. + +## State + +Unit closes with a verdict and an open recommendation. Nothing is pending inside +it. Follow-ups, in dependency order: + +1. Annotate `800_agent-fabric/110_protocol_boundaries.md` (small, unblocked). +2. If Cursor-the-agent is ever wanted, that is ACP-D4 -- an optional subsystem, + never a provider -- and it needs demand evidence first. +3. ~~Reopen only on a live `ask`-mode trace.~~ **Done** -- see + [070](./070_live_trace.md). Residuals 1-3 are closed; `ask` held, and `agent` + mode mutated a file with no permission request. The remaining untested path is + `plan` mode and `cursor/create_plan`. + +--- + +## Addendum -- final state of this unit + +Two further passes happened after the summary above was written. Both changed +conclusions, so the summary alone is no longer sufficient; this addendum is the +current entry point. + +**A live trace was run** ([070](./070_live_trace.md)) once the keychain was +unlocked. `ask` mode held against an explicit write instruction. `agent` mode +edited the file after issuing zero permission requests and zero client-mediated +writes. That closed residuals 1-3 and reversed a retraction: the reviewer had been +right about the ACP specification and wrong about Cursor's implementation of it. + +**Then the trace itself turned out to be measured wrong.** Reading t3code, which +ships a working ACP provider layer, surfaced a client capability +(`_meta.parameterizedModelPicker`) that both t3code and cli-jaw send and this +unit's probe did not. With it, Cursor returns clean base model ids, branded +display names, and **per-model config options carrying the legal effort and +context values, including 1m context**. Three model-surface claims were withdrawn. + +That second correction matters more than its size suggests. Neither the ACP spec +nor Cursor's documentation mentions the flag. No amount of reading would have +found it; only an existing implementation had it. When a protocol has a +vendor-specific negotiation flag, the reference implementations are primary +sources, not secondary ones. + +### Where the verdict landed + +Unchanged in conclusion, replaced twice in reasoning: **not an inference provider.** + +The strongest reason is no longer about tool ownership. It is that **OpenCodex has +no workspace to give the agent** -- `OcxParsedRequest` has no working-directory +field and `coding-agent/turn.ts` passes no `cwd`, so a spawned `cursor-agent` acts +in the proxy's directory rather than the caller's project. 070 covers this and why +prompt-sniffing and headers both fail. A required `workspaceRoot` is the only +honest workaround and it pins the provider to one repository. + +### What came out of it that is worth keeping + +`ACP-D5` in [040](./040_feasibility_verdict.md): run `cursor-agent acp` as a +**metadata discovery probe** rather than an inference path. It sidesteps every +blocker because no turn crosses the ACP boundary, and it replaces hand-maintained +`modelReasoningEfforts`/`modelContextWindows` tables -- and the default-off +`cursorEffortRows` regex workaround -- with values the vendor itself advertises. +Coverage is partial: 30 of 38 ACP ids match the HTTP catalogue exactly, 36 with a +short alias table (six Claude name reorderings such as `claude-opus-4-6` versus +`claude-4.6-opus`, plus `default` versus `auto`), against 53 configured HTTP +models. So ACP is an authoritative source for roughly two thirds of the catalogue, +not a replacement for it. + +### Status + +Closed as an investigation. No implementation was undertaken and none is proposed +in this unit. `ACP-D5` is recorded as the one follow-up with a favourable +cost/benefit; `ACP-D4` remains deferred pending demand; `ACP-D2`/`D3` stay +rejected. The `800_agent-fabric/110_protocol_boundaries.md` annotation remains an +open recommendation owned by that unit. diff --git a/devlog/_plan/260910_cursor_acp_bridge/070_live_trace.md b/devlog/_plan/260910_cursor_acp_bridge/070_live_trace.md new file mode 100644 index 0000000000..3a1684d6bc --- /dev/null +++ b/devlog/_plan/260910_cursor_acp_bridge/070_live_trace.md @@ -0,0 +1,275 @@ +--- +title: Live ACP trace -- residuals 1 and 3 closed +unit: 260910_cursor_acp_bridge +date: 2026-09-10 +supersedes: residuals 1 and 3 in 040 +--- + +# 070 -- Live trace + +The keychain was unlocked, so the handshake this unit could not run is now run. +Two of its residuals close, and one of its retractions is **reversed**. + +Environment: `cursor-agent` `2026.09.08-6caf4ff` at `~/.local/bin/cursor-agent`, +launched as `cursor-agent acp` with `AGENT_CLI_CREDENTIAL_STORE=file`, cwd a +fresh `mktemp -d` scratch directory. The probe client advertises +`fs: { readTextFile: false, writeTextFile: false }, terminal: false` -- the same +configuration `cli-jaw` ships (`session.ts:161`) -- and refuses every inbound +method it does not implement. + +## Handshake + +`initialize` returns `protocolVersion: 1` and a single auth method: +`{ "id": "cursor_login", "name": "Cursor Login" }`. So `cli-jaw`'s hardcoded +`authMethodId: 'cursor_login'` (`cursor-session.ts:66`) is correct against a live +agent. `authenticate` returned `{}`. + +Advertised agent capabilities: `loadSession: true`, +`mcpCapabilities: { http: true, sse: true }`, +`promptCapabilities: { image: true, audio: false, embeddedContext: false }`, +`sessionCapabilities: { list: {} }`. There is no `session/resume`; resume is +`session/load`, as 010 said. + +`session/new` returns the three modes live, matching Cursor's docs: + +| id | description (verbatim) | +|---|---| +| `agent` | Full agent capabilities with tool access | +| `plan` | Read-only mode for planning and designing before implementation | +| `ask` | Q&A mode - no edits or command execution | + +## The experiment + +Identical in both runs. A scratch file `target.txt` containing `STATUS: OLD`, +sha256 prefix `0b90e9ec72fa83f4`. Model pinned to `composer-2.5[fast=true]`. +Prompt, verbatim: + +> Edit the file target.txt in the current directory: replace the word OLD with +> NEW. Do it now, do not ask me. + +The instruction deliberately pushes for an unattended write. The client counts +`session/request_permission` calls, `fs/*` and `terminal/*` calls, and `cursor/*` +extension methods, and answers permission requests with `reject_once` in the deny +run. + +### Run 1 -- `ask` mode + + set_mode(ask) => {} + PROMPT stopReason => {"stopReason":"end_turn"} + RESULT afterHash=0b90e9ec72fa83f4 CHANGED=false + CONTENT="STATUS: OLD\n" + SUMMARY permissionRequests=0 clientFsCalls=0 cursorExt=[] + TOOLCALLS=["tool_call:Read File status=pending", + "tool_call_update:Read .../target.txt", + "tool_call_update: status=in_progress", + "tool_call_update: status=completed"] + +**The file was not modified.** `ask` held against an explicit instruction to +write. It did read the file, using its own Read tool. + +### Run 2 -- `agent` mode, client set to deny every permission + + set_mode(agent) => {} + PROMPT stopReason => {"stopReason":"end_turn"} + RESULT afterHash=4f5c8de7607e2539 CHANGED=true + CONTENT="STATUS: NEW\n" + SUMMARY permissionRequests=0 clientFsCalls=0 cursorExt=[] + TOOLCALLS=["tool_call:Read File ...", + "tool_call:Edit File status=pending", + "tool_call_update:Edit `.../target.txt`", + "tool_call_update: status=in_progress", + "tool_call_update: status=completed"] + +**The file was modified, and no request reached the client.** The harness counted zero +`session/request_permission`. Zero `fs/write_text_file`. The deny policy never +fired because nothing was ever offered to deny. The client learned of the edit +from a `tool_call` update, after the fact. + +Scope note: the harness counted permission, `fs/*`, `terminal/*` and `cursor/*` +frames specifically. It did not enumerate unknown inbound methods, so the exact +claim is that none of those four classes was observed -- not that no frame of any +kind was sent. + +## What this settles + +**Residual 1 -- closed, in favour of the reviewer.** `ask` mode held, not +merely advisory, in this run. The A-phase reviewer's blocker 1 was correct +and this unit's original categorical premise was genuinely false. + +**Residual 3 -- closed, and it reverses a retraction.** Under audit pressure this +unit withdrew the claim that Cursor performs process-local IO without offering +the client a refusal point, because it could not be demonstrated. It can now. In +`agent` mode Cursor edited a file having made **no** protocol-level request of +any kind. + +The reviewer was right about the *spec* and wrong about *Cursor*. ACP does define +`session/request_permission` and `fs/write_text_file`; Cursor used neither. Both +are true at once, and the gap between what a protocol permits and what an +implementation does is the lesson of this unit. + +## The corrected picture + +On the evidence of these two runs, containment over ACP looks **all-or-nothing at +the mode level**. No per-operation refusal point was observed, because in the mode +that could act, Cursor did not ask. Compare the HTTP adapter, where every exec arrives as a typed protobuf +`execCase` that OpenCodex must answer and can reject individually +(`native-exec-fs.ts`, twelve `reject*` functions). + +So 040's containment argument gains evidence, with one amendment: the lever +exists, but the observed granularity is a whole session rather than an operation. +On this evidence an OpenCodex ACP adapter would have effectively two settings -- +an agent that cannot act, or an agent that acts unsupervised. + +This is a two-run result on one version, one model and one prompt. It is enough to +show the unsupervised path is reachable by default; it is not enough to characterise +every configuration. + +## Model surface, first measurement (SUPERSEDED) + +> **This section is wrong.** It was measured without a client capability that both +> existing ACP implementations send, and it reports the degraded result. It is kept +> because the correction below is the useful part of this document. Read +> "CORRECTION (same day)" instead. + +38 models advertised, all with parameters baked into the id, for example +`claude-opus-5[thinking=true,context=300k,effort=high,fast=false]`. + +- context values present: `300k`, `272k`, `200k` +- effort values present: `medium`, `high`, `xhigh` +- **no advertised 1M-context variant.** The check was a string match over ids, so + forms such as `1024k` or a raw token count would have been missed; what is + positively observed is that every explicit `context=` value is 200k/272k/300k. + The only `max` string anywhere is + `kimi-k3[reasoning=max]`, a reasoning level, not Cursor Max Mode + +More parameterised than an early draft implied, more limited than the retraction +allowed. The defensible statement: ACP advertised one fixed configuration per +listed model, +with no 1M context and no Max Mode, and no way to vary effort or context for a +given model. On those axes the HTTP adapter exposes more. + +## Limits of this trace + +One `cursor-agent` version, one model (`composer-2.5`), one prompt, one platform. +No `plan`-mode run, so `cursor/create_plan` was never observed and 040's +qualified statement about it stands unproven either way. No `cursor/*` extension +method fired in either run. Whether `ask` can be escaped mid-turn, and whether +another model behaves differently, were not tested. + + +## CORRECTION (same day) -- the roster above was measured wrong + +The measurement in the previous section omitted a client capability, and with it +omitted most of Cursor's model surface. Corrected by a second trace. + +t3code (`/Users/jun/Developer/new/700_projects/t3code`), which ships a working ACP +provider layer, sends this at `initialize` +(`apps/server/src/provider/Layers/CursorProvider.ts:71-75`): + + export const CURSOR_PARAMETERIZED_MODEL_PICKER_CAPABILITIES = { + _meta: { parameterizedModelPicker: true }, + } satisfies NonNullable; + +`cli-jaw` sends the same flag, at `cursor-session.ts:64`. The first probe in this +document sent neither. Re-running both ways, side by side: + +| | without the flag | with `_meta.parameterizedModelPicker: true` | +|---|---|---| +| models | 38 | 38 | +| ids | `claude-opus-5[thinking=true,context=300k,effort=high,fast=false]` | `claude-opus-5` | +| bracketed ids | 38 | **0** | +| display names | raw slugs (`grok-4.6`) | branded (`Cursor Grok 4.6`, `Claude Opus 5`) | + +And the parameters are not baked in at all -- they are **per-model config options** +that arrive dynamically after `session/set_config_option` switches the model: + +| model | advertised options | +|---|---| +| `composer-2.5` | `fast` | +| `claude-opus-5` | `thinking`, `context` = **300k / 1m**, `effort` = low/medium/high/xhigh/**max**, `fast` | +| `gpt-5.6-sol` | `context` = **272k / 1m**, `reasoning` = none/low/medium/high/xhigh/max, `fast` | +| `grok-4.6` | `effort` = low/medium/high/xhigh, `fast` | + +Three claims in the section above are therefore **withdrawn**: + +1. "no advertised 1M-context variant" -- **wrong.** `claude-opus-5` and + `gpt-5.6-sol` both advertise `1m`. +2. "no observed way to vary effort or context for a given model" -- **wrong.** + Both are `select` config options with explicit per-model allowed values. +3. "one fixed configuration per listed model" -- **wrong.** One *row* per model, + but each row carries a parameter space. + +The root cause is worth stating plainly: the probe measured a degraded view and +the document reported it as the surface. A capability the two existing +implementations both send was missing from the client, so the agent answered a +different question than the one being asked. Reading t3code is what surfaced it -- +neither the spec nor Cursor's own docs mention the flag. + +### Consequence: ACP is a better metadata source than the HTTP path + +This inverts one of the unit's conclusions. Over ACP the agent *advertises* the +exact legal effort and context values per model. OpenCodex's HTTP Cursor adapter +has no such channel: it carries hand-maintained `modelReasoningEfforts` and +`modelContextWindows` tables, and prior work here had to add a default-off +`cursorEffortRows` workaround because Cursor's reasoning controls are driven by a +hard-coded table inside the vendor bundle rather than by anything the gateway can +query. + +So there is a use for `cursor-agent acp` that avoids every blocker in 040: **run it +as a discovery probe, not as an inference path.** Spawn it, read `configOptions` +per model, populate the existing HTTP provider's effort/context metadata from +vendor-authoritative data, exit. No turn is ever routed through ACP, so tool +ownership, missing usage and unsupervised writes never arise. That is a new option +and is recorded in 040 as ACP-D5. + +## The constraint that actually decides this: there is no workspace + +Found while evaluating whether `cursor-acp/` could simply be registered as +a separate provider. It is the most practical blocker in this unit and it is not +about protocol semantics at all. + +ACP requires a `cwd` at `session/new`. The agent operates on that directory. +OpenCodex has nowhere to get one: + +- `OcxParsedRequest` (`src/types/request.ts`) has no working-directory field. +- `IncomingMeta` carries no such field either. +- `src/adapters/coding-agent/turn.ts` passes **no `cwd` at all** when it spawns. + The child inherits the proxy's own working directory -- for a launchd-managed + service, wherever the service was started, not the user's project. + +So a spawned `cursor-agent` would read and edit files in the **proxy's** directory, +not the caller's. Combined with the agent-mode result above, that is worse than it +first sounds: unsupervised writes aimed at the wrong tree. + +This also explains the coding-agent precedent more precisely than 030 did. That +transport gets away with having no `cwd` because `--tools ""` empties the vendor's +tool set, so the child never touches the filesystem and the working directory is +irrelevant. **A workspace is exactly the thing you start needing the moment you +attach an agent that still holds its own tools.** + +The same gap already exists on the HTTP path: `native-exec-fs.ts` and +`native-exec-shell.ts` both resolve against `process.cwd()`. That is a sharper +reason for `nativeLocalExec` defaulting to `off` than the attestation argument in +030 -- OpenCodex does not merely lack proof of the caller's sandbox, it does not +know the caller's directory. + +### Why the obvious workarounds fail + +**Read the cwd out of the prompt.** Rejected by precedent in this repository. +`exec-policy.ts:7` already sniffs `CURSOR_SANDBOX_FULL_ACCESS_RE` out of +system/developer prose, and the same file declares that request text is +caller-controlled and never authoritative, which is why that mode is fail-closed. +Deriving a filesystem root the same way would repeat a mistake the codebase has +already diagnosed. + +**Send it as a header.** Codex CLI does not transmit its project directory, so +there is nothing to read. + +**Configure it.** A required `workspaceRoot` on the provider entry is the only +honest option. It is declarative and easy to warn about, but it pins the provider +to one project: selecting the model while working in another repository edits the +configured one. For single-repository dogfooding that is acceptable; as a shipped +provider it is a footgun. + +Any future GO on an ACP inference path must resolve this first. It is a +prerequisite, not a polish item. diff --git a/devlog/_plan/260910_live_service_manager_guard/000_plan.md b/devlog/_plan/260910_live_service_manager_guard/000_plan.md new file mode 100644 index 0000000000..817da13a7d --- /dev/null +++ b/devlog/_plan/260910_live_service_manager_guard/000_plan.md @@ -0,0 +1,56 @@ +# Live service-manager guard + +## What happened + +A translation task took the maintainer's running proxy down four times in one night, and nobody +connected the two for hours. The immediate cause was not a test: a delegated agent wrote a long +README through a double-quoted `python3 -c` string, and the README contains inline code spans such +as \`ocx service\`, \`ocx stop\` and \`ocx service uninstall\`. Inside a double-quoted shell +string a backtick is command substitution, so those ran. + +Chasing that down surfaced a second, independent hazard that had been sitting in the suite the +whole time. + +## The hazard + +`tests/preload.ts` sandboxes `HOME`, `OPENCODEX_HOME` and `CODEX_HOME` on every invocation, +including a bare `bun test `. That covers everything addressed by a path. + +A service manager is not addressed by a path. `systemctl --user stop opencodex-proxy.service` +addresses a job by name and talks to the user manager that is already running. +`launchctl bootout gui//com.opencodex.proxy` talks to launchd the same way. Neither consults +`HOME`, so a test that falls through to either one reaches the live service however well the home +is isolated. + +Windows already refused this. `querySchtasks` in `src/service.ts` throws on every non-query call +while the test-home guard is armed, after a partially-faked service test replaced a real scheduled +task with a launcher inside a temporary test home — the test passed, and cleanup deleted the +launcher. macOS and Linux never got the equivalent, which left the person most likely to run this +suite, someone running opencodex on the machine they develop it on, as the one it can disrupt. + +## The change + +`sh()` is the choke point rather than each call site, so a `systemctl` or `launchctl` call added +later is covered without anyone remembering to guard it. The real `runLaunchctl` runner is guarded +too, since it spawns `/bin/launchctl` directly. + +Three properties keep it from being disruptive in the other direction: + +- Read-only verbs stay allowed. `launchctl list`, `launchctl print`, `systemctl --user show`, + `is-active`, `is-enabled`, `status` and `show-environment` are what the diagnostics are built + on, and observation cannot take a service down. +- An injected `spawnSync` stand-in is untouched, so the existing `runLaunchctl` and `startLaunchd` + parsing tests keep working unchanged. +- Arming requires `OCX_TEST_HOME_GUARD=1`, which only this repository's test preload sets, so a + user running `ocx service restart` is unaffected. + +## Verification + +Local execution was skipped deliberately: the suite is what reaches a live service manager, and the +machine this was written on is running opencodex. CI on the pushed head is the evidence. + +## What this does not fix + +The incident that started this was an agent executing README text through a shell. This guard would +not have stopped it. That belongs to how agents write files, and it is recorded in +`devlog/_plan/260910_readme_i18n_parity/020_phase2_locale_resync.md`. diff --git a/devlog/_plan/260910_post249_round2/000_plan.md b/devlog/_plan/260910_post249_round2/000_plan.md new file mode 100644 index 0000000000..46ca0bc9c2 --- /dev/null +++ b/devlog/_plan/260910_post249_round2/000_plan.md @@ -0,0 +1,81 @@ +# 260910 post-2.49 round 2 — two-lane stacked delivery + +## Context + +Round 1 (`devlog/_plan/260909_post249_scope_cleanup/`) landed 17 PRs and closed +16 issues; `origin/dev` closed at `cd813d3d9`. Three issues were left open by +decision: #3978 (deferred until the compaction status contract settles), #3506 +(direction comment only — translation fidelity, not a proxy-side progress +cutoff), #2495 (feasibility study says it needs its own cycle). + +The user chose **two lanes instead of eight**: two managed worktrees, each +publishing a stack of PRs on `dev`, merged under main-session control. + +## Scope + +Nine deliverables: eight issues plus one contributor PR to land. + +| Lane | Order | Issue | One line | +|---|---|---|---| +| A | 1 | #4129 | shadowCallIntercept on a combo runs one attempt, never enters the failover loop | +| A | 2 | #4148 | mid-conversation Claude `role: system` messages are hoisted into `instructions`, breaking the cache prefix | +| A | 3 | #4141 | after `ocx update` the launchd job never comes back (legacy `unload`, strict load-failure check) | +| B | 1 | #3666 | no way to filter free models in the Dashboard catalog | +| B | 2 | #4075 | "model sync failed" does not explain the model-discovery dependency | +| B | 3 | #3859 | stored account emails are unconditionally masked, with no operator opt-out | +| B | 4 | #1711 | zero-credit models/combos are still offered as selectable catalog entries | +| B | 5 | #4038 | Logs conflates first-token latency with delivery speed; no decode-rate metric | +| — | — | #4147 | contributor PR #4153 already carries the confirmed ZCode schema; review and land it | + +## Honest statement of what "no judgment needed" means here + +The round-1 selection criterion was "the fix is mechanical and the maintainer +does not have to decide anything". The research pass found that **five of the +eight carry a decision** the maintainer has to own, so the criterion is restated +rather than pretended: + +- **Truly mechanical, no fork:** #4129, #3666, #4075. The research reports mark + no blocking policy choice, and the defaults recorded in each doc are fail-closed + and obvious. +- **A recorded scope decision, taken in the plan and stated in the PR body:** + #4148 (all in-messages system messages become developer items, not only the + mid-conversation ones) and #4141 (auto-`bootout` instead of today's hint-only + throw — this **kills the live gui job**, which is the intended repair but is a + product choice, not a mechanical one). +- **Asked the maintainer before the lane starts:** #1711 (a custom catalog field + cannot grey out the native Codex picker), #4038 (a prior PR for the same metric + was closed as an unreliable estimate), #3859 (a persisted unmask discloses PII + on a remote-bound management surface). + +The three asked items sit at the **top** of the Lane B stack, so any of them can +be dropped without restacking the rest. + +## Out of scope + +- The live-proxy incident and `src/service.ts` test isolation: PR #4152 owns that, + driven by a separate task. No lane may run `ocx service`, `ocx start/stop/restart`, + `launchctl`, or `systemctl`. +- Live-probe issues: #3782, #3765, #3719, #4143, #4126, #3781, #3775, #3433, + #3522, #3661, #3506. +- Policy issues needing their own interview: #3630, #2730, #3729, #2511, #3377, + #4079, #4024, #3417, #3898, #4055, #3705. + +## Constraints carried from the user + +1. **Never run the local product suite**, `bun test`, `bun install`, + `bun run typecheck`, `bun run build`, or lint. Exact-head remote CI is the + only gate. Skipped local checks are labelled NOT RUN in the PR body. This + overrides the PR-ready gate in `AGENTS.md` for this round. +2. Push with `--no-verify`. +3. A cancelled CI run is never passing evidence. +4. Merge order and rebase timing belong to the main session. +5. Subagents are `xai/grok-4.6` and verify only; lane worker threads run + `anthropic/claude-opus-5`. +6. No heartbeat automations. The main session polls. + +## Terminal outcome + +DONE when every delivered issue and #4147 is closed against a merged `dev` commit +with exact-head CI evidence, and `110_delivery_record.md` records the round. +Dropping an asked item on the maintainer's instruction is a recorded decision, +not a failure. diff --git a/devlog/_plan/260910_post249_round2/010_lane_split.md b/devlog/_plan/260910_post249_round2/010_lane_split.md new file mode 100644 index 0000000000..1ba3ce452f --- /dev/null +++ b/devlog/_plan/260910_post249_round2/010_lane_split.md @@ -0,0 +1,102 @@ +# Lane split, stacking, and merge control + +## Why two lanes and not eight + +Round 1 ran eight parallel worktrees. Every lane opened one PR, and the main +session spent most of its time on merge bookkeeping rather than on the changes. +Two lanes keep the same work but give each worktree one reviewer-visible chain. + +## The split is by file ownership, not by issue count + +The first draft of this plan put four issues in each lane. An audit against the +research reports found the write sets were **not** disjoint: #1711 and #3666 both +edit `src/codex/catalog/parsing.ts` (`CatalogModel`) and +`src/codex/catalog/provider-fetch.ts`, #1711's Dashboard half would collide with +#3666 and #4075 in `gui/src/pages/Models.tsx`, and three of the new tests all need +entries in `scripts/test-layout/layout.json` and +`tests/fixtures/test-layout-expected.json`. + +Two lanes that fight over the same files are worse than one lane, so #1711 moved +to Lane B. The lanes are 3 and 5, and every catalog, GUI, and test-layout edit +lives in one chain where stacking serializes it. + +## Write sets + +**Lane A — request path and service manager.** +`src/server/responses/core.ts`, `src/claude/inbound.ts`, `src/service.ts`, and +tests in `tests/responses/`, `tests/claude-integration/`, `tests/service/`. +Lane A needs **no** `layout.json` or `test-layout-expected.json` change: all three +items extend an existing test file. + +**Lane B — catalog, dashboard, and management projection.** +`src/codex/catalog/` (`parsing.ts`, `provider-fetch.ts`, `sync.ts`), +`src/server/management/` (`shared.ts`, `model-rows.ts`), `src/lib/privacy.ts`, +`src/oauth/index.ts`, `src/codex/auth-api.ts`, `src/cli/models-runtime.ts`, +all of `gui/`, and tests in `tests/codex-integration/`, `tests/gui/`, +`tests/lib/`, `tests/oauth/`, `tests/server/`, `tests/cli/`. Lane B owns every +`layout.json` and `test-layout-expected.json` edit this round. + +The two sets share no file. If a lane finds it needs a file the other owns, it +stops and reports rather than reaching across. + +## Stack shape + +Each lane publishes an ordinary manual chain, bottom-up, on the `dev` commit the +lane started from: + +``` +dev + └─ lane-a/1-4129 PR base dev + └─ lane-a/2-4148 PR base lane-a/1-4129 + └─ lane-a/3-4141 PR base lane-a/2-4148 + +dev + └─ lane-b/1-3666 PR base dev + └─ lane-b/2-4075 PR base lane-b/1-3666 + └─ lane-b/3-3859 PR base lane-b/2-4075 + └─ lane-b/4-1711 PR base lane-b/3-3859 + └─ lane-b/5-4038 PR base lane-b/4-1711 +``` + +No GitHub native stacks. `enforce-target` skips the wrong-base gate for a child PR +whose base is another open PR's head branch, which is what makes this legal. After +a parent merges, the child is retargeted to `dev` — **by main-session instruction, +not by the lane**. + +## Why the order inside each lane + +**Lane A** ends with #4141 because PR #4152 +(`fix(service): stop the test suite from mutating a live service manager`) is open, +not draft, mergeable, and rewrites the same `runLaunchctl` runner in +`src/service.ts` that #4141 has to change. It belongs to a separate task +investigating the live-proxy shutdowns. Putting #4141 last means #4152 lands first +and #4141 adopts its seam instead of racing it. + +**Lane B** puts the two items the maintainer was asked about — #1711 and #4038 — +at positions 4 and 5, so a "drop it" answer removes them without restacking +anything below. #3859 is at 3 because its open question is *which surface*, not +*whether*. #3666 is at the bottom because #1711 builds on the same +`CatalogModel` edit, and #4075 sits next to #3666 since both touch +`Models.tsx`. + +## Merge control + +The main session merges in completion order, not lane order. The rule for each +merge: + +1. The PR is not draft, `mergeable`, and its base is `dev`. +2. There is a CI run at the **exact head SHA** that concluded `success`. + `gh run view --exit-status` is the verdict; a cancelled run is not. +3. Merge, then fetch and prove `dev` ancestry before closing the issue. +4. PRs here target `dev`, so GitHub does **not** auto-close the linked issue. + Close it manually with the merge commit as evidence. + +Rebase instructions come from the main session only, and only when a merge has +actually moved `dev` under a still-open child. + +## Lane operating contract + +Each lane thread runs `cxc-loop` + `cxc-dev`: one PABCD cycle per stack item, its +own `xai/grok-4.6` subagents for bounded read-only verification, and +`--no-verify` pushes. A lane never runs the local suite, never merges, never +rebases without instruction, and never touches the service manager. diff --git a/devlog/_plan/260910_post249_round2/020_4129_shadow_combo_failover.md b/devlog/_plan/260910_post249_round2/020_4129_shadow_combo_failover.md new file mode 100644 index 0000000000..4165c8c9ac --- /dev/null +++ b/devlog/_plan/260910_post249_round2/020_4129_shadow_combo_failover.md @@ -0,0 +1,83 @@ +# A1 — #4129 shadowCallIntercept on a combo never enters the failover loop + +Raw research: `_research/4129.md` (xai/grok-4.6, read-only, verified against `cd813d3d9`). + +## Verdict + +Real, and it is **two** cooperating bugs rather than one. + +## Root cause + +The combo gate runs on the un-rewritten body. `comboIdFromRawBody` reads only +`body.model` (`src/combos/request.ts:20`), and at `src/server/responses/core.ts:3306` +the incoming helper slug is still `gpt-5.6-luna`, which is not a combo id. The +`while (pick)` loop at `core.ts:2798` — inside `handleComboResponses`, declared at +`core.ts:2600` — is therefore never entered, `advanceComboAfterFailure` at +`core.ts:3056` never runs, and 429/5xx hops only exist inside that loop +(`src/combos/failover.ts:468`). + +The shadow rewrite happens later, after parse, at `core.ts:3480-3512`, and +`resolveRoute("combo/shadow")` goes through `routeModel` → `tryPickComboModel` +(`src/router.ts:674`, `src/combos/resolve.ts:418`), which collapses the combo to +**one** target while still tagging `routeKind: "combo"`. That is the reported +"combo route, one attempt" log. + +Second path: `shouldInterceptShadowCall` is +`isShadowSourceModel && !shadowCallTargetsIntersect` (`src/lib/shadow-call.ts:88`). +If the collapsed first pick happens to be `openai/gpt-5.6-luna`, the intersect +check is true (`shadow-call.ts:70`), intercept is skipped entirely, +`shadowCallRewrittenFrom` stays unset and the route is plain native. Swapping the +two blocks does not close this path. + +## Chosen fix + +Add an early branch in `handleResponsesInner` **before** `comboIdFromRawBody` +(`core.ts:3306`), only when `!options.comboAttempt`: + +- raw `body.model` is a string and `isShadowSourceModel(body.model, _sci.sourceModels)` +- `_sci.enabled` and `_sci.model` are set +- `resolveComboId(config, _sci.model)` names a configured combo — resolved by + **config lookup, not `routeModel`/`tryPickComboModel`**, so the combo is never + collapsed to one candidate for the identity check + +Then rewrite `body.model` to `_sci.model` and set `logCtx.shadowCallRewrittenFrom` +through the same `shadowSourceModelPrefix` + sanitize as `core.ts:3502`. The existing +combo gate takes it from there and `handleComboResponses` runs its normal loop. + +Treat a combo selector as routing policy, not as its first pick's identity. Leave +`shouldInterceptShadowCall` alone for direct same-provider replacements (#2706). + +Do **not** re-enter `handleComboResponses` from the late intercept site: that +double-runs `expandPreviousResponseInput` and `onRequestBodyRead`. + +## Policy edges — decided here, out of scope for this PR + +- `parsed._cursorIsolateConversation` (`core.ts:3506`) is not propagated to combo + children. Plumbing a new `HandleResponsesOptions` bit is a separate change and + only matters when a Cursor target sits in the combo. **Deferred**, noted in the + PR body. +- `shadowCallTargetError` (`src/server/management/shadow-call-validation.ts:7`) + collapses the same way, so a dashboard PUT of a Luna-first `combo/shadow` can + still 400 even though file config works. **In scope only if the fix is trivial**; + otherwise report it as a follow-up issue. + +## Regression test + +Extend `tests/responses/responses-shadow-intercept.test.ts` (already in +`layout.json` explicit — no new layout entry needed). Reuse its +`handleResponses` + fake `fetch` + `logCtx` harness. + +1. `shadowCallIntercept.model = "combo/shadow"`, failover combo, inbound + `gpt-5.6-luna`, first target 429 or 503 and second 200 → `response.ok`, + `logCtx.shadowCallRewrittenFrom === "gpt-5.6-luna"`, `logCtx.provider === "combo"`, + `routeDecision.routeKind === "combo"`, and `attempts` of length 2 in configured + order. Red today: one attempt, or a native route with no marker. +2. Same combo with `openai/gpt-5.6-luna` as the **first** target returning 200 → + exactly one upstream call, marker still set, `routeKind === "combo"`. Red today: + `routeKind: "native"`, marker unset. +3. Existing non-combo self-target and prefix-log cases stay green. + +## PR + +`fix(responses): let a combo shadow-call target enter the failover loop` — branch +`lane-a/1-4129`, PR base `dev`, bottom of the Lane A stack. Closes #4129. diff --git a/devlog/_plan/260910_post249_round2/030_4148_claude_system_hoist.md b/devlog/_plan/260910_post249_round2/030_4148_claude_system_hoist.md new file mode 100644 index 0000000000..93e5f7ca0c --- /dev/null +++ b/devlog/_plan/260910_post249_round2/030_4148_claude_system_hoist.md @@ -0,0 +1,86 @@ +# A2 — #4148 mid-conversation Claude `role: system` breaks the prompt-cache prefix + +Raw research: `_research/4148.md`. + +## Verdict + +Real, and deliberate in origin. `cee918ce3` folded in-messages `role: "system"` +into `instructions` so native ChatGPT would not 400 on a `system` input item, and +`tests/claude-integration/claude-inbound.test.ts:313` still locks that shape. The +2026-07-11 note that folding is "the only shape that works on every route" is +stale: Responses accepts chronological `role: "developer"` items. + +## Root cause + +`src/claude/inbound.ts:322-336` pushes **every** `role: "system"` message onto +`systemParts`, including ones that arrive after user/assistant turns, and +`:348` assigns the join to `body.instructions`. `src/responses/parser.ts:144-145` +pushes `data.instructions` onto `systemPrompt` before anything else, so each +injected reminder mutates the prompt head and invalidates the KV prefix. (An +earlier draft cited `parser.ts:204-206`; that span is the `context_compaction` +encrypted-content path, not the instructions read.) The Desktop +`prompt_cache_key` fallback hashes the +same `systemParts` (`inbound.ts:373-394`), so the cache key rotates too. + +## Chosen fix + +In `translateAnthropicRequest`, emit each non-empty in-messages system message as +a chronological input item: + +``` +{ type: "message", role: "developer", content: [{ type: "input_text", text }] } +``` + +Stop pushing it onto `systemParts`. Top-level Anthropic `system` keeps flowing +through `systemToInstructions` into `body.instructions` unchanged. + +**Not `role: "system"` in `input`.** The schema allows it (`src/responses/schema.ts:46-49`) +but ChatGPT Codex rejects it, `parseRequest` re-hoists it (`parser.ts:246-250`), +and the canonical Responses forward folds text-only system items back into +`instructions` (`src/adapters/openai-responses.ts:1472-1512`). `developer` is +first-class (`schema.ts:40-44`, `parser.ts:253-258`) and keeps timeline order. + +**Scope decision — taken here, and it is a policy choice rather than a mechanical +one.** All in-messages system messages become developer items, not just the ones +after the first user turn. `_research/4148.md` marks this exact fork as POLICY. +The leading-only alternative keeps `:313` green but still mutates `instructions` +whenever the client injects a fresh leading system message each turn, which is the +reported failure, so it does not close the issue. Say which choice was taken in +the PR body so a reviewer can object to it. + +## Out of scope, stated in the PR body + +`src/adapters/openai-chat.ts:722-748` re-hoists all text developer messages into +a leading `system` chat message for non-`api.openai.com` Chat Completions, locked +by `tests/adapters/openai/openai-chat-system-order.test.ts:21-47`. That is what +keeps the reporter's DeepSeek/SenseNova path broken, and reversing it is a separate +compatibility tradeoff. OpenCode Go's Muse is `openai-responses`, so the inbound +fix reaches it. `src/chat/inbound.ts:264` has the same anti-pattern for Chat +inbound; not this issue. + +## Regression test + +`tests/claude-integration/claude-inbound.test.ts` (already registered). New case: + +- `system: "S"`; turn 1 `user u1`, `system r1`; turn 2 `user u1`, `system r1`, + `assistant a1`, `user u2`, `system r2` +- `turn1.instructions === turn2.instructions === "S"` +- `turn2.input` roles `["user","developer","assistant","user","developer"]` with + texts `u1, r1, a1, u2, r2` +- no input item has `role === "system"` +- `responsesRequestSchema.parse` and `parseRequest` both succeed +- with no `metadata.user_id`, `turn1.prompt_cache_key === turn2.prompt_cache_key` + +Rewrite `:313` to the new contract; it is the test that encodes the old hoist. +Leave `:66`, `:429`, `:439-492` alone — they use top-level `system`. + +## Known consequence + +Anthropic and Google outbound present developer items as chronological `user` +(`src/adapters/anthropic.ts:711-726`, `src/adapters/google.ts:310`). Semantic +drift from privileged system text, but prefix-stable, which is the point. + +## PR + +`fix(claude): keep mid-conversation system messages in the timeline` — branch +`lane-a/2-4148`, PR base `lane-a/1-4129`. Closes #4148. diff --git a/devlog/_plan/260910_post249_round2/040_4141_launchctl_bootout.md b/devlog/_plan/260910_post249_round2/040_4141_launchctl_bootout.md new file mode 100644 index 0000000000..136a3c4245 --- /dev/null +++ b/devlog/_plan/260910_post249_round2/040_4141_launchctl_bootout.md @@ -0,0 +1,91 @@ +# A4 — #4141 service does not come back after `ocx update` + +Raw research: `_research/4141.md`. + +## Verdict + +Real. `ocx update` stops the service, replaces the binary, then runs +`ocx service repair`, which on darwin is `installLaunchd()` +(`src/service.ts:3129-3131`). That function best-effort `unload`s the plist, runs +`load -w`, and **throws** on any stderr matching `Load failed`/`Bootstrap failed` +(`:2296-2324`, regex at `:919-921`). The thrown text is exactly the operator +message in the issue, and it contains the `bootout` recipe as a hint that nothing +ever executes. + +Modern launchd writes `Load failed: 5: Input/output error` **and exits 0** when a +job is already bootstrapped in `gui/` (`:874-879`, `:913-917`, fixture at +`tests/service/service.test.ts:3141-3145`). So a live-but-stale job is precisely +the case that cannot repair itself. + +`startLaunchd` already handles this correctly — it treats the same `Load failed` +as success when `launchdJobMatchesPlist` says the live job matches (`:2344-2354`). +Repair does not go through `startLaunchd`. + +## The choice being made — and it is a policy choice + +`_research/4141.md` marks "auto-`bootout` versus today's hint-only throw" as +POLICY, and it is right to: `bootout` **kills the live gui job**. That is exactly +the repair the issue asks for, and it is also the reason the current code only +prints the command instead of running it. Taking it is a product decision, so it +goes in the PR body as a decision, not as a bug fix that speaks for itself. + +Two things keep the blast radius honest. It runs only inside `installLaunchd`, +which is already the "put the job back" path, never inside `ocx service start`. +And it fires only after `load -w` has already failed, so a healthy job that loads +cleanly is never touched. + +## Chosen fix — `installLaunchd` only + +1. Replace the discarded `runLaunchctl(["unload", plist])` with + `runLaunchctl(["bootout", \`${launchdGuiDomain()}/${LABEL}\`])`, ignoring absence. +2. If `load -w` still trips `launchctlLoadFailed`, `bootout` once more and retry + `load -w` a single time. Keep the existing throw if the retry also fails. +3. Give `installLaunchd` the same `launchctl`/`matches` injection seam + `startLaunchd` already has (`:2337-2341`). It currently hard-calls + `runLaunchctl` and is unexported, so there is no way to test it otherwise. + +**Do not weaken `launchctlLoadFailed` (`:919-921`).** That regex is the +2026-08-02 silent-success guard; the fix is to recover from the condition, not to +stop detecting it. + +## Deliberately not in this PR + +- `stopLaunchd` / `uninstallLaunchd` / install-cleanup stop (`:2363`, `:2367`, + `:3550`) keep legacy `unload`. Once `installLaunchd` boots out before loading, + changing them is not required, and each has a test pinning its exact string. +- `startLaunchd` keeps throwing on a stale non-matching job (`:2345-2348`). That + throw exists so `ocx service start` never kills a healthy loaded job. +- `statusLaunchd` stays `launchctl list | grep` (`:2364`). Moving it to + `print gui//