diff --git a/.github/pr-assets/3863-storage-skip-referenced.png b/.github/pr-assets/3863-storage-skip-referenced.png
new file mode 100644
index 00000000000..0fcc6370a44
Binary files /dev/null and b/.github/pr-assets/3863-storage-skip-referenced.png differ
diff --git a/.github/pr-assets/codex-quota-evidence.md b/.github/pr-assets/codex-quota-evidence.md
new file mode 100644
index 00000000000..8244c2441bc
--- /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 00000000000..f142433e252
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 00000000000..4fa88c99a64
Binary files /dev/null and b/.github/pr-assets/codex-quota-recovered.png differ
diff --git a/.github/pr-assets/muse-spark-meta-search-content-types-400.jpg b/.github/pr-assets/muse-spark-meta-search-content-types-400.jpg
new file mode 100644
index 00000000000..d18dd98dab3
Binary files /dev/null and b/.github/pr-assets/muse-spark-meta-search-content-types-400.jpg differ
diff --git a/.github/workflows/cleanup-orphaned-workflows.yml b/.github/workflows/cleanup-orphaned-workflows.yml
index 3c33c21dc81..6915a063ed2 100644
--- a/.github/workflows/cleanup-orphaned-workflows.yml
+++ b/.github/workflows/cleanup-orphaned-workflows.yml
@@ -37,7 +37,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
- bun-version: 1.3.14
+ bun-version: 1.4.2
- name: Remove stale workflow histories
env:
diff --git a/.gitignore b/.gitignore
index 4baaef5e6f4..aa014ce1107 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,7 @@ src/generated/compatibility-version.json
devlog/_chase/_cca/
devlog/_chase/_litellm/
devlog/_chase/DSCodex/
+devlog/_chase/CLIProxyAPIPlus/
devlog/_fin/opencode-cursor/
devlog/_plan/*/_ref_*/
devlog/**/*-security-redaction/
@@ -67,3 +68,6 @@ tests/**/.tmp-*
# `git add` three separate times and reached `dev` once — see
# tests/ci-workflows/repo-hygiene.test.ts, which fails if any path here becomes tracked again.
go/
+
+# Rust native helpers keep their reproducible sources and lockfile in git, never local artifacts.
+native/**/target/
diff --git a/.npmignore b/.npmignore
index acf3a0c4d01..cfbe1d3750d 100644
--- a/.npmignore
+++ b/.npmignore
@@ -19,6 +19,7 @@ gui/eslint.config.*
gui/bun.lock
# misc
+native/remote-workspace-helper/target/
*.test.ts
*.map
.DS_Store
diff --git a/AGENTS.md b/AGENTS.md
index 7c677f4bfbe..4abe91c1fd6 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -31,7 +31,14 @@ Bun-native TypeScript with no separate server compile step.
- `go/` — retired Go native-runtime experiment; kept only where the TypeScript
runtime still references it. New work does not go here.
- `structure/` — maintainer invariants and architecture notes; read before
- changing shared subsystems.
+ changing shared subsystems. [`structure/INDEX.md`](./structure/INDEX.md) is the
+ reading order and the source-ownership table, and
+ [`structure/AGENTS.md`](./structure/AGENTS.md) holds the rules for changing
+ anything in there. Ownership is not advisory: changing an owned source area
+ obliges the same change to update its doc, and `bun run structure:check`
+ (wired into the suite by `tests/ci-workflows/structure-ssot.test.ts`) fails on a
+ doc that names a path this tree no longer has, on an invariant whose test is
+ gone, and on a new `src/` area nobody claimed.
- `scripts/` — release and maintenance tooling; `scripts/release.ts` is the
release authority.
- `devlog/` — planning and investigation notes, tracked in this repository. See
@@ -150,8 +157,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
@@ -187,6 +194,8 @@ bun run test:changed # import-graph tests against the resolved `dev` merge bas
bun run test # full tests/ suite (PR-ready / explicit ask only)
bun run lint:gui # GUI eslint
bun run privacy:scan # credential/privacy scan used by CI
+bun run structure:check # structure/ doc-map, ownership, and invariant-binding gate
+bun run structure:index # regenerate structure/INDEX.md from structure/manifest.json
bun run build:gui # Vite GUI build
```
diff --git a/AGENTS_INSTALL.md b/AGENTS_INSTALL.md
index 404d5ec420d..05278f37cb9 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/CONTRIBUTING.md b/CONTRIBUTING.md
index 08db5a6bbbf..ea36e08eb46 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -5,7 +5,7 @@ Thanks for helping with opencodex.
- Start with the canonical guide: [Contributing](https://opencodex.me/contributing/)
- Pull-request quality contract: [Review readiness and author responsibility](https://opencodex.me/contributing/pr-quality/)
- Public user docs live in [`docs-site/`](./docs-site)
-- Current maintainer invariants live in [`structure/`](./structure)
+- Current maintainer invariants live in [`structure/`](./structure); start at [`structure/INDEX.md`](./structure/INDEX.md)
- Maintainer roles and merge policy live in [`MAINTAINERS.md`](./MAINTAINERS.md)
- Attribution for work landed through a maintainer carry lives in [`CREDITS.md`](./CREDITS.md)
- Historical investigations live in [`docs/`](./docs)
diff --git a/CREDITS.md b/CREDITS.md
index 16fa2553d81..5de4f2f6966 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -155,6 +155,22 @@ use the source authors' verified numeric GitHub account identities.
A delivered slice is not a statement that every requirement in its original PR
or umbrella issue is complete. The table deliberately retains the unadopted scope.
+### 2026-09-13 follow-up: landing trailer dropped at merge
+
+The last 3,000 commits reachable from current `dev` were scanned the same way
+as the 2026-09-07 audit: carry/reimplement language on the landing, then the
+**actual landing commit**, then GitHub's commit-author mapping. One new miss
+is not already on this page.
+
+[#4031](https://github.com/lidge-jun/opencodex/pull/4031)'s own description
+named the trailer. The merge commit did not keep it. The cherry-picked object
+is authored as an unmapped machine identity, which GitHub maps to no account.
+The only remaining trailer is automation.
+
+| Pull request | Author | Landed as | What landed |
+| --- | --- | --- | --- |
+| [#3988](https://github.com/lidge-jun/opencodex/pull/3988) | [@rrmlima](https://github.com/rrmlima) | [`e2bf1672c`](https://github.com/lidge-jun/opencodex/commit/e2bf1672c974611f8db736cd64a90e1dc443924a) / [`14ce693e5`](https://github.com/lidge-jun/opencodex/commit/14ce693e5846596c823941ce90add538713a25b1) | "Carries #3988 by @rrmlima (`cherry-pick -x`)" — Gemini/CCA/Vertex/AI Studio model-tail `(continue)` nudge in `messagesToGeminiFormat`. |
+
## Report and diagnosis
These fixes exist because of the report. The branch's own approach was not the
diff --git a/Dockerfile b/Dockerfile
index 79e14399ecc..06011a1f33d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Keep the runtime aligned with package.json and pin the multi-platform image index.
-ARG BUN_IMAGE=oven/bun:1.4.0@sha256:5ff609364c049b54eb0ff560ec96319729a972078ef2c755d758f0c6ef89c2d6
+ARG BUN_IMAGE=oven/bun:1.4.2@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
FROM ${BUN_IMAGE} AS build
WORKDIR /home/bun/app
diff --git a/README.md b/README.md
index b2ab7be63ed..9932901e8cb 100644
--- a/README.md
+++ b/README.md
@@ -106,16 +106,19 @@ See [SPONSORS.md](./SPONSORS.md).
-
-
-
Thanks to SPONSOR for sponsoring this project! BLURB
+
+
Thanks to OrcaRouter for sponsoring this project! OrcaRouter is one OpenAI-compatible AI gateway for production AI: adaptive routing that grades every prompt and sends it to the model that clears your bar, automatic failover, routing rules as code, zero-markup provider pricing with prompt caching, and guardrails, an agent firewall, and request logs on every call across 200+ models. Pick OrcaRouter in the Add provider picker or run ocx provider add orcarouter; orcarouter/auto is the adaptive router.
+
+
+
+
Thanks to PackyCode for sponsoring this project! PackyCode is a stable, high-performance API relay provider, offering relay services for Claude Code, Codex, Gemini, and more. With automatic failover, smart routing, and unlimited concurrency, it turns AI into a real productivity tool. Register via this link and get started! Pick PackyCode in the Add provider picker or run ocx provider add packycode. PackyCode 是一家稳定、高效的 API 中转服务商,提供 Claude Code、Codex、Gemini 等多种中转服务。具备自动故障转移、智能路由和无限并发等多种功能,让 AI 编程成为真正的生产力工具。点此链接注册,立即开始使用!
--->
---
@@ -303,7 +306,7 @@ full-slash form keeps working too. Details: [model routing docs](https://opencod
OpenAI (ChatGPT login or API key), Anthropic, Google Gemini, xAI, Kimi, Azure OpenAI, Ollama
(local + Cloud), Cursor (experimental), and every OpenAI-compatible endpoint — plus DeepSeek,
Groq, OpenRouter, Together, Fireworks, Cerebras, Mistral, Hugging Face, NVIDIA NIM, MiniMax,
-Qwen Cloud, SiliconFlow, and more. Full list: `ocx init` or the
+Qwen Cloud, Qoder Global and CN (official PAT + CLI), SiliconFlow, and more. Full list: `ocx init` or the
[provider docs](https://opencodex.me/guides/providers/).
## CLI
@@ -384,6 +387,9 @@ Maintainer source-of-truth notes live under [`structure/`](./structure), contrib
Report undisclosed vulnerabilities privately through
[GitHub private vulnerability reporting](https://github.com/yansigit/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 b39af65d2a5..ec415e969e0 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/SPONSORS.md b/SPONSORS.md
index a0a2ee33b1b..76a54c377b7 100644
--- a/SPONSORS.md
+++ b/SPONSORS.md
@@ -45,7 +45,7 @@ sponsor receives:
supplied by the sponsor and published verbatim. The maintainer may decline or require edits to
text that is false, misleading, disparages third parties, or breaches applicable law or GitHub
policy. A second-language blurb (for example Chinese) may run alongside the English one.
-- A built-in provider preset (`ocx provider select `) shipped in a public npm release,
+- A built-in provider preset (`ocx provider add `) shipped in a public npm release,
listed near the top of the provider picker in the dashboard and CLI and marked as a sponsor
there. (The registry field and picker ordering that back this land with the first sponsor
preset; today the picker follows registry order.)
diff --git a/assets/pr-screenshots/client-compaction-dashboard.png b/assets/pr-screenshots/client-compaction-dashboard.png
new file mode 100644
index 00000000000..89c3cf1c40a
Binary files /dev/null and b/assets/pr-screenshots/client-compaction-dashboard.png differ
diff --git a/assets/sponsors/orcarouter-overview-mobile.png b/assets/sponsors/orcarouter-overview-mobile.png
new file mode 100644
index 00000000000..a43db457890
Binary files /dev/null and b/assets/sponsors/orcarouter-overview-mobile.png differ
diff --git a/assets/sponsors/orcarouter-overview.png b/assets/sponsors/orcarouter-overview.png
new file mode 100644
index 00000000000..a067b5ea608
Binary files /dev/null and b/assets/sponsors/orcarouter-overview.png differ
diff --git a/assets/sponsors/orcarouter-picker.png b/assets/sponsors/orcarouter-picker.png
new file mode 100644
index 00000000000..2c4e5b16286
Binary files /dev/null and b/assets/sponsors/orcarouter-picker.png differ
diff --git a/assets/sponsors/orcarouter-readme.png b/assets/sponsors/orcarouter-readme.png
new file mode 100644
index 00000000000..dfad19db9e1
Binary files /dev/null and b/assets/sponsors/orcarouter-readme.png differ
diff --git a/assets/sponsors/orcarouter.png b/assets/sponsors/orcarouter.png
new file mode 100644
index 00000000000..2d65f01c08a
Binary files /dev/null and b/assets/sponsors/orcarouter.png differ
diff --git a/assets/sponsors/packycode-overview-mobile.png b/assets/sponsors/packycode-overview-mobile.png
new file mode 100644
index 00000000000..934fa6341ff
Binary files /dev/null and b/assets/sponsors/packycode-overview-mobile.png differ
diff --git a/assets/sponsors/packycode-overview.png b/assets/sponsors/packycode-overview.png
new file mode 100644
index 00000000000..ca86f481f34
Binary files /dev/null and b/assets/sponsors/packycode-overview.png differ
diff --git a/assets/sponsors/packycode-picker.png b/assets/sponsors/packycode-picker.png
new file mode 100644
index 00000000000..0f61d1fa867
Binary files /dev/null and b/assets/sponsors/packycode-picker.png differ
diff --git a/assets/sponsors/packycode-readme.png b/assets/sponsors/packycode-readme.png
new file mode 100644
index 00000000000..36e9f8a667e
Binary files /dev/null and b/assets/sponsors/packycode-readme.png differ
diff --git a/assets/sponsors/packycode.png b/assets/sponsors/packycode.png
new file mode 100644
index 00000000000..8b2afee75fd
Binary files /dev/null and b/assets/sponsors/packycode.png differ
diff --git a/bin/ocx.mjs b/bin/ocx.mjs
index 72615a2e016..ef3aa80cd86 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 @yansigit/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,
@@ -32,6 +40,7 @@ import {
isCodexCliUpdateInspectionArgv,
} from "../src/update/codex-cli-update-launch-policy.mjs";
+const PKG = "@bitkyc08/opencodex";
try {
process.cwd();
} catch {
@@ -43,15 +52,7 @@ try {
}
const require = createRequire(import.meta.url);
const here = dirname(fileURLToPath(import.meta.url));
-function readPublishedPackageName() {
- try {
- const parsed = JSON.parse(readFileSync(join(here, "..", "package.json"), "utf8"));
- return typeof parsed.name === "string" && parsed.name ? parsed.name : "@yansigit/opencodex";
- } catch {
- return "@yansigit/opencodex";
- }
-}
-const PKG = readPublishedPackageName();
+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=";
@@ -61,7 +62,7 @@ function isNodeModulesInstall() {
}
function isBunGlobalInstall() {
- return /[\\/]\.bun[\\/]/.test(here);
+ return installMethod === "bun";
}
function currentPackageVersion() {
@@ -110,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,
});
@@ -143,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.
@@ -185,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.
@@ -270,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,
@@ -304,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 });
}
@@ -313,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,
@@ -373,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();
@@ -431,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
@@ -495,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"));
}
@@ -519,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);
@@ -529,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 ${PKG}\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)"
);
@@ -542,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) {
@@ -574,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).
@@ -592,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/bun.lock b/bun.lock
index e25328f0263..34733cee44a 100644
--- a/bun.lock
+++ b/bun.lock
@@ -3,17 +3,17 @@
"configVersion": 1,
"workspaces": {
"": {
- "name": "@yansigit/opencodex",
+ "name": "@bitkyc08/opencodex",
"dependencies": {
"@bufbuild/protobuf": "^2.14.0",
"@modelcontextprotocol/sdk": "^1.30.0",
"@napi-rs/keyring": "1.3.0",
- "bun": "1.4.0",
+ "bun": "1.4.2",
"zod": "4.4.3",
},
"devDependencies": {
"@anthropic-ai/sdk": "0.122.0",
- "@types/bun": "1.4.0",
+ "@types/bun": "1.4.2",
"typescript": "7.0.2",
},
"optionalDependencies": {
@@ -68,33 +68,33 @@
"@napi-rs/keyring-win32-x64-msvc": ["@napi-rs/keyring-win32-x64-msvc@1.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-4DnCWXwDc0HRKwyRlG5y0VhKZW2tNRQfKKfyj6IX/KWfDNyq9hn4n+GL1auyDcOO/v8PwnhmYo2+rOOqCkvvOg=="],
- "@oven/bun-darwin-aarch64": ["@oven/bun-darwin-aarch64@1.4.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GCpf8QuFLsyioVawP5HrMxA1ZRBlu6Hq9RNnSc3UTUWAzIxBso9trjoZczw1HdgpqSssFkszfIV2zmOzFTjhkw=="],
+ "@oven/bun-darwin-aarch64": ["@oven/bun-darwin-aarch64@1.4.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MXdZkP1featqxZ+/VTXWG1BVjM4OGBehVY2Q88EeUj/7L0UMeCGItmyPYTN+wxvlGJ6F66JEtzsw+GvQWewnag=="],
- "@oven/bun-darwin-x64": ["@oven/bun-darwin-x64@1.4.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-cIrhwOr0SPEraewznhC+c/k6TG8bwFn5uZ4EJuXwjiKJLcAF36q7/bGjWkeXSe48JwMcPRUR054JXF7+cRwSSA=="],
+ "@oven/bun-darwin-x64": ["@oven/bun-darwin-x64@1.4.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-gZTxZuLjkUhAWjTETu3tw0WhsEdNkJ64daj60ybhPf835a2yollV3yTkK9JozvzKPx4TRFzLSl8C+U525pxVbw=="],
- "@oven/bun-freebsd-aarch64": ["@oven/bun-freebsd-aarch64@1.4.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-09x7wnjMR6M5KGBDBhVl2CpfoCIQOkVDbPX2KfIhpXv4N6grbWE7dfLPw/Ydi9gaUMGhU7UKhoz444Nu6RCycA=="],
+ "@oven/bun-freebsd-aarch64": ["@oven/bun-freebsd-aarch64@1.4.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-SMNItMw1Z8QeeQVKnw8jA7xQNkeXdP+OPgin4Wi/QTx/B8RHHLnuZfqmFy7NtVeT2NF0kKYppW4WWd2CCYZjhQ=="],
- "@oven/bun-freebsd-x64": ["@oven/bun-freebsd-x64@1.4.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dRwzti/qJqV1HWplU27iUWUqp+f2DtFSf2yqQKSb+HH2dDOC//Uqd9u/A5h1DMsLszfP5OGP9UwQIKxVwFODaA=="],
+ "@oven/bun-freebsd-x64": ["@oven/bun-freebsd-x64@1.4.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-THbPKXhO54N0DpFRKZNDZpQ7dpbX0bWASuARckAUS9wRtFIHsiY+uULXJvxJGo2YD1YewvXQ4G8Fj7XT5oBCiw=="],
- "@oven/bun-linux-aarch64": ["@oven/bun-linux-aarch64@1.4.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y5yAtCbHK6JjprXEtkdklDQFPADgs+CkfcliyY5g4JJ8baGHyQSrfpSkX3XVJ2C+aBLsdwNDdW+oczMsAwx6uA=="],
+ "@oven/bun-linux-aarch64": ["@oven/bun-linux-aarch64@1.4.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-3BBP9ovJ2RGHFH6Ae1CAtxNtG1+YY6GD6rmYbsUosoAk9+OEl6zeDQ/k4fBkc6dYOJCtWnx8hUxzNzQATSmvYQ=="],
- "@oven/bun-linux-aarch64-android": ["@oven/bun-linux-aarch64-android@1.4.0", "", { "os": "android", "cpu": "arm64" }, "sha512-HpPIxJfDNPBPhiBNMyZoo/dOLijARfsx5j72vNuLtaTvl0Hh7HUculxjsOQ2WSyGoCgqXMEr1Qqjab1im9u1RA=="],
+ "@oven/bun-linux-aarch64-android": ["@oven/bun-linux-aarch64-android@1.4.2", "", { "os": "android", "cpu": "arm64" }, "sha512-3mZKO2rhsNgbAUtAHC1UKUlF2zTxFraDZT/Elv8wzyH0fJL9h+Iv3TgB9lO63w89PRn3eFe+NRA1bhVgikKNPQ=="],
- "@oven/bun-linux-aarch64-musl": ["@oven/bun-linux-aarch64-musl@1.4.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RUjAAkJ/CdNV++zVxyANWshPc73CECYsfhk0fWAkoJjtywxJ2BwXzI6nopBBDMfs0HS+fhRGn6zGwU8ccxLeJg=="],
+ "@oven/bun-linux-aarch64-musl": ["@oven/bun-linux-aarch64-musl@1.4.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-+Sm6y+lSiSFBOtXmnekp5Q6n1tUKlyv71FCPWBc61Cgb14T5eBs8SN/nh4MUCOKzONkI3O+as3MGUgikS4aCBQ=="],
- "@oven/bun-linux-x64": ["@oven/bun-linux-x64@1.4.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Du44zebtPXJujvMLmtIxEQ6ykOhYt7L/Q+YIGVm+Yy+Pj/fpOnq60ggwIpKp/pGAFbYHNiTrA3JTjuZ9MTbZIg=="],
+ "@oven/bun-linux-x64": ["@oven/bun-linux-x64@1.4.2", "", { "os": "linux", "cpu": "x64" }, "sha512-9/E/UXOTpSo3YsV5g+FhtTd/qTpiWoKuxS12cqtuYA1ssu9fRAoPQnipFgGyck3tWO63iUdxBiygq+kELFawng=="],
- "@oven/bun-linux-x64-android": ["@oven/bun-linux-x64-android@1.4.0", "", { "os": "android", "cpu": "x64" }, "sha512-u++KyLlfMn36yWz+AgJs+fZtS46UFDNpSSZhrcitkytONtNwq0X6Q9BDVEFXxYl/+Eec0xme1rb6MgW+U35WeA=="],
+ "@oven/bun-linux-x64-android": ["@oven/bun-linux-x64-android@1.4.2", "", { "os": "android", "cpu": "x64" }, "sha512-6HC5tzcC79113n2IHCTJMWv+HsQImv4ZFEK2XpYLxY6HbT8tM4cUM2Zv1bHZBQsS3jv/zYBamDJ1UX7If0d5tw=="],
- "@oven/bun-linux-x64-musl": ["@oven/bun-linux-x64-musl@1.4.0", "", { "os": "linux", "cpu": "x64" }, "sha512-C1Dv+ISL8YKEKM9jAHzNifOcRUoziy6UMxh+yVXjUCP6QnbRhENDHLaIWWkQZJyBLTn0I3xozflorAlHiGzGqA=="],
+ "@oven/bun-linux-x64-musl": ["@oven/bun-linux-x64-musl@1.4.2", "", { "os": "linux", "cpu": "x64" }, "sha512-vVTKUg1bnPhRP/Hp73jIVoFh2vPFNYEqYX0ERKfZBOQEEHitNAeukZzzuUDZS0SoDCIpuWUGSpd/CDMbjdR+Uw=="],
- "@oven/bun-windows-aarch64": ["@oven/bun-windows-aarch64@1.4.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-FBAYaQpJBP0asgqzL6NFUfjdQqsV+kvTpJ/eWxPKj+RcDgIfPSuE8kvQuPYu5pa8u8JTujYMjmuyvHxVuQsInA=="],
+ "@oven/bun-windows-aarch64": ["@oven/bun-windows-aarch64@1.4.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-8EJ1ST7339WJE3poPW5nBgVW/lWf9HBz4W27ZUNhburKmcBLOByPyE6DP9fHD8FQGm5c+ilUN2hX1mrW0jxq9Q=="],
- "@oven/bun-windows-x64": ["@oven/bun-windows-x64@1.4.0", "", { "os": "win32", "cpu": "x64" }, "sha512-jRKv1NPLznMSZY5BEWciMF7zv0Tiyo2pQSxAJ3w+YWJ6y3VWNJQQQdLlV5Jx8lbOFDrJdrc9dD3GV17k3BP41A=="],
+ "@oven/bun-windows-x64": ["@oven/bun-windows-x64@1.4.2", "", { "os": "win32", "cpu": "x64" }, "sha512-+bN6OuVld/9diT/RLSXSW7JE6CvNE3gL9XsAEjULi1nUsXd6DNO6GuA9jNdNb3r8PdJFnYHr5aypNV1Oj3Rd9g=="],
"@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="],
- "@types/bun": ["@types/bun@1.4.0", "", { "dependencies": { "bun-types": "1.4.0" } }, "sha512-K+lZULY23vRgK/CfTjFIV+tyifaNdSMlPh9j+6mQ/cLfpOznLyAuzgV/JQysyECpkBQLVMSyvjlr2fBUSA9wFQ=="],
+ "@types/bun": ["@types/bun@1.4.2", "", { "dependencies": { "bun-types": "1.4.2" } }, "sha512-GimotNn7+ZV0uVArItBbriZsR1oNf0+WTzPkdcFrzShI7k2norL0uzEaJT8T33dWr7O/c9ZDuAFQrctKCi72oQ=="],
"@types/node": ["@types/node@26.0.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw=="],
@@ -164,9 +164,9 @@
"body-parser": ["body-parser@2.3.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.0.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", "qs": "^6.15.2", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw=="],
- "bun": ["bun@1.4.0", "", { "optionalDependencies": { "@oven/bun-darwin-aarch64": "1.4.0", "@oven/bun-darwin-x64": "1.4.0", "@oven/bun-freebsd-aarch64": "1.4.0", "@oven/bun-freebsd-x64": "1.4.0", "@oven/bun-linux-aarch64": "1.4.0", "@oven/bun-linux-aarch64-android": "1.4.0", "@oven/bun-linux-aarch64-musl": "1.4.0", "@oven/bun-linux-x64": "1.4.0", "@oven/bun-linux-x64-android": "1.4.0", "@oven/bun-linux-x64-musl": "1.4.0", "@oven/bun-windows-aarch64": "1.4.0", "@oven/bun-windows-x64": "1.4.0" }, "os": [ "!aix", "!sunos", "!openbsd", ], "cpu": [ "x64", "arm64", ], "bin": { "bun": "bin/bun.exe", "bunx": "bin/bunx.exe" } }, "sha512-iRiFkc2W7UVpCyZXO9tod45TP9QCyN19fWqbpeN/jaM/K7uzeHYx/OSPsahMJazGKBgPsnxRt+4Jc43d8BcHZw=="],
+ "bun": ["bun@1.4.2", "", { "optionalDependencies": { "@oven/bun-darwin-aarch64": "1.4.2", "@oven/bun-darwin-x64": "1.4.2", "@oven/bun-freebsd-aarch64": "1.4.2", "@oven/bun-freebsd-x64": "1.4.2", "@oven/bun-linux-aarch64": "1.4.2", "@oven/bun-linux-aarch64-android": "1.4.2", "@oven/bun-linux-aarch64-musl": "1.4.2", "@oven/bun-linux-x64": "1.4.2", "@oven/bun-linux-x64-android": "1.4.2", "@oven/bun-linux-x64-musl": "1.4.2", "@oven/bun-windows-aarch64": "1.4.2", "@oven/bun-windows-x64": "1.4.2" }, "os": [ "!aix", "!sunos", "!openbsd", ], "cpu": [ "x64", "arm64", ], "bin": { "bun": "bin/bun.exe", "bunx": "bin/bunx.exe" } }, "sha512-TrSXo6HJfIEaczpb3kjX82I2pL47vK1QUNmHRCUdz9IzaOwa9lzOXSWwu2l18YHE3sNfGRapVLd4nNm+22vVVA=="],
- "bun-types": ["bun-types@1.4.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-iIKw23BspnQQYd3prITOBxeUsxBHnwzX6YJfGMuNOZzeNcMmVqzIIVGRm1l69ogaPQmb4wB6BN8mA5bE9YuC5Q=="],
+ "bun-types": ["bun-types@1.4.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-bxV1FgK7yBIzjRe5zBozIM4Bem11ZJcCXSrjWRG3YWLt8yFDePu4cLjpebO8OvPeIE9trbyPF4fuj3Cia4Fj3w=="],
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
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 00000000000..2951fdea1e2
--- /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 00000000000..de5ec0216c5
--- /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 00000000000..c87252e40ab
--- /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 00000000000..4b1ee418c17
--- /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 00000000000..1547db2ff27
--- /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 76%
rename from devlog/_plan/260904_dashboard_minimal/000_inventory.md
rename to devlog/_fin/260904_dashboard_minimal/000_inventory.md
index 2487c1afae9..a1a1d0b0945 100644
--- a/devlog/_plan/260904_dashboard_minimal/000_inventory.md
+++ b/devlog/_fin/260904_dashboard_minimal/000_inventory.md
@@ -1,31 +1,34 @@
# 000 — Dashboard inventory (as shipped, v2.42.0, dev @ 664d80c76)
-Evidence: `assets/_1440.png` (full page, ko, 1440 px headless Chrome against the live
-proxy), `assets/_text.txt` (visible text), `assets/_interactive.txt` (interactive
-controls with refs, `agbrowse snapshot --interactive`). Storage was captured mid-scan (its skeleton
-is the honest first paint on a 1.6 GB CODEX_HOME) and is inventoried from source.
+This inventory used full-page Korean captures at 1440 px against the live proxy,
+plus visible-text and interactive-control snapshots. Storage was captured mid-scan:
+its skeleton records the first paint, and its full inventory came from source.
+
+The capture pack was removed from the current tree after review. The route,
+control-count, and source inventory below retain the historical conclusions.
+Git history is unchanged.
Counts are from the captures: interactive = controls in the snapshot, words = visible text words.
-| Route | Source | Interactive | Words | Screenshot |
-|---|---|---|---|---|
-| Sidebar + top bar | gui/src/App.tsx, components/sidebar-github-row.tsx, styles.css | 22 | — | every capture, left rail |
-| #dashboard (overview) | pages/Dashboard.tsx, dashboard-overview-sections.tsx (669 L), dashboard-dialogs.tsx | 34 | 199 | dashboard_1440.png |
-| #dashboard/providers | same | 18 | — | dashboard_providers_1440.png |
-| #dashboard/models | same | 28 | — | dashboard_models_1440.png |
-| #startup | pages/Startup.tsx (403 L), startup-sections.tsx | 22 | 167 | startup_1440.png |
-| #providers | pages/Providers.tsx, components/provider-workspace/* | 27 | 310 | providers_1440.png |
-| #models | pages/Models.tsx (2329 L) | 135 | 460 | models_1440.png |
-| #models/combos | pages/Combos.tsx, components/combo-workspace-* | 59 | — | models_combos_1440.png |
-| #models/routing | pages/RoutingProfiles.tsx (1139 L) | 28 | — | models_routing_1440.png |
-| #models/compatibility | pages/CompatibilityMatrix.tsx | 27 | — | models_compatibility_1440.png |
-| #subagents | pages/Subagents.tsx, components/subagents-workspace/* | 60 | 232 | subagents_1440.png |
-| #logs | pages/Logs.tsx (1147 L) | 50 | 346 | logs_1440.png |
-| #logs/debug | pages/Debug.tsx, debug-log-viewer.tsx | 24 | — | logs_debug_1440.png |
-| #usage | pages/Usage.tsx (889 L) | 27 | 654 | usage_1440.png |
-| #storage | pages/Storage.tsx (1469 L), components/storage-workspace/* | 16 (skeleton) | — | storage_1440.png |
-| #codex-set | pages/codex-set-multiauth.tsx, codex-set-prompt.tsx, components/codex-set/*, CodexAccountPool.tsx | 51 | 361 | codex-set_1440.png |
-| #integrations | pages/Integrations.tsx, ApiKeys.tsx, Claude*.tsx, Grok.tsx | 86 | 233 | integrations_1440.png |
+| Route | Source | Interactive | Words |
+|---|---|---|---|
+| Sidebar + top bar | gui/src/App.tsx, components/sidebar-github-row.tsx, styles.css | 22 | — |
+| #dashboard (overview) | pages/Dashboard.tsx, dashboard-overview-sections.tsx (669 L), dashboard-dialogs.tsx | 34 | 199 |
+| #dashboard/providers | same | 18 | — |
+| #dashboard/models | same | 28 | — |
+| #startup | pages/Startup.tsx (403 L), startup-sections.tsx | 22 | 167 |
+| #providers | pages/Providers.tsx, components/provider-workspace/* | 27 | 310 |
+| #models | pages/Models.tsx (2329 L) | 135 | 460 |
+| #models/combos | pages/Combos.tsx, components/combo-workspace-* | 59 | — |
+| #models/routing | pages/RoutingProfiles.tsx (1139 L) | 28 | — |
+| #models/compatibility | pages/CompatibilityMatrix.tsx | 27 | — |
+| #subagents | pages/Subagents.tsx, components/subagents-workspace/* | 60 | 232 |
+| #logs | pages/Logs.tsx (1147 L) | 50 | 346 |
+| #logs/debug | pages/Debug.tsx, debug-log-viewer.tsx | 24 | — |
+| #usage | pages/Usage.tsx (889 L) | 27 | 654 |
+| #storage | pages/Storage.tsx (1469 L), components/storage-workspace/* | 16 (skeleton) | — |
+| #codex-set | pages/codex-set-multiauth.tsx, codex-set-prompt.tsx, components/codex-set/*, CodexAccountPool.tsx | 51 | 361 |
+| #integrations | pages/Integrations.tsx, ApiKeys.tsx, Claude*.tsx, Grok.tsx | 86 | 233 |
## Element-level notes from the captures (main agent's own pass)
diff --git a/devlog/_plan/260904_dashboard_minimal/001_subagent_opinions.md b/devlog/_fin/260904_dashboard_minimal/001_subagent_opinions.md
similarity index 99%
rename from devlog/_plan/260904_dashboard_minimal/001_subagent_opinions.md
rename to devlog/_fin/260904_dashboard_minimal/001_subagent_opinions.md
index d4ccdedcd9b..a4d9723fa8d 100644
--- a/devlog/_plan/260904_dashboard_minimal/001_subagent_opinions.md
+++ b/devlog/_fin/260904_dashboard_minimal/001_subagent_opinions.md
@@ -1,8 +1,10 @@
# 001 — Subagent opinions (independent, read-only, dev @ 664d80c76)
-Three reviewers were dispatched in parallel with the same packet (evidence pack in `assets/`,
-full source access, no edits, no suites, no proxy mutation). Model requested → model that
-answered (as self-reported in the REVIEWER line):
+Three reviewers were dispatched in parallel with the same temporary evidence packet and full
+source access (no edits, no suites, no proxy mutation). The packet was subsequently removed from
+the current tree. Filenames remain below only to preserve the reviewers' original reasoning.
+Model requested → model
+that answered (as self-reported in the REVIEWER line):
| # | Requested | Answered as | Agent | Status |
|---|---|---|---|---|
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 00000000000..4101656c12c
--- /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 93%
rename from devlog/_plan/260904_provider_quota_refresh/000_plan.md
rename to devlog/_fin/260904_provider_quota_refresh/000_plan.md
index e37cdca4c2b..9a378ce9651 100644
--- a/devlog/_plan/260904_provider_quota_refresh/000_plan.md
+++ b/devlog/_fin/260904_provider_quota_refresh/000_plan.md
@@ -1,7 +1,6 @@
# Provider quota refresh affordance + Meta usage visibility
-Unit opened 2026-09-04. Two defects reported against the live Providers dashboard
-on `http://localhost:10100/#providers`:
+Unit opened 2026-09-04. Two defects reported against the live Providers dashboard:
1. Only the Codex account pool has a "Refresh quotas" button. Every other provider
— anthropic, xai, cursor, google-antigravity, meta-muse — offers the operator no
@@ -9,7 +8,7 @@ on `http://localhost:10100/#providers`:
2. Meta Muse shows no quota on the provider Usage tab even though the proxy has an
observation for it.
-## Evidence gathered at P (live proxy, port 10100, v2.42.0, pid 73184)
+## Evidence gathered at P (live proxy, v2.42.0)
`GET /api/provider-quotas` returns six reports, and `meta-muse` is one of them:
@@ -77,6 +76,7 @@ config save, provider add/remove. There is no operator-initiated path. The
`bun x tsc --noEmit`, `bun run lint:gui` only.
- Push with `--no-verify`; branch `codex/260904-provider-quota-refresh`; target `dev`.
- A GUI-mentioning PR requires a screenshot in the description (`enforce-target`).
-- The live proxy on port 10100 is the user's working service. Read it, restart it
- only when a rebuild must be picked up, never repoint or reconfigure it.
+- Verification must use an isolated scratch instance. Do not restart, repoint or
+ reconfigure the user's working proxy. The completed isolation record supersedes
+ the original working-service restart plan.
- `refresh=1` must never cause a passive provider to spend an inference turn.
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 56%
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
index a8c4168b63d..29fdc19756b 100644
--- 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
@@ -8,32 +8,34 @@ phase is the evidence phase.
1. `bun run build:gui` — the service serves `gui/dist`, so an unbuilt change is
invisible no matter how green the tests are.
-2. `ocx service restart` — picks up the server-side `observed` flag. Confirm a new
- pid and fresh uptime on `/healthz`, and that the port is still 10100. The service
- is the user's own; restart it, never repoint or reconfigure it.
-3. `curl /api/provider-quotas` with the admin token — the meta-muse row must now
+2. Load the rebuilt code in the isolated scratch instance and confirm its identity
+ and fresh uptime on `/healthz`. The completed isolation record supersedes the
+ original `ocx service restart` plan; do not restart, repoint or reconfigure the
+ user's working proxy.
+3. Query the scratch instance's `/api/provider-quotas` with its admin token — the meta-muse row must now
carry `"observed": true`. This is the wire-level proof, checked before the UI so a
blank screen can be attributed correctly.
-## Browser verification (`aside-jun`, CLI repl on the signed-in profile)
+## Browser verification (aside CLI repl on the signed-in profile)
The dashboard is loopback and needs no login, so `aside repl` is the right surface:
one invocation is one session, it throws on a bad path instead of skipping, and the
screenshots land as real files. A whole inspect-act-verify flow must fit in a single
invocation because bindings do not persist between calls.
-Shots to capture into `devlog/_plan/260904_provider_quota_refresh/assets/`:
+The planned capture set covered the Usage quota display, the Usage and Accounts
+refresh controls, and the post-click success state. The completed record in
+`031_live_verification_record.md` preserves the observed outcomes.
-| File | Content |
-|------|---------|
-| `010_meta_usage_quota.png` | meta-muse → Usage tab with both windows and the observation age |
-| `020_usage_refresh_button.png` | the Usage rate-limits header with its refresh control |
-| `030_accounts_refresh_button.png` | the Accounts tab refresh control for an OAuth provider |
-| `040_refresh_result.png` | the post-click success status |
+Those live captures were subsequently removed from the current tree under one
+retention rule for both surfaces: account and usage screenshots from a real
+operator profile are unnecessary once the behavioral evidence is recorded in
+text. The planned filename list is also retired; it differed from the delivered
+filenames and must not be treated as an asset inventory.
-Aside writes under `~/.aside/u/0/`; Codex copies the files into the repository. Every
-`aside` invocation runs under `perl -e 'alarm shift; exec @ARGV' 300` because macOS
-has no `timeout` and the bare spelling exits 127 without ever starting the run.
+The plan required each `aside` invocation to run under
+`perl -e 'alarm shift; exec @ARGV' 300` because macOS has no `timeout` and the bare
+spelling exits 127 without starting the run.
## Push and PR
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 64%
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
index cda6fdbe9e8..3dfbafb3185 100644
--- a/devlog/_plan/260904_provider_quota_refresh/031_live_verification_record.md
+++ b/devlog/_fin/260904_provider_quota_refresh/031_live_verification_record.md
@@ -1,20 +1,14 @@
# Live verification record — 2026-09-04
Both defects were reproduced and then confirmed fixed against a running proxy serving the
-built GUI. Screenshots in `assets/`.
+built GUI. The observations below preserve that historical verification.
## Isolation
-The user's own proxy runs on port 10100 from
-`/Users/jun/Developer/new/700_projects/opencodex` under launchd — a different checkout
-from this worktree, so restarting it would NOT have loaded this change, and repointing it
-is out of bounds. Verification therefore ran on a scratch instance:
-
-- `OPENCODEX_HOME` = a `mktemp -d` directory holding only `config.json` (three providers),
- `auth.json`, and `provider-account-quota-cache.json` copied from the real home.
-- port 10399, started with `bun run src/cli/index.ts start --port 10399` from this worktree.
-- Port 10100 was confirmed untouched afterwards: same pid 73184, uptime still climbing.
-- The scratch home was moved to Trash when finished.
+Verification ran against an isolated scratch instance. The existing proxy used a
+separate checkout and was left untouched; its process identity and increasing
+uptime were confirmed afterward. The scratch home was moved to Trash when
+verification finished.
## Wire evidence
@@ -46,17 +40,16 @@ The refresh control was exercised, not merely rendered:
- Usage tab: clicking `Refresh quotas` produced `status: "Quotas refreshed"` and the age
line re-derived from `5h ago` to `6h ago` — the read really happened.
-- Accounts tab (anthropic, three pooled accounts): the control appears beside
+- Accounts tab (pooled OAuth provider): the control appears beside
`Add account` and reported `Quotas refreshed` after a real forced read.
-## Assets
+## Capture retention
-| File | Content |
-|---|---|
-| `010_meta_usage_quota.png` | Muse Code → Usage with both windows and the refresh control |
-| `020_usage_refresh_result.png` | the same tab after a click, showing the success status |
-| `030_accounts_refresh_button.png` | Accounts tab control for a pooled OAuth provider |
-| `040_accounts_refresh_result.png` | Accounts tab after a click |
+The Accounts and Usage captures were subsequently removed from the current tree.
+Both came from a real operator profile; retaining either surface is unnecessary
+for the behavioral evidence above. This applies the same retention rule to both
+surfaces without claiming that the Usage captures were independently cleared of
+personal information. Git history is unchanged.
## CI (PR #3448, head 232afdd97)
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 00000000000..b612a406713
--- /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 00000000000..1cede7db071
--- /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 00000000000..f1ae8e5c8a8
--- /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 00000000000..14e3301678e
--- /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/_fin/260906_release_243/000_plan.md b/devlog/_fin/260906_release_243/000_plan.md
new file mode 100644
index 00000000000..e0a7f577e3f
--- /dev/null
+++ b/devlog/_fin/260906_release_243/000_plan.md
@@ -0,0 +1,15 @@
+# Release 2.43.0
+
+Loop archetype: spec-satisfaction release operation; one PABCD cycle.
+Trigger: owner explicitly requests readiness audit, preview/main merges and release.
+Goal: promote RC af50c6d3451078a7d298b044c08fd2684c9e8eeb into preview/main and publish 2.43.0-preview.20260906 and 2.43.0 with matching registry gitHead, tags and successful exact-SHA release workflows.
+Scope: GitHub release/version PRs, required CI workflows, npm OIDC release, local isolated release worktree. Preserve original checkout and existing dirty devlogs. No unrelated open PR integrations or local service reconfiguration.
+Verifier: gh run view/list (executed, exit 0, reports exact SHA job results); git ls-remote (executed exit 0, observes remote heads); git tree comparison; registry metadata and GitHub release readback after publish. No local full suite: hosted CI is the full gate.
+Stop: both releases and artifacts verified. If a real release blocker emerges, record it and resolve only scoped operational/version defects; broader code repair requires replan. Terminal: DONE, BLOCKED or NEEDS_HUMAN with explicit evidence. No silent gate bypass.
+Memory: this unit plus scratch release evidence; one release operation cycle, not separate implementation units.
+Resources: existing gh credential and OIDC workflow only; no secret reads; repository branch/PR/tag/release writes approved by request. No purchased compute or external messages. Hosted CI bounded to one active release per channel and one evidence-based flaky retry. Wall-clock checkpoint at two hours; do not claim completion at timeout.
+Authoritative policies: MAINTAINERS.md, scripts/release.ts, release.yml, dev-version-bump.yml, service-lifecycle.yml from pinned RC.
+Baseline: main 48f8186647d9ffb108d226dcfa91a64225aae2a7 v2.42.0; preview 0748cf50b67103bdc93123acae0d0c545a8cf902 version 2.43.0-preview.20260904 (not yet assumed published). RC push CI 33974061890 success; no exact RC Service lifecycle yet.
+Escalation: release gate failures are blocking. Maintainer administrative PR merge is authorized by owner's merge-and-release instruction; record bypass use in PR description if required by rulesets, never fake approval. An external maintainer decision outside this scope is reported.
+
+Readiness refresh: RC Service lifecycle run 33976119109 passed linux-systemd, macos-launchd and windows-schtasks. Full Windows test matrix remains excluded by ci.yml:643-656; installed/keyring Windows smokes passed. Open draft fixes 3669/3672/3673 document edge-case existing behavior; do not claim these are fixed. PR 3671 has an already-public policy-boundary report and is pending explicit security review; implicated assemble.ts is unchanged between released main and RC (last changed #1681). No new change to that boundary is proposed by this promotion. This audit is release readiness, not a claim that the entire product is defect-free.
diff --git a/devlog/_fin/260906_release_243/002_audit.md b/devlog/_fin/260906_release_243/002_audit.md
new file mode 100644
index 00000000000..b99cca45dc9
--- /dev/null
+++ b/devlog/_fin/260906_release_243/002_audit.md
@@ -0,0 +1,13 @@
+# Release audit
+
+Two independent auditor dispatches returned no usable result within their bounded waits and were retired. Main reclaimed the packet rather than treating silence as approval. This is a direct audit, not an independent-review claim.
+
+1. Version ordering verified in release.yml and version-line.ts: dev must outrank 2.43.0; use existing bump script to 2.44.0 before publication.
+2. Bootstrap amendment accepted: default main does not register dispatch yet; one-file manual pre-move PR is supported by existing helper and preserves protected branch boundary.
+3. RC push CI 33974061890 and service 33976119109 passed; repeat on each actual promotion SHA as required. Windows full suite deliberately non-gating; no full Windows green claim.
+4. Preview merge-tree conflicts in package.json only. Main contains no divergent commits. Require post-merge RC ancestry and exact file parity excluding version.
+5. Owner admin merge authorization recorded in PRs; no self-approval or failed functional check bypass. Production payload comes only from pinned RC already integrated into dev.
+6. Open bug PRs remain visible; no unrelated draft integration. Public PR 3671 boundary existed in main before RC, and is not modified by promotion. General readiness is not defect-free certification.
+7. Release workflow validates exact branch SHA, push CI, lifecycle, dev readiness, global version order, duplicate metadata and npm audit before OIDC publication. Readback npm gitHead and immutable tags after each channel; never rerun an apparently failed publish until inspecting actual registry metadata.
+
+Main verdict: PASS for proceeding to version/promotion PR preparation. Independent audit unavailable; inherited integrated reviews plus current CI and direct release-mechanics inspection are the evidence. No production-code or credential boundary edits are included.
diff --git a/devlog/_fin/260906_release_243/010_promotion.md b/devlog/_fin/260906_release_243/010_promotion.md
new file mode 100644
index 00000000000..93800230505
--- /dev/null
+++ b/devlog/_fin/260906_release_243/010_promotion.md
@@ -0,0 +1,11 @@
+# Promotion implementation
+
+1. Pin RC af50c6d3451078a7d298b044c08fd2684c9e8eeb. Audit release-specific risks and open blockers using current GitHub state. Dispatch lifecycle for RC if absent.
+2. Default main still has workflow_call only, so workflow_dispatch is not yet registered. Use the existing scripts/bump-dev-version.ts 2.43.0 package.json locally on a dedicated branch and open a one-file package.json 2.43.0 -> 2.44.0 pre-move PR. Merge only after exact-head checks; this bootstraps the first release of the new pre-move workflow without changing it. Do not promote the new dev version into release payload.
+3. In isolated /private/tmp/ocx-release-01a07240, build promotion branch from origin/preview, merge pinned RC preserving ancestry, resolve package.json only to 2.43.0-preview.20260906. Any non-version conflict is audited explicitly. Push promotion branch, create templated PR to preview, inspect checks and owner-authorized merge. Prove RC ancestor and tree parity excluding package.json against RC. Wait branch push ci.yml and service-lifecycle.yml exact merged SHA. Publish via release.yml on preview, version and expected-sha pinned, tag preview, dry-run false. Verify GitHub/npm/tag before stable.
+4. Build main promotion from origin/main, merge the same pinned RC, set package.json 2.43.0. Follow same PR/check/ancestry/tree proof and branch push gates. Publish release.yml main tag latest with exact expected-sha.
+5. Verify registry latest and preview versions, gitHead against merged commits, release tags, release workflows and clean dedicated worktrees. Re-check original checkout changes preserved. Record evidence and close FSM.
+
+No production code edits intended. Changes are package versions and merge ancestry only; prepublish assets produced by release workflow. Main and preview independently inherit RC; preview need not be main ancestor. Public devlog omits unreleased security findings; any such analysis stays in ignored scratch.
+
+Execution amendment: the owner reiterated shipping this candidate now and deferring fixes. Preview/main PR preparation is parallel because both consume the same immutable RC; publication stays preview first, stable second. PRs: #3676 pre-move (1a6ebc22c), #3677 preview (5c2d63465), #3678 main (af50c6d34). Local broad pre-push hook deferred to exact-head hosted CI; no gate weakening or workflow edits.
diff --git a/devlog/_fin/260906_release_243/011_promotion_result.md b/devlog/_fin/260906_release_243/011_promotion_result.md
new file mode 100644
index 00000000000..cfb3f0b8267
--- /dev/null
+++ b/devlog/_fin/260906_release_243/011_promotion_result.md
@@ -0,0 +1,11 @@
+# Promotion result
+
+Owner reaffirmed shipping this candidate and deferring fixes.
+
+- #3676 merged as 81871b3fa7034250b8d5ba2cbbfde44e40f0e69c; dev version 2.44.0; all PR checks passed.
+- #3677 merged as 53c784c2a635b061799e4f7542432a921f548bf9; preview version 2.43.0-preview.20260906; functional PR CI passed, dev-only target policy exception recorded; screenshot added from implementation #3670. Gate had marked promotion draft; explicit ready followed by owner-authorized admin merge completed.
+- #3678 merged as 06ec553630fa2ee51a96b5cbf694089021249194; main version 2.43.0; exact candidate push CI 33974061890 and lifecycle 33976119109 success were merge evidence. Duplicate PR macOS test still running at merge, so no claim that PR rollup was all green. CodeQL residual and owner-directed deferral recorded on PR; no alert was dismissed.
+- Both release refs contain RC af50c6d3451078a7d298b044c08fd2684c9e8eeb. Main tree identical to RC; preview differs only in package.json version.
+- Final release gates: preview CI 33976927260 and service 33976927241; main CI 33976953219 and service 33976953226. Docs deployment 33976953239 accompanies main promotion.
+
+Publication remains pending; these merges alone are not completion.
diff --git a/devlog/_fin/260906_release_243/012_registry_recovery.md b/devlog/_fin/260906_release_243/012_registry_recovery.md
new file mode 100644
index 00000000000..498ba11ce31
--- /dev/null
+++ b/devlog/_fin/260906_release_243/012_registry_recovery.md
@@ -0,0 +1,9 @@
+# Registry processing recovery
+
+Preview Release run 33977377565 passed dispatch guard, dependency audit, source/version/CI/lifecycle gates, changelog, and npm publish. npm signed provenance (transparency log index 2727657111) and returned acceptance for 2.43.0-preview.20260906 at 2026-09-05T16:18:25Z, explicitly saying the package was being processed. The 30-attempt registry smoke ended before processing completed, so workflow conclusion is failure; do not report it as green and do not republish.
+
+Registry metadata subsequently became visible with gitHead 53c784c2a635b061799e4f7542432a921f548bf9. Generated the release notes with the same canonical build-release-changelog.ts (baseline v2.42.0, 15 first-parent commits covered), then completed the skipped GitHub release creation at exactly that commit. Preview flag true, draft false, tag SHA verified. The tarball was delayed further; waited until ordinary canonical download succeeded. npm pack --ignore-scripts fetched all 1033 entries. Package manifest version, CLI bin, src/cli/index.ts, gui/dist/index.html and registry SHA-512 integrity passed.
+
+Stable Release run 33977810259 was dispatched only after exact main push CI 33976953219 and Service lifecycle 33976953226 succeeded at 06ec553630fa2ee51a96b5cbf694089021249194. Stable processing remains pending as of this record; same-registry acceptance plus later readback is the recovery route, not a blind release rerun. Canonical stable notes prebuilt at .tmp/release-01a07240/main-notes.md if its smoke deadline also precedes processing completion.
+
+These operational recoveries preserve all publication gates. A successful package publication and reconciled GitHub metadata are the final evidence, while the timed-out workflow remains honestly recorded as failed.
diff --git a/devlog/_fin/260906_release_243/019_done.md b/devlog/_fin/260906_release_243/019_done.md
new file mode 100644
index 00000000000..5b43aef210f
--- /dev/null
+++ b/devlog/_fin/260906_release_243/019_done.md
@@ -0,0 +1,15 @@
+# DONE — 2.43.0 release train
+
+Both owner-authorized channels are published and downloadable:
+
+- npm latest 2.43.0; main/tag/gitHead 06ec553630fa2ee51a96b5cbf694089021249194; GitHub https://github.com/lidge-jun/opencodex/releases/tag/v2.43.0
+- npm preview 2.43.0-preview.20260906; preview/tag/gitHead 53c784c2a635b061799e4f7542432a921f548bf9; GitHub https://github.com/lidge-jun/opencodex/releases/tag/v2.43.0-preview.20260906
+- dev pre-moved to 2.44.0 at 81871b3fa7034250b8d5ba2cbbfde44e40f0e69c.
+
+Final verifier exited 0: registry dist-tags, both package gitHeads, release draft/prerelease flags, remote tag SHAs, tarball SHA-512 integrity, 1033 packaged files per channel, CLI/source/dashboard entrypoints, and three stable source files byte-matched to the main commit. Required exact push CI and Service lifecycle succeeded for both release SHAs; main docs deployment also succeeded.
+
+Both Release workflows returned failure solely after successful signed npm publication, because registry processing exceeded their five-minute smoke windows. Waited for real registry metadata and downloadable tarballs, rebuilt notes using the unchanged canonical script, and completed the skipped GitHub release creation. No duplicate npm publish and no fake green workflow claim. Stable signed provenance transparency log index 2727757812; preview 2727657111.
+
+Known residuals: pending fixes deferred by owner; CodeQL promotion warnings not dismissed or claimed fixed; full Windows suite remains outside the current shipping gate. Windows install/keyring/service checks passed. Two nonresponsive audit agents were retired; direct release audit recorded honestly.
+
+Original checkout remains dev at ef9c538f36f94f0e95c7f4833642e5b03bd29e2e; pre-existing modified/untracked closeout files untouched. No installed runtime or service configuration was changed. Next work is the separately deferred fixes; no required release work remains.
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 00000000000..c955060918c
--- /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/_fin/260907_init_publication_guidance/010_implementation.md b/devlog/_fin/260907_init_publication_guidance/010_implementation.md
new file mode 100644
index 00000000000..80b59549246
--- /dev/null
+++ b/devlog/_fin/260907_init_publication_guidance/010_implementation.md
@@ -0,0 +1,28 @@
+# Issue #3893: implementation plan
+
+Satisfy-spec work, triggered by issue #3893 and the request to implement separate draft PRs. Goal: actionable first-run publication diagnostics. Non-goals: changing file writes, permissions, replacement/cleanup guarantees, or adding a filesystem fallback. Stop after a verified draft PR; unresolved platform checks are reported, never marked passed. Escalate if resolving the issue requires weakening publication guarantees. This file records the plan and eventual evidence.
+
+Class C2: diagnostic propagation and user documentation. One independent branch from 522ce5f8c; no branch dependencies or orchestration state changes.
+
+File map:
+- MODIFY src/config/initialize.ts: add an optional hardeningFailed flag to constructor options; select a fixed privacy-safe permission diagnostic when hardening throws. Track the flag around the existing harden call only, and pass it in the existing error options. Append supported-location guidance to denied-link diagnostics. All I/O order and cleanup remain identical.
+- MODIFY tests/config/config-mutation-lock.test.ts: inject a harden failure and prove write/link never happen, target remains absent, no residue remains, and raw error details do not appear. Assert all five denied-link codes provide recovery guidance while retaining uncertain-publication state. Partial-write errors must not be mislabeled as permission failures.
+- MODIFY tests/service/init-eof.test.ts: use its existing child bootstrap seam to inject publication errors during the real CLI wizard; verify exit=1, diagnostics and residue warnings, no configuration/backup damage or integration prompts.
+- MODIFY docs-site/src/content/docs/getting-started/quickstart.md and structure/02_config-and-codex-home.md: explain supported locations, inspection before retry, separate permission and link failures, and fresh-install OPENCODEX_HOME examples. Existing translations reviewed for contradictions.
+
+Optional constructor input chain: created by the publication function; consumed by Error.message; no config serialization, migration, or persistent state. Existing constructor calls keep their meaning.
+
+Verification: focused config/init tests read the real publication and CLI code; typecheck includes src; privacy scan; required docs-site build. Baseline focused run: 35 pass, 3 skip, 2 fail (Windows file-symlink privilege: symlinkSync EPERM and dependent missing-residue assertion). No baseline failure will be hidden by changing tests. New regression checks must pass. Windows-native filesystem support remains bounded by the host.
+
+Audit: direct O_EXCL and replacement fallbacks rejected because they change complete-file/no-replace guarantees. Reuse the existing error and test seams; no new diagnostic module. Guidance never prints raw cause text or candidate bytes.
+
+## Verification before draft publication
+
+- `bun install --frozen-lockfile`: passed; lockfile unchanged.
+- New diagnostics were observed failing before implementation: 9 failures across the focused hardening/link/CLI fault cases. After implementation: 9 passed.
+- `bun test tests/config/config-mutation-lock.test.ts tests/service/init-eof.test.ts`: 38 passed, 3 skipped, 2 failed. The same two tests failed on unchanged 522ce5f8c: file-symlink creation is denied on this Windows host, and the swapped-symlink test then lacks its expected residue. New recovery tests pass; no skips or weakened assertions were added.
+- `bun run typecheck`: passed.
+- `bun run privacy:scan`: passed.
+- `cd docs-site; bun install --frozen-lockfile; bun run build`: passed, 425 pages. Translated quickstarts contain no conflicting recovery/fallback instructions.
+- CLI fault scenarios verify exit=1, distinct permission/link messages, uncertain-publication/residue warnings, backup preservation and no integration prompts. Partial-write errors keep the generic diagnostic.
+- No physical non-NTFS filesystem support is claimed. Maintainer review remains required; this is a draft handoff.
diff --git a/devlog/_fin/260907_release_246/000_plan.md b/devlog/_fin/260907_release_246/000_plan.md
new file mode 100644
index 00000000000..405a2e44ccb
--- /dev/null
+++ b/devlog/_fin/260907_release_246/000_plan.md
@@ -0,0 +1,11 @@
+# Release 2.46.0 plan
+
+Single-cycle satisfy-spec release operation, authorized by the owner to inspect readiness, promote main/preview and publish. First produce this roadmap; no product implementation work is planned. Goal: publish the integrated RC in preview and stable with immutable evidence. Source RC: 0d8b0cd1e3d10bc6b85bfefb3d68555f558407b0. Published baseline v2.45.0. Existing checkout is dirty and remains unchanged except this new unit and ignored evidence/state. Dedicated worktree: /private/tmp/ocx-release-246-01a078cd.
+
+Scope: only version metadata and release branch integration through PRs; hosted tests, registry packaging and release metadata. Excludes unrelated open PRs, default login policy changes, installed-service upgrades and account settings. No new field/enum or enforcement layer is introduced. Existing release gates remain authoritative; administrator capability is not CI or review evidence.
+
+Verification: GitHub source/PR/readiness inspection, independent source audit, candidate CI, exact final release-branch push CI and applicable lifecycle, registry gitHead/integrity/provenance, immutable tag/release and package smoke. Existing successful CI 34071673682 observes 44c69fdd, not the RC (44-file delta). Future CI is NOT RUN until receipts exist. User has specified no cost/time budget; use existing shell/GitHub/npm tools and bounded waits. Record credentials only by auth mechanism, never value.
+
+Terminal DONE: preview and stable verified and dev ahead; NOOP: already delivered identical candidate; blocked/unsafe: concrete external prerequisite or failed gate without a safe remedy. A failure is repaired or remains a blocker, never weakened. Only new product decisions/out-of-scope changes require owner direction. Plan/evidence artifact is this unit plus .tmp/release-246; stop only after required outputs or genuine prerequisite failure. Source-of-truth: MAINTAINERS.md and release.yml unchanged; record outcome in 090_delivery.md.
+
+One PABCD cycle has dependent operational steps in 010_release.md. These steps are not separate product implementation phases.
diff --git a/devlog/_fin/260907_release_246/010_release.md b/devlog/_fin/260907_release_246/010_release.md
new file mode 100644
index 00000000000..fbbeabd1f46
--- /dev/null
+++ b/devlog/_fin/260907_release_246/010_release.md
@@ -0,0 +1,9 @@
+# Release operation
+
+1. Verify source RC and previous published tags. Read merged review state and policy. Run candidate CI on dev; inspect exact SHA and jobs. Separate outstanding unrelated PRs from candidate blockers.
+2. MODIFY package.json only on a dev bump branch using scripts/bump-dev-version.ts: 2.46.0 -> 2.47.0. Verify unused tags, version-line tests, typecheck/full tests as appropriate. Push branch, template PR to dev and integrate after checks; retain frozen RC for release.
+3. NEW independent promotion branches based on existing origin/preview and origin/main. Merge frozen RC into each; resolve only channel-version conflicts. MODIFY preview package.json to 2.46.0-preview.20260907; main package.json to 2.46.0. Runtime tree must match the frozen RC, with explicit version-only/channel lineage differences. Publish template promotion PRs and verify head/base/native membership/review/CI before merge. Record owner-authorized promotion decision, never self-approval.
+4. Require each merged SHA's own successful push-event Cross-platform CI and lifecycle. Validate current branch tips, dev 2.47.0, package name/version and unused target tags. Invoke existing release.yml dry-run (build/pack), then actual preview publication, then stable, serialized. expected-sha must equal branch tip.
+5. Inspect npm dist-tags, version gitHead, SHA512 tarball integrity, provenance and GitHub tag/release; run safe published-package version/help smoke in isolated home. No installed service changes. Capture final branch ancestry and preserve initial dirty files.
+
+Activation scenarios: moved branch -> refuse dispatch and repin/revalidate; wrong package/tag mapping -> reject; failed CI -> inspect and repair or rerun substantiated transient failure; post-publish smoke failure -> inspect registry before retry, finish missing GitHub metadata only after publication proof. Existing rollback artifact v2.45.0 remains published; no destructive rollback is planned.
diff --git a/devlog/_fin/260907_release_246/011_audit.md b/devlog/_fin/260907_release_246/011_audit.md
new file mode 100644
index 00000000000..76c0667b665
--- /dev/null
+++ b/devlog/_fin/260907_release_246/011_audit.md
@@ -0,0 +1,3 @@
+# Independent release audit
+
+Averroes: VERDICT: PASS. No verified source/security blocker. Checked 20 delivery PRs: zero unresolved review threads or CHANGES_REQUESTED. The prior successful CI 34074350604 (26 jobs) and lifecycle 34074351720 (3 jobs) tested 9470fdb1 whose tree equals runtime merge 5fdf9bbdd. RC 0d8b0cd1 differs only in closeout documents. New RC workflow 34079952328 is pending. Login-required default is preserved by codexDesktopAuthless === true; #3689 closed unmerged. Promotion repinning must retain frozen source RC. Final branch CI/lifecycle and artifact gates remain mandatory.
diff --git a/devlog/_fin/260907_release_246/020_progress.md b/devlog/_fin/260907_release_246/020_progress.md
new file mode 100644
index 00000000000..522a92fc386
--- /dev/null
+++ b/devlog/_fin/260907_release_246/020_progress.md
@@ -0,0 +1,9 @@
+# Release progress
+
+Frozen candidate 0d8b0cd1 passed all 26 jobs in CI 34079952328. Local typecheck, privacy and 21,112 tests passed (16 skipped, zero failures). Twenty merged delivery PRs had zero unresolved threads.
+
+Dev pre-move #3850 merged as 6cf38b59 (2.47.0); PR CI/lifecycle succeeded and post-merge CI 34081097509 succeeded. Main #3851 merged as bba63222; exact tree equals candidate, lifecycle 34081245230 and docs deployment 34081245209 succeeded, push CI 34081245213 pending.
+
+Preview #3852 head 6ccfe7ed differs only in package version. Attempt 1 CI 34080243039 macos 2/2 stopped after client-connect transaction fixture and hit the 20-minute job bound; runner log retained in .tmp/release-246/preview-macos-attempt1.log. File and src/cli/connect.ts unchanged versus v2.45.0; all seven transaction cases passed in the same-candidate local suite. Only unsuccessful jobs rerun once unchanged, attempt 2. Root cause not established and no limits/assertions changed.
+
+A new P2 promotion review noted legacy mixed sig/red streaming versus JSON ordering inconsistency. Independent re-review confirmed it is introduced in newly supported legacy preservation, not a regression of functioning v2.45 replay; current bridge produces separate items. Existing axis-three scope explicitly deferred this shape. Accepted limitation tracked under open #3719; disposition https://github.com/lidge-jun/opencodex/pull/3852#discussion_r3946450143. Thread resolution represents explicit deferral, not a fix. No universal reasoning-replay claim.
diff --git a/devlog/_fin/260907_release_246/090_delivery.md b/devlog/_fin/260907_release_246/090_delivery.md
new file mode 100644
index 00000000000..213b476614f
--- /dev/null
+++ b/devlog/_fin/260907_release_246/090_delivery.md
@@ -0,0 +1,31 @@
+# OpenCodex 2.46.0 release delivery
+
+Outcome: DONE. Owner requested readiness inspection, main/preview promotion and deployment. Source frozen at 0d8b0cd1e3d10bc6b85bfefb3d68555f558407b0; previous stable v2.45.0. No additional product patches were made during release.
+
+## Published artifacts
+
+| Channel | Version | Exact SHA | Promotion |
+| --- | --- | --- | --- |
+| stable/latest | 2.46.0 | bba63222d3eeb5c8e397edae35798225e4fa1a6f | #3851 |
+| preview | 2.46.0-preview.20260907 | 9ef2aaf3f02ace0778b05e2112d944db61c1a06d | #3852 |
+
+Dev advanced to 2.47.0 through #3850 (6cf38b59). Both release branches contain the frozen source; main tree exactly matches it and preview differs only in package version. Login remains required by default; authless needs explicit opt-in.
+
+## Verification
+
+- Frozen full-platform CI 34079952328 passed all 26 jobs, including Windows 6 shards and macOS control. Local typecheck, privacy and 21,112 tests passed, 16 skipped, zero failures.
+- Main pushCI 34081245213 and lifecycle 34081245230 passed on bba63222. Preview pushCI 34082147716 and lifecycle 34082147733 passed on 9ef2aaf3. Main docs deploy 34081245209 passed.
+- Both release dry-runs passed: main 34081837842, preview 34082893066.
+- Both registry artifacts contain 1,067 files. SHA512 integrity, npm registry cryptographic signatures, SLSA provenance subject/source matching, CLI --version/--help all passed. Provenance payload matching is recorded separately from registry signature verification; no independent Sigstore certificate-chain validation is claimed.
+- npm latest=2.46.0 and preview=2.46.0-preview.20260907; immutable GitHub tags/releases match their npm gitHead. Final live verifier PASS recorded in .codexclaw/evidence/01a078cd-8133-7c33-b020-d5b17a9b3a04/test-receipt.json.
+- All 25 initial dirty files retain their original SHA256. Shared checkout identity unchanged; no installed proxy/service/account changes.
+
+## Recovery and limits
+
+Preview PR CI 34080243039 attempt 1 macOS 2/2 stalled in unchanged client-connect tests and hit 20 minutes. Only unsuccessful jobs reran unchanged; attempt 2 passed. No stall root cause or timeout fix is claimed.
+
+Publication runs 34083011934 (preview) and 34083607269 (stable) both completed npm publishing with signed provenance, but failed only the 5-minute post-publish registry smoke while npm processed the packages. Later registry evidence proved successful publication. Skipped GitHub releases were created at the exact published commits with the repository changelog builder. No package was republished and these workflow runs are not described as green.
+
+Independent source and plan audits passed. The 20 delivered feature PRs had no unresolved review threads. Late promotion comments were explicitly dispositioned, not silently counted as fixed: legacy mixed-envelope streaming/JSON ordering remains under #3719; display-name unknown-receipt recovery is a reversible label-only P2 follow-up; Raycast unsupported-platform messaging, CLI text-test coverage, historical plan formatting and locale documentation are nonblocking follow-ups. Each rationale is recorded on #3851/#3852; final unresolved count 0. Release notes retain the functional limitations. No new release-blocking defect was established.
+
+Evidence: .tmp/release-246/state.json, run-*.json, artifact-*/verification.json, initial-dirty.json, promotion-reviews.json, postmerge-review-dispositions.json, review-disposition-verification.json. The rollback baseline v2.45.0 remains published at b0900e556; no rollback was performed. No remaining work within the authorized release scope.
diff --git a/devlog/_fin/260908_a_stack_responses_compat/000_plan.md b/devlog/_fin/260908_a_stack_responses_compat/000_plan.md
new file mode 100644
index 00000000000..3a22b2d98f1
--- /dev/null
+++ b/devlog/_fin/260908_a_stack_responses_compat/000_plan.md
@@ -0,0 +1,103 @@
+# 000 — a_stack_responses_compat: Plan
+
+## Objective
+
+Land four Responses-compatibility changes on `dev` as one manual dependent branch
+chain whose tip carries all of them, so a single CI run certifies the whole set.
+Three layers carry existing contributor pull requests; one is new work for an
+issue that has no pull request.
+
+| Layer | Source | Author to preserve | Subject |
+|---|---|---|---|
+| 1 | PR #3906, commit `11c498b6c` | MohamadSabree8 | Muse Spark Contributor Free tiers keep unsupported `web_search` fields |
+| 2 | PR #3886, commit `83c1d9b12` | cb8010d6 | Spark streams end `adapter_eof` when the Responses Lite header is present |
+| 3 | Issue #3922, new work | — | Claude optional tool parameters become strict on Responses routes |
+| 4 | PR #3917, commit `2430724e5` | mashfromband | Routed destinations reject Codex `agent_message` with 422 |
+
+Evidence base: four read-only `gpt-6-astra` explorer lanes read the current tree at
+`2abf071e0` and returned quoted `path:line` anchors, reproduced in each phase doc.
+
+## Loop-spec
+
+- Loop archetype: satisfy-spec. Each layer has a stated correct behavior; there is
+ no metric to optimize.
+- Trigger: maintainer request to execute workstream A as a stack.
+- Goal: the tip merged into `dev`, children settled with authors preserved, linked
+ issues closed.
+- Non-goals: registry `modelWireDefaults` for the `-free` ids; setting the Lite
+ header to `"false"` instead of removing it; PR #3838's tool-promotion,
+ `customToolWireName` export and `statelessResponses` work; any other open PR;
+ `main`/`preview` promotion.
+- Verifier: the single Cross-platform CI run on the tip pull request's head SHA.
+ It runs the repository's own workflow over the cumulative tree, so it observes
+ every file changed by all four layers.
+- Write scope: `src/adapters/openai-responses.ts`, `src/adapters/opencode-go.ts` ->
+ `src/adapters/routed-agent-messages.ts`, `src/claude/inbound-content-options.ts`,
+ their regressions, the two test-layout registries,
+ `docs-site/src/content/docs/reference/adapters.md`,
+ `docs-site/src/content/docs/reference/configuration/providers.md`, and this unit.
+- Budget: no local suite runs at all (instructed). Wall-clock bound is the CI run
+ plus merge; a red tip after one bounded repair attempt is BLOCKED, not DONE.
+- Stop condition: the stack landed on `dev`, proven for the merge method actually
+ used (see 050): original-tip ancestry for a merge commit, or landed-commit
+ ancestry plus per-path content equality for squash and rebase.
+- Memory artifact: this unit, plus the goalplan at
+ `.codexclaw/goalplans/deliver-opencodex-workstream-a-responses-compati/`.
+- Escalation: a finding that changes a carried author's intended behavior, or a
+ provider that rejects an explicit `strict: false`, returns to the maintainer.
+
+## Constraints (from the requesting maintainer)
+
+- No local product suite, typecheck, build or install runs in this session. Every
+ such check is recorded `NOT RUN`.
+- Every push uses `--no-verify`.
+- CI triggers on the stack tip only. When that one run is green, the tip merges
+ into `dev`; the remaining pull requests are then settled and the issues closed.
+- Carried work keeps its original author through a `Co-authored-by` trailer.
+
+## Why tip-only CI is achievable
+
+`.github/workflows/ci.yml` declares `pull_request: {}` with no base filter, and
+`push: branches: [main, preview, dev]`. Pushing `codex/a-stack-l1..l3` starts no
+workflow: those refs are not integration branches and no pull request points at
+them. Opening exactly one pull request, for layer 4 against `dev`, produces
+exactly one Cross-platform CI run whose head contains all four layers.
+
+## Base and chain
+
+Base: `origin/dev` = `942c028735d39b2ad410b1baa95670984e16576d`.
+
+```
+codex/a-stack-l4-routed-agentmsg (tip, the only pull request) -> base dev
+codex/a-stack-l3-claude-strict
+codex/a-stack-l2-spark-lite
+codex/a-stack-l1-muse-free
+origin/dev 942c02873
+```
+
+An ordinary dependent branch chain. GitHub native stacks are not used and were
+not requested.
+
+## Work-phase map (one phase = one full PABCD cycle)
+
+| WP | Doc | Slice | Depends on |
+|----|-----|-------|------------|
+| wp0 | 000 | This roadmap (docs only) | — |
+| wp1 | 010 | Layer 1, carry #3906 | wp0 |
+| wp2 | 020 | Layer 2, carry #3886 | wp1 |
+| wp3 | 030 | Layer 3, implement #3922 | wp2 |
+| wp4 | 040 | Layer 4, carry #3917 | wp3 |
+| wp5 | 050 | Publish, one CI run, merge, settle | wp4 |
+
+Ordering follows textual adjacency in `src/adapters/openai-responses.ts`, which
+layers 1, 2 and 4 all touch at distinct hunks (≈2125, ≈2503, ≈2366 and its import).
+Layer 3 touches `src/claude/inbound-content-options.ts` only.
+
+## Terminal outcomes
+
+- DONE — the stack landed on `dev` with the merge-method-specific proof recorded,
+ children settled with authors preserved, issues #3885/#3922/#3911 closed.
+- BLOCKED — CI red on the tip after a bounded repair attempt, or an unmet merge
+ requirement.
+- NEEDS_HUMAN — an audit finding that would change a carried author's intended
+ behavior beyond what the issue asks.
diff --git a/devlog/_fin/260908_a_stack_responses_compat/010_phase1.md b/devlog/_fin/260908_a_stack_responses_compat/010_phase1.md
new file mode 100644
index 00000000000..85f9e11d3dc
--- /dev/null
+++ b/devlog/_fin/260908_a_stack_responses_compat/010_phase1.md
@@ -0,0 +1,62 @@
+# 010 — Phase 1: carry PR #3906 (Muse Spark Contributor Free web_search strip)
+
+Branch `codex/a-stack-l1-muse-free`, based on `origin/dev` `942c02873`.
+Carried commit `11c498b6c62ae9f7c5b0d25ca24fc6612f607a5c` by MohamadSabree8.
+
+## Problem
+
+`stripMuseSparkUnsupportedWebSearchFields` removes `search_content_types` and
+`indexed_web_access` from a Codex `web_search` tool before it reaches the Zen
+Responses wire, because that gateway 400s on them. The model guard only lists the
+two paid contributor ids, so the two Contributor Free ids ride the same wire and
+same gateway contract but keep the rejected fields.
+
+## MODIFY map
+
+`src/adapters/openai-responses.ts` — the constant at 2125-2128.
+
+Before:
+
+```ts
+const MUSE_SPARK_WEB_SEARCH_STRICT_MODELS = new Set([
+ "muse-spark-1.3-contributor",
+ "muse-spark-1.2-contributor",
+]);
+```
+
+After:
+
+```ts
+const MUSE_SPARK_WEB_SEARCH_STRICT_MODELS = new Set([
+ "muse-spark-1.3-contributor",
+ "muse-spark-1.3-contributor-free",
+ "muse-spark-1.2-contributor",
+ "muse-spark-1.2-contributor-free",
+]);
+```
+
+Nothing else changes. The consumer at 2148, its model guard at 2155
+(`if (!MUSE_SPARK_WEB_SEARCH_STRICT_MODELS.has(modelId.trim().toLowerCase())) return body;`),
+the destination guard at 2159-2164 and the call site at 2451 are untouched.
+
+## TESTS
+
+`tests/providers/muse-spark-web-search-compat.test.ts` — add free-tier cases that
+mirror the paid-tier assertions already in the file:
+
+- top-level `tools`: type stays `web_search`, `search_context_size` preserved,
+ `search_content_types` and `indexed_web_access` absent (mirrors 81-87, 131-137);
+- nested `input[].additional_tools.tools`: same removal (mirrors 106-114, 150-158);
+- `web_search_preview` untouched for the free ids (mirrors 90-97, 140-147).
+
+## Known limit (recorded, not fixed here)
+
+`src/providers/registry.ts:1685-1690` maps only the paid ids in
+`modelWireDefaults`, so the `-free` ids do not select the Responses wire
+automatically; this fix applies when that wire is chosen explicitly. Changing the
+registry is out of scope, matching the carried pull request.
+
+## Verification (C)
+
+No local command. The layer is verified by the single tip CI run described in 050.
+Local suites: NOT RUN by instruction.
diff --git a/devlog/_fin/260908_a_stack_responses_compat/020_phase2.md b/devlog/_fin/260908_a_stack_responses_compat/020_phase2.md
new file mode 100644
index 00000000000..13b4cc949cf
--- /dev/null
+++ b/devlog/_fin/260908_a_stack_responses_compat/020_phase2.md
@@ -0,0 +1,73 @@
+# 020 — Phase 2: carry PR #3886 (disable Responses Lite transport for Spark)
+
+Branch `codex/a-stack-l2-spark-lite`, based on layer 1.
+Carried commit `83c1d9b129b80d4f65a797fd61a2026deb8c8123` by cb8010d6.
+
+## Problem
+
+Issue #3885: with `x-openai-internal-codex-responses-lite: true`, the canonical
+backend opens a `gpt-5.3-codex-spark` SSE response and closes it before a terminal
+event, which the adapter reports as `response.incomplete` / `adapter_eof`. The same
+request without that header completes.
+
+## MODIFY map
+
+`src/adapters/openai-responses.ts` — inside the canonical-forward block, before
+the existing routing-hint work. Line numbers here are against the pinned base
+`942c02873` (block at 2503-2513); layer 1 adds two lines above it, so on this
+branch the block sits at 2505-2515.
+
+After:
+
+```ts
+ if (isCanonicalOpenAiForwardProvider(provider)) {
+ // Spark closes Responses Lite streams before a terminal completion. Select compatibility
+ // from the final wire model so aliases cannot leave the caller or a static header enabled.
+ if (isPlainObject(finalBody) && finalBody.model === "gpt-5.3-codex-spark") {
+ for (const name of Object.keys(headers)) {
+ if (name.toLowerCase() === CODEX_RESPONSES_LITE_HEADER) delete headers[name];
+ }
+ }
+ const routingHeaders = new Headers(headers);
+ applyCodexRoutingHint(routingHeaders, finalBody);
+```
+
+`finalBody` is computed at 2494-2502 and serialized at 2523 on the pinned base
+(2496-2504 and 2525 on this branch), so it is the actual wire model.
+`parsed.modelId` can differ; the existing test at 187-188 pins that distinction
+deliberately. Keying on `finalBody.model` therefore also covers aliases. The loop
+removes every case spelling, which matters because static provider headers merge
+in at 2315 and 2353 on the pinned base (2317 and 2355 here) with arbitrary casing.
+
+## Scope of the fix, and what it does not cover (audit finding 1)
+
+The independent audit established a real boundary, verified against source:
+
+- **Covered.** Removing the header fixes the reported defect on the HTTP header
+ replay path, including the HTTP fallback: `prepareCodexHttpInit()` recomputes
+ only the routing hint (`src/server/responses/codex-ws-request.ts:46-52`) and
+ `httpInit` carries the header-deleted request forward (`:68`).
+- **Not covered.** On the WebSocket path, `codex-ws-request.ts:30-33` writes
+ `client_metadata[CODEX_RESPONSES_LITE_METADATA_KEY]` only when the header is
+ present and reads `"true"`/`"false"`. Deleting the header leaves any
+ pre-existing `client_metadata` Lite value in the body untouched, and that value
+ reaches the frame at `:71` and the pool reuse key at
+ `codex-ws-pool.ts:53-55`.
+
+Setting the header to `"false"` instead of deleting it would also cover the WS
+case, but that expands the carried author's diff beyond issue #3885 and changes
+socket reuse identity. This phase carries the author's delete-only form and
+records the WS metadata case as unresolved rather than silently expanding scope.
+The tip pull request states this limit explicitly so the residual is visible.
+
+## TESTS
+
+`tests/codex-integration/codex-metadata-integrity.test.ts`, beside the mixed-case
+test at 171: Spark wire model with a caller-provided Lite header, with a
+mixed-case static header, and with `parsed.modelId` set to an alias while the
+serialized model is Spark; `gpt-5.6-sol` keeps the header. Existing guards at 185,
+211, 225 and 267 stay intact.
+
+## Verification (C)
+
+No local command. Verified by the single tip CI run in 050. Local suites: NOT RUN.
diff --git a/devlog/_fin/260908_a_stack_responses_compat/030_phase3.md b/devlog/_fin/260908_a_stack_responses_compat/030_phase3.md
new file mode 100644
index 00000000000..c0e6a00b3f7
--- /dev/null
+++ b/devlog/_fin/260908_a_stack_responses_compat/030_phase3.md
@@ -0,0 +1,141 @@
+# 030 — Phase 3: fix issue #3922 (Claude tool strict default on Responses routes)
+
+Branch `codex/a-stack-l3-claude-strict`, based on layer 2. New work; no existing
+pull request. This is the only layer authored here.
+
+## Problem
+
+When Claude Code sends a custom tool without an explicit `strict`, the
+Messages -> Responses translation emits a function tool that also omits `strict`.
+The Responses API treats an omitted `strict` as an attempt to normalize the schema
+into strict mode, so parameters that the Anthropic `input_schema` marks optional
+are treated as required upstream, and a tool call that omits them fails. Anthropic
+requires an explicit `strict: true` to opt in, so the two defaults disagree.
+
+## MODIFY map
+
+`src/claude/inbound-content-options.ts` — `toolsToResponses`, the function-tool
+branch at 26-33.
+
+Before:
+
+```ts
+ if (typeof raw.name === "string" && raw.name.length > 0 && isRec(raw.input_schema)) {
+ out.push({
+ type: "function",
+ name: raw.name,
+ ...(typeof raw.description === "string" ? { description: raw.description } : {}),
+ parameters: raw.input_schema as Record,
+ });
+ continue;
+ }
+```
+
+After:
+
+```ts
+ if (typeof raw.name === "string" && raw.name.length > 0 && isRec(raw.input_schema)) {
+ out.push({
+ type: "function",
+ name: raw.name,
+ ...(typeof raw.description === "string" ? { description: raw.description } : {}),
+ parameters: raw.input_schema as Record,
+ // Anthropic opts into strict tool use explicitly, while Responses normalizes
+ // an omitted strict into strict mode. Carry the source intent rather than the
+ // destination default, so an optional input_schema parameter stays optional.
+ strict: typeof raw.strict === "boolean" ? raw.strict : false,
+ });
+ continue;
+ }
+```
+
+The value is derived from the source tool rather than hardcoded, so an explicit
+`strict: true` or `strict: false` from the client survives translation, which is
+what issue #3922 asks for.
+
+## How the existing strict-tool admission policy relates (audit finding 2)
+
+An earlier draft of this document described `src/claude/compatibility.ts:102`
+backwards. What that line actually does:
+
+```ts
+if (tool.strict === true) codes.add("strict_tools");
+```
+
+`strict_tools` is listed as an incompatible feature at `compatibility.ts:16`, and
+`analyzeClaudeCompatibility` (`compatibility.ts:179`) returns `"reject"` for an
+incompatible request under enforce mode (decision expression at `:189`), which
+`src/server/claude-messages.ts:733-740` applies **before**
+translation. So an explicit `strict: true` is already refused in enforce mode and
+only reaches translation on the default and shadow paths.
+
+This phase does not change that policy. Detection reads the **source** Anthropic
+tool before translation, so emitting a default `strict: false` downstream adds no
+new rejection; `tests/claude-integration/claude-compatibility.test.ts:73-78`
+already covers the explicit-false allow case, and `:24` lists strict tools among
+rejected features.
+
+## Propagation (independently traced twice, no further change needed)
+
+- `src/claude/inbound-content-options.ts:26-32` builds the tool.
+- `src/claude/inbound.ts:350-351` assigns it to `body.tools`.
+- `src/server/claude-messages.ts:875,897` serializes that body into the internal
+ Responses request; `src/server/responses/core.ts:3160` parses it.
+- `src/responses/schema.ts:114` accepts `strict`; `parser-tools.ts:63` preserves an
+ explicit value including `false`.
+- `src/responses/parser.ts:570` keeps `_rawBody`, and
+ `src/adapters/openai-responses.ts:2362-2364` starts from it. Canonical-field
+ stripping only removes `external_web_access`/`defer_loading` (`:189-200`) and
+ schema normalization spreads the tool (`{ ...tool, parameters }`, `:648-658`),
+ so `strict` reaches `JSON.stringify(finalBody)` at `:2523`.
+
+## Boundaries
+
+- Hosted `web_search` leaves the function at 22-24, before this branch, so it gains
+ no `strict` field.
+- Native Anthropic passthrough never reaches translation:
+ `src/server/claude-messages.ts:721-722` returns from `anthropicNativePassthrough`
+ before the translation call at 757.
+- Other Anthropic server tools still drop at 35.
+
+## Schema promise, stated precisely (audit finding 4)
+
+At translation, `parameters` is the caller's `input_schema` reference
+(`inbound-content-options.ts:31`), so `properties`, `required` and nested schemas
+are unchanged there. That is not a promise of a byte-identical schema on every
+outbound route: `openai-responses.ts:651` runs `normalizeXaiToolParameters`, `:657`
+supplies a root `type: "object"` when absent, `responses-code-mode.ts:23-27` can
+rewrite an `exec` parameter description, and Azure Chat sanitizes at
+`openai-chat.ts:1359-1361`. The regression asserts an unchanged schema through the
+ordinary OpenAI Responses route.
+
+Adding the field also shifts fallback cache-cohort hashes, because translated tool
+definitions participate in the hash at `src/claude/inbound.ts:386-392`. That is a
+cohort change, not a correctness change.
+
+## Known risk
+
+The same translated tools feed translated Chat Completions routes, where
+`openai-chat.ts:1343` forwards an explicit `strict`; Azure deletes it at `:1364`,
+and `tests/providers/azure-model-router-tool-schema.test.ts:42` already pins that
+absence. No repository-declared rejection of an explicit `strict: false` was found,
+but universal upstream acceptance is not proven. A provider rejecting it is an
+escalation.
+
+## TESTS
+
+`tests/claude-integration/claude-inbound.test.ts`:
+
+- **Update the existing assertion at 80-83.** It is an exact `toEqual` on the
+ translated `Read` tool and will fail once `strict` is present; the expected
+ object gains `strict: false`.
+- Keep `expect(tools[1]).toEqual({ type: "web_search" })` at 84 unchanged.
+- New cases: omitted `strict` -> `false`; explicit `false` -> preserved; explicit
+ `true` -> preserved; an `input_schema` with one required and one optional
+ property keeps its `required` array through `parseRequest`.
+- Assert the three values on the **serialized adapter output**, not only the
+ translator return, so the wire body is what is pinned.
+
+## Verification (C)
+
+No local command. Verified by the single tip CI run in 050. Local suites: NOT RUN.
diff --git a/devlog/_fin/260908_a_stack_responses_compat/040_phase4.md b/devlog/_fin/260908_a_stack_responses_compat/040_phase4.md
new file mode 100644
index 00000000000..b5a0f94be3c
--- /dev/null
+++ b/devlog/_fin/260908_a_stack_responses_compat/040_phase4.md
@@ -0,0 +1,121 @@
+# 040 — Phase 4: carry PR #3917 (routed agent_message conversion), stack tip
+
+Branch `codex/a-stack-l4-routed-agentmsg`, based on layer 3. This branch is the
+stack tip and the only one with a pull request.
+Carried commit `2430724e57e0950bde4b006c0175a2d5c70a0baf` by mashfromband.
+
+## Problem
+
+Codex writes every sub-agent reply into the rollout as an `agent_message` input
+item, which is private to the ChatGPT Codex schema, so it is replayed in the input
+of every later turn of that thread. The routed Responses destinations reported in
+#3911 and #3907 reject the whole body with
+`422 unknown item type "agent_message"`. 422 is a client error, so nothing fails
+over and the thread stays broken. The plaintext conversion already existed but was
+scoped to the OpenCode Go destination, and nothing in those reports is specific to
+that destination.
+
+## MODIFY / RENAME map
+
+1. `src/adapters/opencode-go.ts` -> `src/adapters/routed-agent-messages.ts`.
+ `isOpenCodeGo` is deleted; its only production consumer is the call site below.
+ `normalizeOpenCodeGoAgentMessages` becomes `normalizeRoutedAgentMessages` with
+ the algorithm unchanged, including the fail-closed check that every content part
+ is `input_text`/`input_image`/`input_file`.
+
+2. `src/adapters/openai-responses.ts:1` and `:2366`.
+
+ Before:
+
+ ```ts
+ import { isOpenCodeGo, normalizeOpenCodeGoAgentMessages } from "./opencode-go";
+ ...
+ if (!forward && isOpenCodeGo(provider.baseUrl)) outBody = normalizeOpenCodeGoAgentMessages(outBody);
+ ```
+
+ After:
+
+ ```ts
+ import { normalizeRoutedAgentMessages } from "./routed-agent-messages";
+ ...
+ if (!forward) outBody = normalizeRoutedAgentMessages(outBody);
+ ```
+
+ `forward` is `provider.authMode === "forward"` (2356). All forward destinations
+ retain the existing behavior and keep the item unchanged.
+
+3. `tests/providers/opencode-go-agent-messages.test.ts` ->
+ `tests/adapters/routed-agent-messages.test.ts`, with the two Go-specific
+ expectations (54, 120-122) changed from `agent_message` to the converted
+ `message`/`user` shape for arbitrary routed URLs.
+
+4. `tests/responses/responses-opaque-blob-recovery.test.ts` — the four assertions
+ at 553, 583, 611 and 754. That fixture is `authMode: "key"` (163-164), so its
+ retried item is now converted. Opaque-blob recovery repairs an undecryptable part
+ into `[encrypted content omitted]`, which leaves the item fully plaintext; on a
+ routed retry it is converted too, which is what lets the retry be accepted.
+ Expected object becomes:
+
+ ```ts
+ {
+ type: "message",
+ role: "user",
+ content: [
+ { type: "input_text", text: 'Agent message {"author":"/root/child_task","recipient":"/root"}' },
+ { type: "input_text", text: "Message Type: MESSAGE\nTask name: /root\nSender: /root/child_task\nPayload:" },
+ { type: "input_text", text: "[encrypted content omitted]" },
+ ],
+ }
+ ```
+
+ The `authMode: "forward"` case in the same file is untouched.
+
+5. Both test-layout registries, because the test basename and directory change:
+ `scripts/test-layout/layout.json:917` and
+ `tests/fixtures/test-layout-expected.json:752` drop
+ `"opencode-go-agent-messages.test.ts": "providers"` and gain
+ `"routed-agent-messages.test.ts": "adapters"`.
+ `tests/test-layout-tooling.test.ts:250` compares the two tables exactly, so
+ missing either one fails.
+
+6. `docs-site/src/content/docs/reference/adapters.md` and
+ `docs-site/src/content/docs/reference/configuration/providers.md`, whose wording
+ describes the conversion as Go-specific, as in the carried pull request. The
+ carried text's universal "any routed destination" phrasing is narrowed to the
+ observed non-forward destinations rather than copied unchanged.
+
+## Preserved behavior
+
+Forward destinations, ciphertext and unknown part types (the `every` guard),
+replay immutability and no-op reference identity, the identity prefix text, and
+the session-header assertion at 55 of the moved test.
+
+## PR #3838 boundary
+
+#3838 stays open and independent. Its `normalizeOpenCodeGoAdditionalTools`
+promotion, `customToolWireName` export and `statelessResponses` registry flag are
+unrelated to this conversion. Its mixed-content policy drops ciphertext and unknown
+parts whenever plaintext survives, which contradicts the fail-closed retention kept
+here; it is not carried.
+
+## Verification (C)
+
+No local command. Verified by the single tip CI run in 050. Local suites: NOT RUN.
+
+
+## Which destinations actually change (audit finding 5)
+
+`src/types/provider.ts:449` declares `authMode?: "key" | "forward" | "oauth" | "local"`.
+Because the new gate is `!forward` and `forward` is `provider.authMode === "forward"`
+(`openai-responses.ts:2356`), the conversion now applies to **key, oauth, local and
+undefined** whenever this adapter is selected. Every forward destination is
+unchanged, including noncanonical forward gateways; the built-in ChatGPT
+destination is forward (`src/providers/registry.ts:1200-1204`), so its native items
+stay intact.
+
+No repository-declared non-forward destination requires plaintext `agent_message`
+preservation. Authentication mode alone cannot prove what an arbitrary custom
+upstream accepts, so the carried claim that *every* routed destination rejects the
+item is stated here as the observed pattern rather than a proven universal.
+Regression coverage adds a non-forward mode beyond the carried key/forward
+fixtures.
diff --git a/devlog/_fin/260908_a_stack_responses_compat/050_phase5.md b/devlog/_fin/260908_a_stack_responses_compat/050_phase5.md
new file mode 100644
index 00000000000..97b928d92ca
--- /dev/null
+++ b/devlog/_fin/260908_a_stack_responses_compat/050_phase5.md
@@ -0,0 +1,95 @@
+# 050 — Phase 5: publish, single CI run, merge, settle
+
+## Publication
+
+Push all four branches with `--no-verify`, in chain order:
+
+```
+git push --no-verify origin codex/a-stack-l1-muse-free
+git push --no-verify origin codex/a-stack-l2-spark-lite
+git push --no-verify origin codex/a-stack-l3-claude-strict
+git push --no-verify origin codex/a-stack-l4-routed-agentmsg
+```
+
+Pushing l1-l3 starts no workflow: `.github/workflows/ci.yml` limits its `push`
+trigger to `[main, preview, dev]`, and no pull request points at those refs.
+
+## The single pull request
+
+Open exactly one pull request: `codex/a-stack-l4-routed-agentmsg` -> `dev`. Its
+head contains all four layers, so the one Cross-platform CI run it starts is
+cumulative evidence for the whole stack. The description follows
+`.github/PULL_REQUEST_TEMPLATE.md` (Summary, Verification, Checklist), states that
+local suites were NOT RUN by maintainer instruction with CI as the verification
+gate, and names every carried pull request and issue.
+
+Author preservation: each carried commit keeps its original author through a
+`Co-authored-by` trailer that survives a squash, satisfying
+`missing_coauthor_credit` in `.github/scripts/pr-carry-attribution.cjs`:
+
+- `Co-authored-by: MohamadSabree8 `
+- `Co-authored-by: R <53855466+cb8010d6@users.noreply.github.com>`
+- `Co-authored-by: mashfromband `
+
+## Merge gate
+
+Merge only when the tip's Cross-platform CI is green on the exact head SHA of the
+pull request. Skipped, cancelled or queued jobs are not passing evidence, and a
+green run on an earlier head does not certify a newer one. After merging, fetch
+`origin/dev` and prove landing in the way the chosen merge method allows.
+
+The method decides the proof, and only one of the three preserves the tip SHA:
+
+- **Create a merge commit.** The tip SHA itself becomes reachable from `dev`, so
+ `git merge-base --is-ancestor origin/dev` exits 0 and is sufficient.
+- **Squash and merge.** GitHub writes one new commit, so the tip SHA never becomes
+ an ancestor and that check would fail on a successful merge.
+- **Rebase and merge.** GitHub replays the commits onto `dev` with new SHAs, so the
+ original tip SHA is likewise not an ancestor.
+
+For the two rewriting methods, identify the landed commit or commits on `dev`
+first, then prove landing by content and attribution rather than by the original
+SHA:
+
+- `git diff origin/dev -- ` is empty;
+- `git merge-base --is-ancestor origin/dev` exits 0 for the landed
+ commit;
+- the landed record carries all three `Co-authored-by` trailers.
+
+Record the method used together with its matching proof, rather than asserting
+ancestry of the original tip generically.
+
+## Settlement
+
+Once the change is confirmed on `dev`:
+
+- PRs #3906, #3886 and #3917 — comment that the work landed on `dev` through the
+ stack tip, name the merge commit, and close them. Their authors are already
+ preserved in the trailers.
+- Issues #3885 (Spark `adapter_eof`), #3922 (Claude tool strict) and #3911
+ (routed `agent_message` 422) — close, since PRs target `dev` and GitHub only
+ auto-closes on merges into the default branch.
+- PR #3838 stays open; its residual work is unrelated to this stack.
+
+## Verification (C)
+
+The tip CI run identified by its run id and head SHA, with every required job
+reporting success, plus the merge-method-specific landing proof above against a
+freshly fetched `origin/dev`. Local suites: NOT RUN by maintainer instruction.
+
+
+## Merge readiness is broader than one workflow (audit finding 7)
+
+The single tip CI run is the verification evidence this session produces, but it is
+not by itself the whole merge gate. `MAINTAINERS.md:57-69` also requires the
+applicable required checks, resolution of outstanding maintainer objections, and
+applicable security review. The `dev`-only maintainer-integration path still
+records the decision and the exact-head evidence. Anything in that set that this
+session cannot satisfy is reported rather than assumed.
+
+## Attribution is added, not inherited
+
+The three carried commits do **not** already contain `Co-authored-by` trailers in
+their original messages. The trailers listed above are added when the commits are
+carried onto the stack, and their presence is verified on the final squash-surviving
+record before the children are closed.
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 00000000000..e4775732e65
--- /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/_fin/260908_b_track_quota_recovery_stack/000_plan.md b/devlog/_fin/260908_b_track_quota_recovery_stack/000_plan.md
new file mode 100644
index 00000000000..d73b85568a1
--- /dev/null
+++ b/devlog/_fin/260908_b_track_quota_recovery_stack/000_plan.md
@@ -0,0 +1,69 @@
+# 000_plan.md — B트랙 대화 복구·quota 스택 배송
+
+## 목표
+#3889(만료된 forward continuation의 WebSocket 복구)과 #3934(자격증명 세대 기반 늦은 WS quota 차단)를
+원저자 기여를 보존한 수동 종속 브랜치 체인으로 재구성하고, 최종 tip 한 곳에서만 CI를 태워
+green이면 tip을 dev에 통합한다.
+
+## 제약 (사용자 지시)
+- 로컬 스위트 절대 실행 금지: bun run test / test:changed / typecheck / build / install 모두 NOT RUN.
+- 푸시는 `--no-verify`.
+- CI는 최종 tip에만 트리거한다. 하위 레이어에는 PR을 열지 않는다.
+- 원작 PR이 있으면 원저자를 Co-authored-by로 보존한다.
+- tip이 dev에 머지되는 순간 연결 이슈도 닫는다.
+
+## CI 트리거 계약 (근거)
+`.github/workflows/ci.yml`의 `on.pull_request`에는 base 브랜치 필터가 없다(주석에 stacked child PR을
+일부러 포함시켰다고 명시). 따라서 **PR을 여는 것 자체가 CI run을 만든다.**
+`push:`는 `branches: [main, preview, dev]`로 제한되므로 포크/작업 브랜치 푸시는 CI를 만들지 않는다.
+결론: 하위 레이어 L1은 **브랜치 푸시만** 하고 PR을 열지 않는다. tip L2에만 PR을 연다.
+
+## 의존성 정렬 (PHASE-SPLIT-01)
+효율이 아니라 의존 구조로 나눈다. 두 변경 모두 `src/server/responses/core.ts`를 만지므로
+같은 파일 위에서 순서를 가진 체인으로 쌓는다.
+
+- L1 = #3889 continuation 복구 (core.ts:3598 부근 오류 코드 계약)
+- L2 = #3934 WS quota 세대 펜싱 (core.ts:1004 부근 observer) — L1 위에 쌓는다
+
+텍스트 충돌은 없다(두 훅 사이 거리 약 2600줄). 체인 순서는 리뷰 단위 분리를 위한 것이다.
+
+## 파일 변경 맵
+### L1 (#3889, 원저자 ykvv / y2ambition-ai)
+- MODIFY `src/server/responses/core.ts` — 400 응답 코드를 `invalid_request_error` → `previous_response_not_found`,
+ 메시지를 "전체 대화를 다시 보내라"로 변경. HTTP 상태와 인증 전 거부 위치는 유지.
+- MODIFY `tests/codex-integration/issue-702-expired-replay-state.test.ts` — 기존 HTTP 기대값의 code 갱신 +
+ expired/missing 두 모드의 WebSocket 재연결·전체 도구 이력 재전송 회귀 추가.
+- MODIFY `docs-site/src/content/docs/guides/codex-integration.md`, `.../ko/guides/codex-integration.md`
+
+### L2 (#3934, 원저자 luvs01)
+- MODIFY `src/server/responses/core.ts` — `codexWsQuotaObserver`에서 pool 자격증명 generation을 포착하고
+ `isCodexAccountGenerationLive`가 false면 늦게 도착한 quota 프레임을 무시.
+- MODIFY `tests/responses/responses-account-label.test.ts` — 교체된 자격증명의 늦은 quota가 지워진 상태를
+ 되살리지 못하는 회귀 추가.
+
+## 범위 밖 (OUT)
+- `REPLAY_TTL_MS` 등 캐시 보존 기간 변경
+- 인증/자격증명 회전 정책 변경
+- main-pool writer 소유권 규칙 변경
+- B트랙 외 항목(#3906/#3886/#3922/#3917/#3900/#3896/#3924/#3930/#3890)
+
+## 검증자 (PLAN-VERIFIER-REAL-01)
+로컬 스위트가 금지되었으므로 **로컬 검증자는 NOT RUN으로 기록한다**. 유일한 실행 검증자는
+tip PR head SHA에 대한 hosted Cross-platform CI다. 관측 대상: 4 Linux shard, Windows,
+macOS lane, gates(typecheck/lint/privacy scan), packaging.
+- `gh api repos/lidge-jun/opencodex/actions/runs?head_sha=` → conclusion=success
+- 이 CI는 `src/**`와 `tests/**`를 changes 필터에 포함하므로 실제로 이번 변경 대상을 관측한다.
+
+## 수용 기준
+1. L1/L2 커밋 각각에 원저자 Co-authored-by 트레일러가 살아 있다.
+2. L1에는 PR이 없고 CI run도 없다. CI run은 tip 하나뿐이다.
+3. tip head SHA의 CI conclusion이 success다.
+4. 로컬 스위트 미실행, 푸시는 --no-verify.
+5. tip이 dev 조상이 되고, #3889/#3934가 정리되며 연결 이슈가 닫힌다.
+
+## 우회 경로 (PLAN-BYPASS-NAMED-01)
+- tier: E2 (hosted CI 게이트)
+- 실행 주체: GitHub Actions + maintainer 통합
+- 알려진 우회: admin 권한 보유자는 CI 미완료 상태에서도 머지 가능. 이 계획은 그러지 않는다.
+- 잔여 위험: 하위 레이어 L1은 자체 CI 없이 tip 누적 CI로만 증명된다. 사용자 지시에 따른 의도된 선택.
+- 문구 하향: 없음.
diff --git a/devlog/_fin/260908_b_track_quota_recovery_stack/010_phase1_l1_continuation_recovery.md b/devlog/_fin/260908_b_track_quota_recovery_stack/010_phase1_l1_continuation_recovery.md
new file mode 100644
index 00000000000..4c054dd8c33
--- /dev/null
+++ b/devlog/_fin/260908_b_track_quota_recovery_stack/010_phase1_l1_continuation_recovery.md
@@ -0,0 +1,56 @@
+# 010_phase1_l1_continuation_recovery.md — L1 (#3889) 브랜치 구성
+
+## 목적
+만료·부재한 forward continuation 상태를 Codex WebSocket 클라이언트가 스스로 복구할 수 있게,
+프록시가 돌려주는 400 오류의 코드를 클라이언트가 인식하는 `previous_response_not_found`로 바꾼다.
+
+## 브랜치
+`codex/b-stack-l1-continuation-recovery`, base = `origin/dev`.
+
+## 커밋 계약
+원저자 보존이 필수다. 체리픽으로 원 커밋의 author를 그대로 유지한다.
+
+```
+git cherry-pick -x e8d82a181ea0daa06c5111c09e0148475e45458f
+```
+
+체리픽은 원 커밋의 author(ykvv <229483879+y2ambition-ai@users.noreply.github.com>)를 보존한다.
+squash 병합 시 author가 소실될 수 있으므로 커밋 메시지에 트레일러도 추가한다:
+
+```
+Co-authored-by: ykvv <229483879+y2ambition-ai@users.noreply.github.com>
+```
+
+## 정확한 변경 (before → after)
+`src/server/responses/core.ts` 약 3598행:
+
+```diff
+ if (
+ hasUnexpandedPreviousResponse
+ && isCanonicalOpenAiForwardProvider(route.provider)
+ ) {
+ return formatErrorResponse(
+ 400,
+- "invalid_request_error",
+- "OpenAI forward continuation state is unavailable or expired; start a new session instead of reusing this previous_response_id.",
++ "previous_response_not_found",
++ "OpenAI forward continuation state is unavailable or expired; resend the full conversation without previous_response_id.",
+ );
+ }
+```
+
+가드 위치(인증·어댑터·upstream I/O 이전)는 바뀌지 않는다. HTTP 상태 400도 유지한다.
+
+테스트: `tests/codex-integration/issue-702-expired-replay-state.test.ts`
+- 기존 HTTP 케이스: `code`를 `previous_response_not_found`로 갱신, `type`은 `invalid_request_error` 유지.
+- 신규: expired/missing 두 모드로 WebSocket 연결 → 거부 확인 → upstream 요청 0건 확인 →
+ 재연결 후 전체 이력 재전송 → upstream 1건 + `previous_response_id` 없음 + 도구 호출/결과 쌍 보존.
+
+문서: `docs-site/src/content/docs/guides/codex-integration.md` 및 한국어 페이지에 복구 경계 문단 추가.
+
+## 검증
+로컬 스위트 NOT RUN(사용자 금지). 이 레이어는 PR을 열지 않으므로 자체 CI도 없다.
+증명은 L2 tip의 누적 CI가 담당한다.
+
+## 감사 반영
+서브에이전트 audit-3889의 결과에 따라 문서의 TTL 수치와 error type/code 매핑을 확정한다.
diff --git a/devlog/_fin/260908_b_track_quota_recovery_stack/020_phase2_l2_ws_quota_generation_fence.md b/devlog/_fin/260908_b_track_quota_recovery_stack/020_phase2_l2_ws_quota_generation_fence.md
new file mode 100644
index 00000000000..8ac37b83f90
--- /dev/null
+++ b/devlog/_fin/260908_b_track_quota_recovery_stack/020_phase2_l2_ws_quota_generation_fence.md
@@ -0,0 +1,54 @@
+# 020_phase2_l2_ws_quota_generation_fence.md — L2 (#3934) tip 레이어
+
+## 목적
+pool 자격증명이 교체된 뒤 이전 WebSocket 연결에서 늦게 도착한 quota 프레임이,
+새 자격증명을 위해 비워둔 quota 상태를 되살리지 못하게 막는다.
+
+## 브랜치
+`codex/b-stack-l2-ws-quota-generation`, base = `codex/b-stack-l1-continuation-recovery` (L1 위에 쌓음).
+이 브랜치가 스택의 tip이며, **PR은 여기에만 연다.**
+
+## 커밋 계약
+```
+git cherry-pick -x e5c01f44e9736baba5b3a993c7f489f6b60d5ddd
+```
+원저자 luvs01 보존 + `Co-authored-by: luvs01 ` 트레일러.
+
+## 정확한 변경 (before → after)
+`src/server/responses/core.ts` 약 1004행:
+
+```diff
++import { isCodexAccountGenerationLive } from "../../codex/account-store";
+
+ function codexWsQuotaObserver(authCtx, provider): CodexWsQuotaObserver | undefined {
+ if (!isCanonicalOpenAiForwardProvider(provider) || !usesCodexForwardPoolAuth(authCtx, provider)) return undefined;
+ const { accountId, writerGeneration } = authCtx;
++ const credentialGeneration = authCtx.kind === "pool" ? authCtx.generation : undefined;
+ const mainWriter = authCtx.kind === "main-pool" ? authCtx.mainQuotaWriter : undefined;
+- return headers => applyCapturedCodexQuota(accountId, headers, writerGeneration, mainWriter);
++ return headers => {
++ if (credentialGeneration !== undefined && !isCodexAccountGenerationLive(accountId, credentialGeneration)) return;
++ applyCapturedCodexQuota(accountId, headers, writerGeneration, mainWriter);
++ };
+ }
+```
+
+`credentialGeneration === undefined`면 기존 동작을 그대로 유지한다(main-pool·비pool 경로 무변경).
+
+테스트: `tests/responses/responses-account-label.test.ts`
+- quota 10 전달 → 자격증명 교체 → quota clear → 옛 연결에서 quota 100 전달 → 최종 상태가 null인지 확인.
+
+## L1과의 관계
+같은 파일이지만 서로 다른 함수(약 2600줄 간격)라 텍스트 충돌이 없다.
+체인 순서는 리뷰 단위를 나누기 위한 것이며, L2 diff는 이 변경만 보여준다.
+
+## CI 계약
+`.github/workflows/ci.yml`의 `on.pull_request`는 base 필터가 없어 PR 생성 즉시 CI가 붙는다.
+따라서 L1에는 PR을 열지 않고, tip인 L2에만 PR을 연다 → CI run 정확히 1개.
+`changes` 필터가 `src/**`, `tests/**`, `docs-site` 외 경로를 보므로 이 변경 세트는 `ci=true`가 되어
+4개 Linux shard, Windows, macOS lane, gates가 모두 돈다.
+
+## 머지 후 처리
+- tip PR 머지 → `git merge-base --is-ancestor`로 dev 조상 확인
+- #3889, #3934: 내용이 dev에 들어갔으므로 원저자 크레딧을 명시하며 닫는다
+- 연결 이슈: dev 머지 시점에 닫는다 (PR base가 dev라 GitHub 자동 종료가 안 됨 — AGENTS.md 명시)
diff --git a/devlog/_fin/260908_b_track_quota_recovery_stack/030_outcome.md b/devlog/_fin/260908_b_track_quota_recovery_stack/030_outcome.md
new file mode 100644
index 00000000000..7f5ef7e74ec
--- /dev/null
+++ b/devlog/_fin/260908_b_track_quota_recovery_stack/030_outcome.md
@@ -0,0 +1,58 @@
+# 030_outcome.md — 배송 결과
+
+## 결과
+PR [#3937](https://github.com/lidge-jun/opencodex/pull/3937)이 `dev`에 머지되었다.
+머지 커밋 `ca381ea764cfbc63bec978f53eb58e96c00c0c64`, 2026-09-07T18:31:27Z.
+
+## 스택 구조 (실제)
+```
+dev 942c02873
+ └─ 7273a0d1f docs(devlog): plan the B-track ... [계획]
+ └─ 531753340 fix(responses): recover expired ... [L1, author ykvv]
+ └─ a06bfa2f2 fix(codex): fence late WS quota [L2 = tip, author luvs01]
+```
+L1에는 PR을 열지 않았다. tip에만 PR을 열어 CI를 1회 트리거했다.
+
+## CI 증거
+- tip `a06bfa2f2`: Cross-platform CI run 1건, conclusion=success, run_attempt=1
+ ([run 34149252860](https://github.com/lidge-jun/opencodex/actions/runs/34149252860)).
+ 잡 21/21 완료, failure 0. Linux shard 4, macOS lane 2, gates, packaging, keyring, Docker smoke 포함.
+- 하위 레이어 `531753340`: workflow run **0건**. 브랜치 푸시가 CI를 만들지 않는다는 계약이 실측으로 확인됐다.
+- `enforce-target`은 동시성 그룹 충돌로 1차 시도가 취소되어, 대기 중이던 중복 run을 취소하고 재실행해 success를 받았다.
+
+## 검증 한계 (사실대로 기록)
+- **로컬 제품 스위트는 한 번도 실행하지 않았다** (`bun run test`/`test:changed`/`typecheck`/`build`/`install`: NOT RUN).
+ 사용자 지시에 따른 것이며, hosted CI가 유일한 실행 검증자였다. 푸시는 전부 `--no-verify`.
+- tip SHA에 취소된 체크 2건이 남아 있다: `enforce-target`(101832157192, 옛 시도)과 `label`(101827844691).
+ 같은 워크플로의 후속 시도가 success로 끝났고 failure는 0건이다. 체크 목록이 전부 깨끗하다고 말하면 사실이 아니다.
+- CI가 검증한 트리(tip)와 최종 dev 트리는 동일하지 않다. 머지 직전 별도 PR #3936(문서)이 먼저 착륙해
+ lifecycle 문서 5개가 차이로 남는다. `git diff --exit-code a06bfa2f2 ca381ea76 -- src tests`는 exit 0으로,
+ **소스와 테스트는 CI가 본 그대로** 착륙했다.
+
+## 감사 (astra-high 서브에이전트 4기)
+1. `audit-3889`: PASS. `formatErrorResponse`의 2번째 인자는 `classifyError` 입력이며
+ `previous_response_not_found` 분기가 `type=invalid_request_error`/`code=previous_response_not_found`를 만든다
+ (`src/bridge.ts:2130`, `src/lib/errors.ts:179`). 문서의 1시간은 `RESPONSE_TTL_MS=3_600_000`과 일치.
+2. `audit-3934`: PASS. `main-pool`에 `generation`이 없는 것은 의도된 분리이며 `mainQuotaWriter`가 별도 펜싱한다.
+ `writerGeneration`(설정 재조정)과 `generation`(영속 자격증명)은 다른 개념이라 새 검사가 중복이 아니다.
+ generation `0`은 엄격 동등으로 정상 처리된다.
+3. `verify-stack`: PASS. 체리픽 hunk 무결성, 두 변경의 공존, import/export, 테스트 심볼, layout, privacy 6항목.
+4. `verify-landing`: 7개 주장 중 6개 CONFIRMED, 1개 REFUTED(위 취소 체크 건). 이 문서가 그 반증을 반영한다.
+
+## 원저자 크레딧
+머지 커밋에 두 트레일러가 모두 살아 있다.
+```
+Co-authored-by: ykvv <229483879+y2ambition-ai@users.noreply.github.com>
+Co-authored-by: luvs01
+```
+원본 PR #3889·#3934는 배송 완료 안내와 함께 closed(미머지)로 처리했다.
+
+## 연결 이슈
+GraphQL `closingIssuesReferences`로 확인한 결과 #3889·#3934·#3937 모두 종료 대상 이슈가 **0건**이다.
+따라서 이번 머지로 닫을 이슈는 없었다. (#3885는 A트랙 #3886 소관이라 대상이 아니다.)
+
+## 이번에 나아지지 않은 것
+- 하위 레이어 L1은 자체 CI 증거 없이 tip 누적 CI로만 증명됐다. 사용자 지시에 따른 의도된 선택이며,
+ 레이어별 독립 회귀 증거가 필요한 변경에는 이 방식을 그대로 쓰면 안 된다.
+- `enforce-target` 동시성 충돌은 재실행으로 우회했을 뿐 원인을 고치지 않았다.
+ 같은 SHA에 워크플로가 두 번 트리거되는 조건이 남아 있다.
diff --git a/devlog/_fin/260908_bug6_manual_stack/000_plan.md b/devlog/_fin/260908_bug6_manual_stack/000_plan.md
new file mode 100644
index 00000000000..cda1225ad63
--- /dev/null
+++ b/devlog/_fin/260908_bug6_manual_stack/000_plan.md
@@ -0,0 +1,18 @@
+# Six-item bug stack — completed delivery
+
+All five new product PRs merged into `dev` on 2026-09-08. The sixth source item, #3965, had independently landed and required no duplicate PR. [071](071_delivery.md) records verification and retained failure history; [072](072_final_proof.md) records actual landing proof.
+
+| Source | Delivery | Disposition |
+| --- | --- | --- |
+| #3838 Go placement/stateless residual | #3986 | Landed; source closed; lossy mixed-ciphertext filtering declined |
+| #3907 xAI string child result | #3991 | Landed; issue completed |
+| #3944 V2 proxy guidance | #3992 | Landed; source closed |
+| #3951 server-owned delegation preset | #3993 | Landed; source closed |
+| #3965 canonical operation alias | Existing merge402be7c1f | Verified landed NOOP for another PR |
+| #3973 cooldown recovery, consolidated #3995 | #4002 | Landed; issue completed and source PR closed |
+
+This was one ordinary manual chain. Children were retargeted to dev before their parents merged because repository settings automatically delete merged branches. Original authorship and Co-authored-by trailers were preserved. #3997/#3996 remain outside this delivery.
+
+The earlier decade documents are historical plans and audit amendments. Their future-tense steps describe what was required at that point; this outcome and the final ledger are authoritative for completion. The work used repeated PABCD cycles and independent Astra high source/security reviews.
+
+Local product tests, installs, typechecks and builds: **NOT RUN**, by owner instruction. Commits disabled hooks per invocation and pushes used --no-verify. Hosted CI, synthetic dashboard observation and isolated remote documentation builds supplied verification. No release, deployment, main/preview promotion, live account operation or reset credit was used. All 30 pre-existing user files were preserved.
diff --git a/devlog/_fin/260908_bug6_manual_stack/010_go_compatibility.md b/devlog/_fin/260908_bug6_manual_stack/010_go_compatibility.md
new file mode 100644
index 00000000000..fa7a4e78450
--- /dev/null
+++ b/devlog/_fin/260908_bug6_manual_stack/010_go_compatibility.md
@@ -0,0 +1,42 @@
+# wp1: Go compatibility residuals
+
+Historical phase record. Delivery is complete; see [071](071_delivery.md) and [072](072_final_proof.md) for terminal evidence.
+
+Depends on wp0. C3 with independent boundary review. Source PR #3838 head `d84e5a80a5e40a65462a0466d82cdcec463a847e`; baseline dev `9e1468d4b7a41b498ed2aca98507ada2c741afea`. #3942 already landed the generic array agent-message normalizer. Reimplement the remaining Go behavior on current owners; do not restore the removed `opencode-go.ts` or duplicate namespace logic.
+
+## Main decisions
+
+Carry Go additional-tools placement and the canonical preset's stateless intent. Retain current all-parts readable/fail-closed agent-message behavior; the old lossy mixed-content hunk is deliberately declined because successful transport alone does not prove complete task content. Original PR disposition must name that decision rather than claim every historical hunk landed. Custom renamed providers retain explicit configuration semantics; no destination-based configuration migration is added.
+
+## File changes
+
+- NEW `src/adapters/opencode-go-additional-tools.ts`: export a small immutable placement helper taking body and base URL. Match HTTPS `opencode.ai`, standard port, exact `/zen/go/v1` (optional terminal slash); reject credentials/query/fragment and other paths. For valid `additional_tools` array wrappers append their already-normalized tools to top-level tools and remove the wrapper. Preserve unrelated input and supported nameless hosted tools. Non-array malformed wrappers remain unchanged; no valid wrappers returns the original body. Existing namespace/custom owners perform identity lowering and dedupe before this pass.
+- MODIFY `src/adapters/openai-responses.ts`: import helper; invoke only inside non-forward dispatch after existing namespace/custom/search lowering around baseline line 2455, before code-mode/compaction and later hosted-tool pruning. Response alias maps stay owned by prior normalization.
+- MODIFY `src/providers/registry.ts`: canonical `opencode-go` entry gains `statelessResponses: true`. Existing derive logic seeds/backfills only absent values; explicit false remains authoritative.
+- MODIFY `tests/providers/opencode-go-grok46-responses.test.ts`: replace the old expected private wrapper with promoted tools; cover duplicate containers, distinct namespace same-name children, custom/function handling, hosted Luna search versus Go Grok denial, tool_choice none/allowed list, tool_search_output activation, forward/Zen/lookalike/wrong-port exclusion and immutable replay.
+- MODIFY `tests/providers/opencode-go-luna-wire.test.ts`: cover seed/backfill/false, full-history continuation with synthetic reasoning and paired tool results; assert previous_response_id removed, store false, call pairing/history retained. Cover stateless orphan and reasoning-summary interactions through existing focused suites. No new test file is required if these current owners remain reviewable.
+- MODIFY `docs-site/src/content/docs/reference/configuration/providers.md` and `structure/04_transports-and-sidecars.md`: record Go wrapper placement and canonical stateless default with explicit override and full-history limits. Update only contradicting translated statements.
+
+## Before / after flow
+
+Before: namespace normalization leaves valid declarations inside `input.additional_tools`; strict Go receives a private wrapper. After: the same normalized declarations appear in `tools`, and valid wrappers are removed. Before: canonical Go may forward previous_response_id with replay history. After: existing stateless normalization strips the stored-continuation parameters and sends complete history.
+
+## Activation and observable coverage
+
+Use production adapter fixtures, not a duplicate normalizer oracle. Namespace alpha.lookup and beta.lookup must both remain callable; duplicate wire identities follow the existing canonical owner. Nameless hosted tools must survive placement until provider/model pruning. Malformed wrapper, unapproved destination and forward controls stay byte-identical. Seed false must differ from default true. A two-turn synthetic continuation must preserve meaningful reasoning/tool history while removing stored-state references. Inspect existing stateless orphan repair and summary tests; extend any missing Go model coverage without weakening assertions.
+
+Hosted verification: PR CI covers changed runtime and provider suites, with final full dispatch before integration. Local product tests/install/typecheck/build are NOT RUN. Preserve original PR account-linked Co-authored-by credit; resolve identity from GitHub before commit. The full source investigation is in ignored `.tmp/bug6-01a07e9d/go-xai-plan.md`; it is not public implementation proof.
+
+## wp1 P refresh
+
+Previous wp0 D directs Go residual implementation. During live refresh dev advanced to c15662855 (#3975), changing only tests/codex-integration/codex-prompt-text-probe.test.ts. Hook-disabled merge incorporated that unrelated probe fixture correction before B; Go owners and this design are unchanged. The initial A narrative said unchanged dev based on the pre-fetch snapshot; this entry corrects it.
+
+## C audit foldback and repair plan
+
+Independent review at 9b42c1a80 found two blockers. F1 accepted: the stateless flag enables content-to-summary output normalization, but the continuation cache records original output; full-history overlap then fails. The adapter-only full-history fixture bypassed the affected server boundary. F2 accepted: baseUrl-only matching misses split/endpoint-inclusive configurations and can affect an overridden non-Go resource. Neither finding conflicts with preserving opaque items or existing fail-closed policy.
+
+Repair F1: MODIFY `src/server/responses/core.ts` at `rememberPassthroughResponseChecked` only. After current namespace/custom/function restoration, apply existing `rewriteReasoningSummaryInJson` under the same `hideThinkingSummary !== true && routeUsesContentChannelReasoning(provider, model)` condition as client output, then record that representation. Preserve item content and IDs under the existing opaque-item rule; do not weaken overlap comparison or use ID-only matching. This aligns stored output with the actual client serialization for SSE and JSON. Extend the current Go server fixture to send actual full-history plus previous_response_id and assert each prior call/message occurs exactly once; retain delta replay and hiding/opaque controls. The shared callback is an explicit narrow scope expansion required by this newly activated path, not unrelated state refactoring.
+
+Repair F2: the helper now accepts the final resolved Responses request URL already built by the adapter. Match exact origin and `/zen/go/v1/responses`, rejecting userinfo/query/fragment. Positive fixtures cover normal base, endpoint-inclusive base and split custom path; negative fixtures cover an override resolving to Zen/non-Go and assert both actual request URL and body. Update destination wording in docs and preserve all prior host/port/immutability controls.
+
+Re-review the repaired diff with the same implementation auditor; retain CI failures and repair evidence. No local product commands are authorized.
diff --git a/devlog/_fin/260908_bug6_manual_stack/020_xai_continuation.md b/devlog/_fin/260908_bug6_manual_stack/020_xai_continuation.md
new file mode 100644
index 00000000000..8308cc290ef
--- /dev/null
+++ b/devlog/_fin/260908_bug6_manual_stack/020_xai_continuation.md
@@ -0,0 +1,25 @@
+# wp2: xAI string child-result continuation
+
+Historical phase record. Delivery is complete; see [071](071_delivery.md) and [072](072_final_proof.md) for terminal evidence.
+
+Depends on wp1 current outbound placement and full-history regression controls. C3. Issue #3907 posts string `agent_message.content`; #3942 already implements arrays for all non-forward destinations. Scope is the string residual only.
+
+## File changes
+
+- MODIFY `src/adapters/routed-agent-messages.ts`: extend `normalizeRoutedAgentMessages` with optional `{ allowStringContent?: boolean }`, default false. If enabled and content is a nonblank string, create one input_text part containing the exact original string. Existing attribution and array handling continue. Whitespace-only, unknown, malformed and ciphertext shapes remain unchanged; never trim the forwarded text or mutate the input.
+- MODIFY `src/adapters/openai-responses.ts`: reuse `isXaiResponsesDestination` from `src/providers/xai-transport.ts`, pass its result as allowStringContent inside the existing `!forward` call. Existing array behavior stays available for other non-forward destinations. No custom-forward exception.
+- MODIFY `tests/adapters/routed-agent-messages.test.ts`: exercise both exact xAI hosts, API-key and OAuth, exact text/newlines, attribution, missing transport item ID and input immutability. String controls: native/custom forward, other providers, lookalike hosts and blank content stay unchanged; existing all-parts array/ciphertext tests remain.
+- MODIFY `tests/server/server-xai-responses-streaming.test.ts`: extend the synthetic server fixture with parent request, child request, then parent continuation containing string child result plus genuine paired tool history. Upstream stub rejects surviving private agent_message with 422; assert user-message child text, ordinary response completion, paired calls preserved and no repeated incompatible dispatch. This exercises the wire boundary, not the actual Codex scheduler.
+- MODIFY `docs-site/src/content/docs/reference/adapters.md`, `docs-site/src/content/docs/reference/configuration/providers.md`, and the contradicting Russian adapters paragraph: describe existing non-forward array conversion and xAI string extension, preserving forward/encrypted exclusions. Sync `structure/04_transports-and-sidecars.md` without broadening the passive manifest claims.
+
+## Before / after
+
+Before the raw-body outbound normalizer requires array content and leaves the issue's string item on the strict xAI wire. After it produces `{type: message, role: user, content: [{type: input_text, text: originalText}]}` through the existing attribution rules, only for an approved non-forward xAI destination. No tool result is synthesized and no encrypted message is partially discarded.
+
+## Verification
+
+Pin parent/child fixtures to synthetic input. The strict upstream stub must reject the pre-fix request shape and accept the normalized one; destination-negative controls prove the guard is active. Hosted PR CI and final full dispatch execute adapter/server regressions. Local tests/install/typecheck/build remain NOT RUN. Source audit checks raw-body call placement and all consumers of the added option. There is no serialized configuration field or migration: option creation and consumption are both in-memory adapter calls.
+
+## wp2 P refresh
+
+Previous wp1 D: PR3986 at d1f61e933 passed run34178540141 and independent source/security audit, with18Go replay scenarios and remote docs425pages. Proceed to xAI string residual. Candidate339e42c1e was prepared in an isolated worktree under the owner-authorized parallel-preparation amendment; it is not yet adopted. Its base exactly equals the certified preceding layer, and the eight-file diff matches this plan. Existing xAI predicate remains the destination owner; no account changes. Issue3907 is still open. Main revalidates candidate before B and retains fresh hosted CI before wp2 closure.
diff --git a/devlog/_fin/260908_bug6_manual_stack/030_v2_guidance.md b/devlog/_fin/260908_bug6_manual_stack/030_v2_guidance.md
new file mode 100644
index 00000000000..aac9ffab00d
--- /dev/null
+++ b/devlog/_fin/260908_bug6_manual_stack/030_v2_guidance.md
@@ -0,0 +1,55 @@
+# wp3: V2 guidance carry
+
+Historical phase record. Delivery is complete; see [071](071_delivery.md) and [072](072_final_proof.md) for terminal evidence.
+
+Depends on wp2 in the owner-requested manual chain. Carry PR #3944 at 6fb0fc6f1d34c77b98a74fe817e5bd90063a7d1a with both original commits and contributor trailer. Local product verification is NOT RUN.
+
+ separate proxy routing metadata from native policy
+
+Source: [pinned collaboration implementation](https://github.com/lidge-jun/opencodex/blob/6fb0fc6f1d34c77b98a74fe817e5bd90063a7d1a/src/server/responses/collaboration.ts#L244), [pinned regression changes](https://github.com/lidge-jun/opencodex/blob/6fb0fc6f1d34c77b98a74fe817e5bd90063a7d1a/tests/codex-integration/multi-agent-compat.test.ts#L1188). C3 source change, with C4 care for developer-instruction and public-contract semantics; this lane does not run orchestration.
+
+### Concrete before -> after hunks
+
+| Exact path / baseline anchor | Before | After to carry |
+| --- | --- | --- |
+| `src/server/responses/collaboration.ts:242` | Only the native proactive constant exists. | Add private `OPENCODEX_SUBAGENT_GUIDANCE_OPEN_TAG` / `CLOSE_TAG` constants after it. Leave `PROACTIVE_MULTI_AGENT_MODE_TEXT` unchanged. |
+| `src/server/responses/collaboration.ts:466-490` | Custom and built-in v2 bodies use native tags; built-in prescribes overrides, `fork_turns`, and preferred-model use. | Wrap both v2 paths in the proxy tag; replace only built-in preamble with routing-metadata wording; preferred clause ends with a period. Preserve placeholder substitution, effective preferred model, account namespace filtering, roster/fallback text, stale/unknown suppression and roster-drop budget. |
+| `src/server/responses/collaboration.ts:543-545` | Exact generated-item predicate only. | Add `generatedGuidanceFamily(text)` recognizing exactly the two outer tag families. This is a dedup classification, not an authorship assertion. |
+| `src/server/responses/collaboration.ts:583-594` | Latest-match handling only for native tag; other text dedups against any earlier exact match. | For either known family, compare incoming text to the latest exact generated developer text within that family in the replay prefix. For untagged text retain existing exact-item behavior. |
+| `src/types/config.ts:472-475,515-516` | Effort JSDoc prescribes spawn overrides; custom-body wrapper is native tag. | Describe effort as advisory v2 metadata; update wrapper name. Preserve type declarations, injectionModel dependency and reasoning-level validation documentation. |
+| `tests/codex-integration/multi-agent-compat.test.ts:270,387,615,741,781,847,1185` | Old v2 wrapper and imperative expectations; native-family A-B-A only. | Carry all changed expectations and new proxy/native replay matrices from the head; preserve existing v1, catalog, placement, sanitization and shape-negative tests. |
+| `docs-site/src/content/docs/reference/configuration/agents.md:77-93` | Excludes v1 leaf workers and describes model/fork overrides. | Correct already-shipped leaf eligibility; describe proxy wrapper, advisory metadata, preserved custom body and per-family latest-text dedup. State mixed-version limitations. |
+| `docs-site/src/content/docs/ja/reference/configuration/agents.md:31-37` | Old roster/guidance contract. | Carry corresponding roster correction and proxy/native/replay qualification. |
+| `docs-site/src/content/docs/ko/reference/configuration/agents.md:31-37` | Same old contract. | Same localized contract. |
+| `docs-site/src/content/docs/ru/reference/configuration/agents.md:38-57` | Same old contract, longer prose. | Carry full localized explanation including historical-message limitation. |
+| `docs-site/src/content/docs/zh-cn/reference/configuration/agents.md:31-37` | Same old contract. | Same localized contract. |
+
+Do not mass-replace ``: `collaboration.ts:493-497` remains the v1 max/ultra parity path, and native/legacy history must remain byte-identical. Do not add a settings migration or infer the author of an old tagged message. No revocation of already injected instructions is claimed.
+
+### Data creation and consumers
+
+Existing config fields, not new fields: `/api/injection-model` maps `model`, `effort`, `prompt`, `multiAgentGuidanceEnabled` to `OcxConfig.injectionModel`, `injectionEffort`, `injectionPrompt`, `multiAgentGuidanceEnabled` (`agent-settings-routes.ts:501-508,529-589`). `saveConfigPreservingClaudeCode` persists the existing JSON config. No changes to that input/storage contract are needed.
+
+`src/server/responses/core.ts:2421-2429` passes those fields, route account namespace, featured models and fallback to `multiAgentGuidanceText`. `collaboration.ts:366-405` retains feature/tool/catalog admission; `:409-464` retains request-scoped roster and preferred/fallback derivation. Only the final rendered text changes. `applyInjectionPlaceholders` at `:504-509` still substitutes the four placeholders and preserves unknown placeholders.
+
+`core.ts:2430-2433` calls `injectDeveloperMessage`. `collaboration.ts:583` creates raw `{type:"message",role:"developer",content:[{type:"input_text",text}]}`; `:597-613` creates the parsed `{role:"developer",content:text,timestamp}` and places it; `:616-621` splices raw input at the matching conversation position. Both representations must retain the same ordered text across stateful replay. `src/server/responses.ts:6` reexports the same public helpers unchanged. `src/codex/subagent-model-fallback.ts:688-692` continues owning fallback prose. Native default sync is an independent consumer per `structure/03_catalog-and-subagents.md:459-465`; do not merge it with guidance.
+
+### Regression activation
+
+Carry the existing hermetic `CODEX_HOME` / catalog fixture builders (`multi-agent-compat.test.ts:23-103`), including the fresh catalog override and its cleanup. Test the actual helpers through `parseRequest`, raw input, `_replayPrefixLen`, and `_continuationConversationMessageIndex`.
+
+1. V2 built-in has proxy tags and model/effort/roster/fallback metadata, and lacks native tag, `fork_turns`, and preferred-model imperative. V1 max/ultra retains native tag and below-top silence. Disabled guidance and stale/unknown catalog produce null.
+2. Built-in A -> custom B -> built-in A appends last A; unknown placeholders/newlines in custom B remain unchanged.
+3. Native A -> proxy P -> native B -> same proxy P adds nothing; native text dedup ignores later proxy P.
+4. Old built-in/native-tagged custom text + native policy + new proxy text preserves the original prefix and appends new proxy metadata. Never assert historical authorship or automatic cleanup.
+5. Keep exact-shape negatives, 700-character fixture, external-task input, leading tool-result, compaction marker and raw/parsed alignment cases.
+6. Small additional hunk recommended in `tests/server/server-combo-failover-e2e.test.ts:2285` beside the existing generated-guidance replay case: configure a synthetic v2 tool/catalog route, change built-in -> custom -> built-in across actual response continuations, capture adapter input, assert latest proxy guidance and native policy survive once and precede the current task. Use the existing server harness. Preserve current-dev compaction/recall fixtures in this file. This activates the caller/replay integration rather than only manually assigning parsed indices.
+
+Remote-only focused activation: `bun test tests/codex-integration/multi-agent-compat.test.ts`; additionally `bun test tests/server/server-combo-failover-e2e.test.ts` if adding the caller fixture. **NOT RUN here.** Negative controls for the remote verification owner: reverting the custom wrapper, collapsing the two families, or changing latest-family comparison to any-ever comparison must fail the corresponding transition tests. No local mutation/control execution.
+
+
+Main decision: preserve the complete original diff. The optional extra server caller fixture is deferred unless source audit reveals an untested change; do not duplicate the existing replay matrix merely for volume. Sync structure/03_catalog-and-subagents.md to the new tag and policy boundary.
+
+## wp3 P refresh
+
+Previous wp2 D: PR3991 head00eb47886 passed run34180674115, source audit and remote docs425pages; proceed guidance carry. Prepared layer3 consists of24977adf2,21757b71a,8000e2482, based on d1f61e933. Intervening wp2 changes affect xAI adapter/tests, provider/adapters docs and structure04; none overlap the9layer3 files. Original #3944 remains open at6fb0fc6f. Independent prepared-source/security audit PASS in isolated v2GuidanceReviewer.md; actual adoption requires unchanged-delta/interdiff verification and own hostedCI.
diff --git a/devlog/_fin/260908_bug6_manual_stack/040_v2_preset.md b/devlog/_fin/260908_bug6_manual_stack/040_v2_preset.md
new file mode 100644
index 00000000000..4ab32b47683
--- /dev/null
+++ b/devlog/_fin/260908_bug6_manual_stack/040_v2_preset.md
@@ -0,0 +1,96 @@
+# wp4: server-owned proactive preset carry
+
+Historical phase record. Delivery is complete; see [071](071_delivery.md) and [072](072_final_proof.md) for terminal evidence.
+
+Depends on wp3. Carry PR #3951 at db502d486c8d8de80c0143fcfd6f86238d1ce917 with its contributor trailer. All local product commands are NOT RUN.
+
+ make the server own proactive preset recommendations
+
+Source: [pinned policy module](https://github.com/lidge-jun/opencodex/blob/db502d486c8d8de80c0143fcfd6f86238d1ce917/src/codex/multi-agent-mode-policy.ts#L1), [pinned API additions](https://github.com/lidge-jun/opencodex/blob/db502d486c8d8de80c0143fcfd6f86238d1ce917/src/server/management/agent-settings-routes.ts#L252), [pinned GUI source binding](https://github.com/lidge-jun/opencodex/blob/db502d486c8d8de80c0143fcfd6f86238d1ce917/gui/src/pages/Subagents.tsx#L48). C3 API/GUI change, C4 care for persisted developer-policy text and management boundary.
+
+### Concrete before -> after hunks
+
+| Exact path / baseline anchor | Before | After to carry |
+| --- | --- | --- |
+| `src/codex/multi-agent-mode-policy.ts` (new, head lines 1-24) | Browser owns the preset; no recommendation owner. | Create dependency-free constant `{revision:"proactive-trigger-v1",text:[six clauses].join(" ")}`, two exact released legacy strings, and exact-equality canonicalizer. The recommendation changes the delegation trigger while preserving user/authority/scope/tool rules. |
+| `src/codex/features.ts:39,1077-1094` | Non-null writer probes support then forwards original text. | Import canonicalizer; after existing capability probe set `canonicalValue = value === null ? null : canonicalizeOpenCodexModeHint(value)`; pass only that value to existing `setV2StringField`. No read-time/startup migration. |
+| `src/server/management/agent-settings-routes.ts:43,250,423` | GET/PUT return stored hint only. | Import recommendation constant and append `multiAgentModeHintRecommendation: MULTI_AGENT_MODE_HINT_RECOMMENDATION` to both successful serializers. Do not add it to accepted PUT fields or config storage. |
+| `gui/src/pages/use-subagent-delegation.ts:22-31` | State has stored hint but no recommendation. | Export `UltraModeHintRecommendation {text:string;revision:string}`; require `recommendation: UltraModeHintRecommendation \| null` in `UltraModeState`. Keep patch type as hint text/null and mode. |
+| `gui/src/pages/Subagents.tsx:11,43,60-79` | Mode state outlives apiBase; no recommendation validation. | Add `UNLOADED_ULTRA_MODE` including null recommendation; hold `{apiBase,mode}`; derive current/unloaded state by exact apiBase. Validate nonblank string text AND revision, retaining their exact bytes; malformed/missing recommendation becomes null. Preserve abort/generation/current-server guards. |
+| `gui/src/pages/Subagents.tsx:97-118,343` | Save checks busy only; forwarded busy does not describe hydration. | Refuse save without current-server mode or matching current apiBase; forward `ultraSaving || !ultraModeCurrent`. Keep PUT then GET refresh and stale-server outcome suppression. |
+| `gui/src/components/subagents-workspace/SubagentDelegationSection.tsx:299-328` | Enable/Restore use local constant. | Enable sends current server recommendation.text; disable still sends null. Disable install if forced-v2 eligibility or recommendation is absent; existing nonblank hint remains clearable. Pass nullable server preset to editor. Keep editor key based on stored hint, not recommendation revision. |
+| `gui/src/components/subagents-workspace/SubagentDelegationSection.tsx:341-391` | Editor requires string preset; exported browser preset remains. | Nullable preset; Restore changes draft only and is disabled without preset; Save preserves nonblank draft bytes. Remove local `ULTRA_MODE_PRESET` export. |
+| `gui/tests/multi-agent-guidance.test.tsx:68,153` | Fixtures lack new field. | Add null recommendation and recommendation-refresh/unsaved-draft regression. |
+| `gui/tests/subagents-ultra-mode.test.tsx:1-178` | Imports local preset and asserts duplicated text. | Remove preset import; lazy-import createRoot after DOM setup; use distinctive server recommendation fixture; carry API/malformed/edit/restore/save/server-switch cases. |
+| `tests/codex-integration/codex-v2-gate.test.ts:47,426,1342` | Existing TOML and API contracts only. | Import recommendation; independently pin both released strings; add writer normalization, custom preservation, GET-no-write/unrelated-PUT preservation, readback and idempotence cases. |
+
+All nine locale modules retain keys and change exactly these five values: `sub.ultraMode`, `sub.ultraModeText`, `sub.ultraModeLoadFail`, `sub.ultraModeSaveFail`, `sub.ultraModeSaved`. Exact baseline anchors: `gui/src/i18n/en.ts:712`, `de.ts:683`, `fr.ts:695`, `ja.ts:643`, `ko.ts:700`, `ru.ts:698`, `tr.ts:705`, `zh-TW.ts:550`, `zh.ts:693`. English label becomes “Always proactive delegation”; no hardcoded JSX copy or locale-key rename.
+
+Docs: merge the wp4 proactive-preset section into `docs-site/src/content/docs/reference/configuration/agents.md:48-60`, and add the equivalent section to `ja/`, `ko/`, `ru/`, `zh-cn/` versions of that same path. Preserve every wp3 roster/proxy-wrapper/replay paragraph. These five files are the only shared files between the original PRs; carry their hunks, not whole-file replacement from #3951. The combined original carry touches 26 distinct files.
+
+Recommended small SOT followups, main to include in its phase write allowlist: at `structure/03_catalog-and-subagents.md:459` record the separate native-hint versus proxy-guidance owners; at `structure/05_gui-and-management-api.md:129` add the response-only recommendation and explicit-save-only canonicalization contract. No broader restructuring.
+
+### Complete field lifecycle / consumer audit
+
+1. **Creation:** policy module owns constant `text` and `revision`. Revision labels server guidance; the GUI validates a nonblank string, does not whitelist revisions, does not infer installed native capability from it.
+2. **Serialization:** GET at route `:234-254` and successful PUT at `:413-427` add the same nested object. `src/server/auth-cors.ts:259-263` JSON.stringify serializes it with the existing JSON content type. No new envelope, endpoint, auth path or DTO needed.
+3. **GUI hydration:** `Subagents.loadUltraMode` GET reads the object, retains both fields only if valid, and stores it with exact apiBase. `SubagentsWorkspace.tsx:47-60` forwards typed state; it needs no runtime logic change. The recommendation is not written to session-list cache.
+4. **GUI interaction:** `ultraOn` remains derived from nonblank stored hint (`SubagentDelegationSection.tsx:57-60`). Enable uses recommendation.text, Clear sends null, Restore only updates local draft, Save sends draft. Recommendation-only refresh must not remount the editor; stored-hint changes still do. While a new API source is pending, the previous source's editor/hint disappears and all mode writes are disabled.
+5. **Write input:** `/api/v2` accepts only the existing `multiAgentModeHintText?: string|null` for this behavior. Blank/non-string validation at route `:308-310` and unsupported-runtime preflight at `:335-339` remain before mutations. The recommendation object and revision are never sent back as configuration. The GUI PUT body at `Subagents.tsx:103-107` is JSON.stringify(patch).
+6. **Persistence:** route `:390` calls `setMultiAgentModeHintText`. Its capability check remains first. Exact legacy strings become recommendation.text; arbitrary custom text, including one-space variants, stays byte-identical; null stays null. `features.ts:951-1061` owns dedicated/inline/boolean TOML forms, sibling/comment/EOL preservation and atomic writes; `:485` owns string escaping. Persist only `features.multi_agent_v2.multi_agent_mode_hint_text`. Never persist text/revision as extra TOML keys: native config rejects unknown members.
+7. **Readback:** `getMultiAgentModeHintText` at `features.ts:935-937` uses `getV2StringField` at `:857`; no canonicalization on read. The successful PUT re-reads stored hint and independently returns recommendation. CLI `src/cli/v2.ts:138,151,167` reads or invokes the same writer, so CLI explicit writes also gain exact-legacy normalization without a CLI implementation edit.
+8. **Other consumers:** `gui/src/pages/Models.tsx:476-486,1116-1127,1244-1255` deliberately projects flag/mode/thread/hybrid fields and ignores recommendation; `dashboard-core-poll.ts:233-244` projects mode only; `use-dashboard-data.ts:609-615` sends mode only. These clients need no new required field. Existing synthetic missing-recommendation responses remain useful old-server fixtures. `ULTRA_MODE_PRESET` has only the local component and `subagents-ultra-mode.test.tsx` consumers found in the repository.
+9. **Native consumer boundary:** the stored hint is for native Codex sessions; OpenCodex's v2 request guidance does not read this field. Keep the v1 proactive constant and new recommendation as distinct owners rather than replacing one with the other. No capability claim or live native delegation behavior was tested here.
+
+### Regression activation
+
+Carry backend fixtures at pinned #3951 `codex-v2-gate.test.ts:52-57,434-446,1382-1412`. Independently pinned release strings must remain in tests; do not import the production legacy array, which would make deleting a compatibility entry delete its oracle too.
+
+Backend cases: each legacy input canonicalizes only on an explicit write; GET preserves exact TOML bytes; unrelated PUT preserves stored legacy; successful PUT returns canonical stored hint plus unchanged recommendation; re-save is changed:false; adjacent comments and concurrency limit survive; null clears; whitespace variants/custom text survive; unsupported binary leaves a combined request unchanged; existing inline, dedicated, boolean, CRLF, quoting and multiline-refusal tests remain active.
+
+Small additional hunks recommended in the same registered file:
+
+- Beside baseline API tests at `:1360`: PUT a custom string with leading/trailing spaces, newline, quotes and backslash; assert exact response hint and decoded TOML equality, same recommendation, and no persisted recommendation/revision keys. Assert a recommendation-only body is rejected without changing files, and recommendation extras beside a valid hint never replace the server constant.
+- Beside CLI test at `:1626`: `cmdV2(["mode-hint", legacy])` for both released values; read/status returns canonical text; re-save no change; custom one-space variant preserved; `--clear` removes only hint. Use `fixtureConfig`, temporary CODEX_HOME and `installModeHintRuntime(true)` with existing cleanup.
+
+GUI cases, pinned [subagents-ultra-mode.test.tsx:126-318](https://github.com/lidge-jun/opencodex/blob/db502d486c8d8de80c0143fcfd6f86238d1ce917/gui/tests/subagents-ultra-mode.test.tsx#L126): distinctive server text in exact PUT; missing/bad fields block install; existing custom text remains editable/clearable; Restore sends no PUT; Save sends one; A -> pending B hides A and blocks writes; B missing/malformed/valid selects correct behavior; late A response cannot overwrite B. Preserve recommendation-only refresh draft test in `multi-agent-guidance.test.tsx:154-182`. Extend malformed matrix with null, empty revision, omitted text/revision, and non-object values; no local preset fallback.
+
+Remote-only focused activation: `bun test tests/codex-integration/codex-v2-gate.test.ts`; from gui, `bun test tests/subagents-ultra-mode.test.tsx tests/multi-agent-guidance.test.tsx`. **NOT RUN here.** The existing test-layout entries are already present at `scripts/test-layout/layout.json:488,825` and `tests/fixtures/test-layout-expected.json:323,660`; additions to these existing files need no new registration. GUI test discovery already covers `gui/tests`. Main's full hosted gates remain authoritative.
+
+## UI proof: hosted bundle, synthetic API, existing browser (main-owned)
+
+The author's missing screenshot is **not a blocker**. Use the current workflow's existing artifact: [ci.yml at baseline](https://github.com/lidge-jun/opencodex/blob/9e1468d4b7a41b498ed2aca98507ada2c741afea/.github/workflows/ci.yml#L451). The `gates` job builds GUI when GUI changes, writes `build-commit.txt` / `build-gui-tree.txt`, and uploads `dashboard-preview-${github.sha}` (`:451-470`). No workflow edits are required for this source lane.
+
+Main's execution recipe:
+
+1. Select the hosted artifact covering the cumulative carry candidate. Verify run/event/head, archive artifact ID, build-commit, and build-gui-tree against the actual CI checkout. A pull_request merge SHA may differ from branch head; record that and prove the GUI tree matches the carried candidate. Do not relabel an old artifact as current.
+2. Unpack into isolated scratch; serve prebuilt JS/CSS/assets unchanged using a small static/synthetic HTTP server. No Bun/Vite/TypeScript build or product server locally. Fixture state lives in memory and receives only synthetic local requests.
+3. Serve the actual SPA at `/#subagents` (`gui/src/App.tsx:431`) and open Advanced (`SubagentDelegationSection.tsx:235-236`). Supply synthetic bootstrap metadata in served HTML: token `ocx_session_fixture`, CSRF `fixture-csrf`, browser/server origins equal the scratch origin. `gui/src/api.ts:90-105,139-149` requires these fields. This modifies only served bootstrap HTML, not bundled implementation. Implement the fixture bootstrap path if requested; never connect it to the user's daemon or credentials.
+4. Minimal page fixtures: GET `/api/subagent-models` -> `{available:[],chosen:[]}`; GET `/api/subagent-model-fallback` -> `{available:[],models:[],pollMs:60000}`; GET `/api/injection-model` -> `{multiAgentGuidanceEnabled:true,syncCodexSubagentDefaults:false,model:null,effort:null,efforts:[],available:[]}`. Give other app-shell reads safe synthetic responses with appropriate content types; record requested routes rather than silently forwarding unknown routes. Nonfixture writes must fail closed.
+5. GET `/api/v2` baseline -> `{enabled:true,multiAgentMode:"v2",keepNativeChatGptOnV1:false,multiAgentModeHintText:null,multiAgentModeHintRecommendation:{text:,revision:"proactive-trigger-v1"}}`. PUT validates/captures the JSON patch, changes only fixture hint/mode, and returns fresh full state plus `ok:true`. Subsequent GET echoes that state. Use the exact candidate text for screenshots, and a second distinctive text/revision for source-of-preset behavioral proof.
+6. Exercise: enable -> expected text PUT and editor; edit custom -> no PUT until Save; Restore -> draft changes without PUT; Save -> exact text PUT; Clear -> null; missing and malformed recommendation with/without existing hint; disabled v2; initial GET 503 then retry; PUT 502 -> error without false saved state. Keep request log assertions next to screenshots.
+7. Server switching: the actual App keys Subagents by sharedBase (`App.tsx:431`), while unit tests also cover in-place prop updates. Use two synthetic fixture sources through the existing connection UI if supported by the host; otherwise retain hosted component-test proof for the delayed A/B update and explicitly label that branch browser-NOT-RUN. Do not rebuild a special component harness locally or fake a source switch by altering product code.
+8. Capture the changed component at 1440, 1024, 768, 390 and dense 320 px widths; EN and KO long labels, light/dark and visible keyboard focus. Check textarea readability, label/button clipping and horizontal overflow. Save representative off/on/custom/old-server/error screenshots and viewport/state/request-log manifest. No need for decorative assets.
+
+Suggested main-owned scratch outputs: `ui-v2/source-manifest.json`, `ui-v2/requests.jsonl`, `ui-v2/en-desktop-on.png`, `ui-v2/ko-mobile-custom.png`, `ui-v2/old-server.png`, `ui-v2/error-retry.png`. This lane has created none of them. Screenshots prove the carried GUI rendering and fixture interaction; they do not prove real native delegation or TOML persistence. Backend hosted tests supply persistence evidence. Main can attach its observed images to the carrying PR; it need not wait for the author's images.
+
+
+Main decision: preserve original 23-file diff and all regressions. Suggested extra CLI/API test cases remain a targeted audit decision rather than mandatory scope expansion. Sync structure/03_catalog-and-subagents.md and structure/05_gui-and-management-api.md with the response-only recommendation and explicit-write behavior. Render QA uses the existing built artifact.
+
+## wp4 P refresh and artifact publication
+
+Previous wp3 D: PR3992 at3ceef0121 passed CI34181398746, target-check rerun34181398713, independent source/security audit and425page remote docs build. Proceed server-owned preset. Candidate5b60a4fa7+e5a6f35b8 was independently audited25files; all25preimage files on actual predecessor3ceef0121 equal candidatebase8000e2482. Preserve original contribution and both layers of agent guides.
+
+UI proof uses the immutable matching hosted dashboard artifact and reviewed auxiliary stdlib fixture at /tmp/ocx-bug6-prep-v2-01a07e9d/.tmp/ui-v2/server.py. No product server/build or actual account operations. Capture and inspect representative desktop/mobileEN/KO, valid/custom/restore/save/clear/missing/malformed/error cases; hosted tests retain in-place delayed-server-switch coverage. Publish only sanitized screenshot assets on an owned evidence branch via noverify push and link immutable commit URLs in this PR, so adding review evidence does not rewrite the tested product head. This evidence-only branch is not another product PR or native stack. Actual product delivery remains the one six-layer chain. Preserve asset provenance/buildGUI-tree checks and teardown proof.
+
+## C render foldback: narrow editor layout
+
+Real prebuilt GUI capture at320px shows an unbroken configuration-key hint clipped and the textarea compressed beside both actions;390px has no page overflow but editing space is unnecessarily narrow. Evidence ui-qa/ko-320-custom.png and ko-mobile-custom.png under ignored scratch. This is a bounded improvement of the changed preset surface, not a claim that the original PR introduced the old layout.
+
+MODIFY gui/src/styles-subagents-workspace.css only: add overflow-wrap:anywhere to existing delegation setting hints; give the preset editor a minmax(0,1fr)/auto/auto grid with min-width0 textarea; reuse existing640px media breakpoint to span textarea across a full first row and place two actions on the second row. NoJS, labels, fields or authority semantics change. Rebuild only remotely; compare head-stamped320/390/1440captures and DOMwidth/textareawidth, plus existing frontendgates. Retain prior functional UI request evidence becauseJS unchanged; verify a representative Save on finalrender. Main re-audits this CSS scope before B and captures finalartifact before publishing screenshotproof.
+
+## C verification foldback: bounded guard probes
+
+Run34183701289 timed out on macos1/2 after test-home-guard passed its unregistered-home case, then Bun reported a dangling process. Runtime root cause remains unproven (H1retainedchildhandles/H2syncwait/H3isolate state); source establishes an unbounded synchronous probe owner. This is a verification-harness correction, not a claim to fix Bun internals. No production guard behavior changes.
+
+MODIFY tests/ci-workflows/test-home-guard.test.ts only: convert runProbe and all its callers to awaited async Bun.spawn; reuse exported captureTestOutput from scripts/test.ts (its executable entry is import.meta.main gated, already imported by test-runner tests); preserve exact argv/cwd/environment filtering/fake homes and all existing assertions. Use existing watchdogMs(5000) below CI60s test ceiling, bounded execution/TERM/KILL/reap/output-drain stages with cleared timers and cleanup of probe scratch. Require natural exit0, no signal, complete output; timeout/nonzero/incomplete/unreaped is a thrown diagnostic, never a success marker. Record safe probe-boundary ID/PID/exit diagnostics without inherited environment. Add synthetic nonzero-exit and nonterminating-probe controls proving failures are reported and owned child gone; do not skip guards or extend CI deadlines. All product verification stays remote. If new diagnostics still stall/fail, investigate that boundary; passing the converted harness is not an upstream-root-cause claim.
diff --git a/devlog/_fin/260908_bug6_manual_stack/050_credit_alias.md b/devlog/_fin/260908_bug6_manual_stack/050_credit_alias.md
new file mode 100644
index 00000000000..30c38ee20b3
--- /dev/null
+++ b/devlog/_fin/260908_bug6_manual_stack/050_credit_alias.md
@@ -0,0 +1,29 @@
+# wp5: canonical reset-credit operation identity
+
+Historical phase record. Delivery is complete; see [071](071_delivery.md) and [072](072_final_proof.md) for terminal evidence.
+
+Depends on wp4 for the owner-requested delivery chain. C4; no live credit consumption. Source PR #3965 at `6c1477d19c7d1a77a1866cabfd2b4411f1a210d7` carries #3919 by luvs01. Revalidate both source heads and current dev before implementation; do not rewrite their branches.
+
+## Published patch to carry
+
+- MODIFY `src/codex/auth-api.ts` at the reset consume handler: `const identity` becomes `let identity`; after execute admission assign `identity = { ...identity, operationId: opened.operationId };`. Upstream dispatch and both durable settlement paths then share the canonical operation ID. Authentication, admission failures, account binding and terminal replay stay before this assignment.
+- MODIFY `tests/codex-integration/codex-auth-api.test.ts`: import the existing ledger opener, assert a settled alias replay consumes no additional credit, and construct truly pending canonical operations for thrown fetch, non-2xx and unknown-code alias failures. Assert the durable row becomes ambiguous while account key and canonical ID remain unchanged and terminal code remains null.
+- MODIFY `docs-site/src/content/docs/reference/management-api.md`: carry the source paragraph distinguishing unfinished alias joins, known terminal replay and new explicit intent after settlement.
+
+Retain `Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>` and original source commit references. Do not carry auto-redeem worker changes: #3970 is already on baseline.
+
+## Verification
+
+The final immutable three-file source range is `abb46a1599ec0d0bbfbe03905114178df92e67f5..62412d38606851f7cace76360f3c5737db9cae20`; the landed equivalent is `abb46a1599ec0d0bbfbe03905114178df92e67f5..402be7c1f88283eb8465c3aec8437ccecd2542ec`. These final pins supersede the initial intake head above. A live source-head mismatch requires renewed comparison before carry; the mutable PR files page is navigation, not patch authority. Each negative fixture begins pending, so it observes the changed failure-settlement path instead of rechecking an already ambiguous row. Existing no-operationId and ordinary terminal paths remain regression controls. Hosted CI runs the auth and ledger suites; local tests/typecheck/build/install are NOT RUN by owner instruction. A source/security reviewer verifies the exact carried head before merge. Existing source-PR CI failure is historical and must not be described as passing.
+
+All additional unpublished security analysis lives in ignored `.tmp/bug6-01a07e9d/credit-plan.md` and later audit artifacts. It must not be copied into this public unit.
+
+## wp5 P refresh
+
+Previous wp4 D certified PR3993 head727683f44 with CI34185870948, source/security/GUI audits, QA and remote docs. Proceed canonical alias carry. Live refresh supersedes the prepared pin: #3965 merged at03:17:07Z with head62412d386 and merge402be7c1f; prepared bdb9f4bfe+9eb44cfb4 passed two source/security audits. All three target files on actual predecessor727683f44 equal preparedbase d1f61e933; intervening V2/GUI/test-harness deltas do not modify this owner. Keep exact3filecarry and original attribution. No real credentials/resetcredits and no localproductcommands. Actual adoption equality and hostedCI remain required.
+
+## Verified landed disposition
+
+Current origin/dev402be7c1f contains the #3965 merge402be7c1f. Its three target files exactly equal prepared candidate9eb44cfb4 (git diff exit0); PR CI34181771859 passed at exacthead62412d386, including Linux4/macOS2/gates. Thus the source item is already landed, not a new product fix. The initial A narrative retained the old OPEN assumption; this fresh source/API evidence corrects it before B.
+
+NOOP for a new PR. Adopt the identical two contribution commits locally only as the prerequisite for wp6; preserve provenance and contributor credit. The final new recovery PR targets existing layer4 and explains the already-landed alias dependency in its base-relative diff. No duplicate fifth PR is created and no original branch is rewritten. c5 closes on live merged-state/CI/ancestry/file equality evidence; wp6 and final cumulative integration still run their full gates. The single product stack has five new PRs plus this independently landed sixth source item.
diff --git a/devlog/_fin/260908_bug6_manual_stack/060_credit_recovery.md b/devlog/_fin/260908_bug6_manual_stack/060_credit_recovery.md
new file mode 100644
index 00000000000..1fe1b8137e1
--- /dev/null
+++ b/devlog/_fin/260908_bug6_manual_stack/060_credit_recovery.md
@@ -0,0 +1,43 @@
+# wp6: manual reset recovery
+
+Historical phase record. Delivery is complete; see [071](071_delivery.md) and [072](072_final_proof.md) for terminal evidence.
+
+Depends on wp5 canonical operation identity. C4. Implements the user-visible contract in public issue https://github.com/lidge-jun/opencodex/issues/3973 . No real account actions or credit consumption are authorized by this development task.
+
+## File map and private implementation appendix
+
+- MODIFY `src/codex/auth-api.ts`: connect the authenticated manual operation with the existing quota-observation and routing-recovery ownership contracts.
+- MODIFY `src/codex/routing.ts`: reuse narrowly targeted recovery ownership rather than broad account-health clearing.
+- MODIFY `tests/codex-integration/codex-auth-api.test.ts` and `tests/codex-integration/codex-cooldown-recovery.test.ts`: mocked endpoint and ownership-race regressions using existing fixture conventions.
+- MODIFY `docs-site/src/content/docs/reference/management-api.md` and `structure/08_openai-provider-tiers.md`: document the resulting supported contract when the patch is public, without account examples or internal proof material.
+- NO CHANGE to persisted ledger schemas, auto-redemption policy, selected-account policy, GUI, or real credentials.
+
+The complete before/after design, exact current source anchors, threat model, reachable activation cases and observable negative assertions are recorded in ignored `.tmp/bug6-01a07e9d/credit-plan.md`, section "Layer 2", against baseline `9e1468d4b7a41b498ed2aca98507ada2c741afea`. This is a mandatory implementation appendix, not deferred planning. Repository AGENTS.md requires unpublished security working notes to stay in scratch, overriding public devlog placement. Both the A reviewer and B worker must read the appendix; loss of the appendix requires reconstructing and auditing it before B.
+
+## Acceptance and verification
+
+Only the matching account's eligible pre-existing cooldown may be recovered after confirmed reset and fresh supporting evidence. Ordinary successful requests, uncertain results and replay do not gain broader recovery authority. Existing unrelated scopes and caller selections remain intact. The private appendix enumerates the full mocked positive/negative matrix and claim cleanup requirements.
+
+Run no local product commands. Hosted CI must execute the affected auth, cooldown, quota and provenance suites; independent security review remains required. PR #3848 overlaps the flight interface: refresh before B and integrate any landed change without absorbing its unrelated registration behavior. New code belongs to this owned stack; do not modify other open PRs. Record privacy-safe outcome evidence here only after publication.
+
+## wp6 P refresh
+
+Previous wp5 D verified #3965 already landed in dev402be7c1f, exacthead62412d386CIpassed, and locally adopted identical prerequisite (926b3719f); no duplicatePR5. This recovery PR targets existing layer4 #3993 and identifies the already-landed alias prerequisite in its relative diff. Source3973 remains open. Fresh inventory found overlapping contributorPR3995 (e172453052bf7bbc4a0ae5aa24592982c0c64b15) and independent fallbackPR3997; the latter resolves3996 and is outside this goal. The earlier no-overlap narrative was incorrect and is superseded before B.
+
+Prepared recovery e6e081c09 plus repair a87a3f624 passed independent security and behavior audits. The private repair synthesis and updated handoff under ignored scratch resolve main-publication ordering and positive refresh provenance; never copy security working analysis into this public unit. All six target preimages on actual predecessor926b3719f equal auditedbase9eb44cfb4. Revalidate the unchanged candidate across intervening V2/GUI/testharness context, then adopt. All mocked regressions, current-head hostedCI, privacy, finalfullcohort proof and source-item closeout remain required. No localproductcommands or realcreditactions.
+
+## Concurrent source reconciliation in P/A
+
+Review new3995 against the prepared candidate before adoption. Preserve originalcontributor credit and include its useful language/CLI docs or regression cases when source comparison warrants. Existing prepared recovery provides bounded claims/publication/provenance invariants; no competing implementation is accepted solely from prior green claims. Comparative security/behavior source reviews are in progress, all notes remain scratch. No productdelta forwp6 has been adopted yet.
+
+## Consolidated source decision
+
+Retain audited recovery e6e081c09+a87a3f624 and consolidate contributorPR3995 rather than creating competing deliveries. Comparative security review retains its PASS; detailed algorithm findings remain private in credit3995Comparison.md. Keep pause/reauth eligibility and existing background lease ownership conservative and document that recovery can remain pending under those conditions. #3997/#3996 stays outside scope.
+
+Additional MODIFY paths: docs-site/src/content/docs/ko/reference/management-api.md and docs-site/src/content/docs/reference/cli/providers-accounts.md, carrying the matching contributor guidance with parity to the final conditional recovery contract. This expands six unique files to eight. Do not duplicate the fuller English API paragraph. Adapt PR3995 tests into the existing auth-api test: two cold-main reset/already_redeemed cases without prior listing/reconciliation, bogus consume99 versus freshWHAM1; strengthen the existing saturation test with pre-existing shared cooldown, one consume, zero usage and retainedcooldown; adapt the two-old-flight/current-generation convergence scenario to assert fresh fourthdispatch completes before oldresponses, then oldresponses cannotoverwritefreshquota or recoveredcooldown. Preserve and await every deferred fixture cleanup. No new testfile, account-store schema or CLI runtime change.
+
+Carry sourcee172453052 with Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com> in the adaptation commit and final PR body; describe exactly which tests/docs are adopted. B includes local candidate adoption and these bounded test/doc additions; independent interdiff review and exacthead hostedCI remain mandatory. No local tests/install/typecheck/build or realcredits.
+
+## C fixture foldback
+
+CI34188041321 caught a shared401-recovery budget leaking between fake-home cases: prior manual-a selfrefresh spends generation2, and the next case creates a different generation2 in a newhome but doesnotreset the module budget. The early spent-budget refusal prevents the intended external-replacement replay. MODIFY only the existing auth-api test: import/call resetQuotaRecoveryForTests in beforeEach/afterEach, assert empty budget at the negative-case start, observe real force-refresh provenance, and KEEP expectedfreshremaining2, replay URLs and cooldown-preservation assertions. No production relaxation. Also use existing watchdogMs(10000) and60souter ceiling for the new convergence fixture; its current run passed, so this is convention/contended-runner safety, not increasing a failing behavioral timeout. Source/interdiff review and newexactheadCI are required.
diff --git a/devlog/_fin/260908_bug6_manual_stack/070_integration.md b/devlog/_fin/260908_bug6_manual_stack/070_integration.md
new file mode 100644
index 00000000000..8a2dac44822
--- /dev/null
+++ b/devlog/_fin/260908_bug6_manual_stack/070_integration.md
@@ -0,0 +1,61 @@
+# wp7: hosted verification and integration
+
+Historical phase record. Delivery is complete; see [071](071_delivery.md) and [072](072_final_proof.md) for terminal evidence.
+
+Depends on wp1–wp6. The owner explicitly requested a single manual branch chain. This cycle changes only its delivery records and evidence; a discovered product defect is assigned an audited repair cycle before integration continues.
+
+## File changes
+
+- MODIFY this unit's `000_plan.md`: replace in-progress outcomes with exact source commit, PR, run IDs, tested heads and terminal dispositions; record failed/skipped checks separately.
+- NEW `071_delivery.md`: six-row original-to-carried PR mapping, attribution, pinned GitHub evidence, active branch/base topology and merge result per layer. Store no account identifiers or private payloads.
+- NEW `072_final_proof.md`: fetched dev SHA; per-layer ancestry command results; final candidate tree and landed tree comparison; unchanged pre-existing-file fingerprint verification. If a merge commit contains concurrent changes, isolate and explain each difference rather than claiming whole-tree equality.
+- MOVE this completed unit to `devlog/_fin/260908_bug6_manual_stack/` only when all outcomes are terminal. Evidence generated before moving records both paths. Do not move other units.
+- GUI screenshot files, if needed, use the existing `.github/pr-assets/` convention after verifying the generated image contains synthetic settings only.
+
+## Exact delivery actions
+
+1. For each nonempty candidate use a new owned `codex/bug6-01a07e9d-*` branch. Bottom base is dev; each upper base is the prior owned branch. Preserve author trailers and satisfy every section of `.github/PULL_REQUEST_TEMPLATE.md`.
+2. Commit with `git -c core.hooksPath=/dev/null commit`; push with `git -c core.hooksPath=/dev/null push --no-verify`. No install, test, typecheck or build hook runs locally.
+3. Read each PR's current head/base and native `stack` field. A native membership conflict is inspected without mutating membership. Our newly created ordinary PRs must remain manual.
+4. Inspect `gh pr checks` and matching workflow runs. Before landing obtain final candidate `ci.yml` `workflow_dispatch` with `lane=all` as well as required PR checks. Bind conclusions to `head_sha`, event and run attempt. Retry failed jobs only after investigating the actual failure and ensuring it does not hide a product regression.
+5. For the preset UI, download the hosted `dashboard-preview-*` artifact from the verified head. Verify `build-commit.txt` and `build-gui-tree.txt`; serve the prebuilt bundle with synthetic API fixtures on a disposable loopback port; observe preset activation/restoration and server-switch behavior in a browser; capture/read the screenshot. No local product compilation. Existing browser driver only, no installation.
+6. Refresh MAINTAINERS.md, live actor permission, reviewer objections and security evidence. Record maintainer integration in the owned PR body. The repository deletes merged head branches: inspect direct children and retarget our next child to dev immediately before merging its parent, so automatic deletion cannot close it. Land only the bottom PR with `--match-head-commit`; verify the resulting integration head and CI evidence before advancing. Never merge an upper PR into its parent branch as if that landed it in dev. Do not change repository settings or unrelated children.
+7. Fetch dev after each merge and prove the merged commit is an ancestor. At final integration compare actual trees against the final certified candidate, including any explicitly reviewed concurrent dev changes.
+8. Refresh each original item and mark closed only if its entire user-visible bug is resolved by the landed tree. Preserve unresolved residuals as open; report the exact residual rather than treating overlap as duplication.
+
+## Activation and observation
+
+- Failed/queued/cancelled hosted job: inspect actual run/head; no merge until required evidence is successful.
+- Base advances: recompute integration tree and obtain fresh evidence; old SHA checks are historical.
+- A maintainer objection remains: resolve its concrete finding or obtain withdrawal before merge.
+- A source PR lands concurrently: verify its actual delta and remaining contract; use an evidence-backed NOOP rather than reapplying it.
+- UI stale-server or malformed-recommendation fixture: preset install disabled; custom edit/clear retained; no cross-server write.
+- Final condition: all six source contracts mapped to landed results; no destructive changes to user files, service state or credentials.
+
+## Validation limits
+
+Local product tests, installs, typechecks and builds: NOT RUN by owner instruction. Hosted tests and independent source audits provide product evidence; docs-only filesystem/link/whitespace checks provide document evidence. Neither substitutes for the other.
+
+## wp7 P refresh
+
+Previous wp6 D:6904ecd9c passed CI34188893148 and source/security/interdiff audits; coldmain, busy, same-tick replacement and converged-flight regressions passed; docs425pages plus renderedlink/KOparity passed. Latestdev402be7c1f is pinned for integration. Read-only merge-tree predicts conflicts only in reference/management-api.md and codex-auth-api.test.ts because dev already contains the canonicalalias prefix. Both dev versions exactly equal our adopted alias predecessor9eb44cfb4; resolve those two to our current versions, which include that prefix plus the audited recovery. No pre-existing userfile overlaps the incomingdevdelta.
+
+Merge402be7c1f into the topbranch with hooksdisabled, preserving all other incomingfiles. Record exactresolvedtree, recheckcurrentdev, publishnoverify, and dispatchci.yml lane=all on that exacthead. NewPRchain is3986→3991→3992→3993→4002;3965alreadymerged. All original candidateheads retain passingPRCI; any rewrittenhead gets freshproof. Refresh each target/head/membership/review/CI before its separately authorized ordinary merge. Use merge commits to preserve ancestry/attribution, retarget each nextchild todev, and prove resolved integration content is covered by the certifiedtop. Current-headrequiredchecks and source/security duties remain separate.
+
+After productlanding, close only satisfied sourceitems, including superseded3995 aftercore3973lands; preserve3997. Archive this unit with an evidence-only closing PR if needed, so completedrecords do not change the tested product tree. Verify that closingdelta is documentation-only and receives its properCI; retain exact product-tree equivalence to the full-matrix candidate rather than attributing skipped productjobs to passingexecution. No release/main/preview/deployment changes.
+
+## Final CI repair amendment
+
+Full run34190287787 at f1b436324 failed Windows3/6: the first restart-help correctness test returned an unobserved exit status after its fixed10s synchronous subprocess bound. Other observed shards passed; wait for the complete run before deciding whether any additional repair is needed. The source investigation does not establish a Bun defect or a startup latency cause.
+
+Modify only `tests/cli/cli-restart-health.test.ts` for this repair. Replace synchronous spawning with awaited Bun.spawn, existing captureTestOutput and watchdogMs(10000). Preserve all eight command tests, arguments, private homes, output assertions and legitimate health exit1. Independently bound execution, TERM grace5s, KILL reap2s and output drain1s; clear timers and keep timeout, signal, rejected observation, incomplete output and unreaped child as failures even after eventual exit0/1. Use an outer cleanup envelope below the existing60s CI ceiling. Keep child ownership and avoid deleting an unreaped child's private home. Emit safe stage/PID/exit diagnostics without inherited environment or credentials. Add small controlled wrapper regressions for sticky timeout, incomplete output at0/1, unreaped child and spawn/observation errors; reuse capture-owner coverage for its internals. No production CLI or workflow timeout changes, assertion removal, skip, retry loop or local product execution.
+
+An independent plan audit precedes implementation; an independent patch audit precedes publication. Publish the repaired top head with hooks disabled and --no-verify, obtain fresh PR checks and a new full lane=all dispatch on that exact SHA, and verify the original Windows lane. Passing results establish that head's observed outcomes, not the historical root cause. The final docs build runs remotely on the integrated docs tree. Preserve failed-run evidence and all prior user files.
+
+## Last-layer ancestry repair
+
+Four lower PRs landed with exact predicted trees; current dev is `74f62f9c2914ead2fba474aa97734e322251bd46`. Candidate `f80f39d20e8395901d3b62758d118ea3a559a9f4` passed PR CI34193213502 attempt2 and full CI34193218874 attempt2 (26 named jobs and execution steps). Each investigated macOS retry was limited to its failed/cancelled job; prior failures remain recorded.
+
+GitHub refuses the last PR as conflicting even after its base metadata was refreshed to actual dev; ordinary GraphQL and REST merges both refused. Local merge-tree remains clean and equals candidate tree `1b3bd117702b86da5810b1ad21988a2c60df6d17`. The histories have two merge bases, `727683f44e9f1daa9b6b1e2dbf93167e4ce30cc1` and `402be7c1f88283eb8465c3aec8437ccecd2542ec`; this explains the need to simplify ancestry without claiming a proven server implementation cause.
+
+Merge that exact current dev into the owned top branch with hooks disabled. Before adding this record, require exact candidate-tree equality; no product files may change. Include only this integration record in the merge commit. Independently audit both parents and the exact record-only tree delta, preserve all 30 user files, push no-verify, and obtain fresh PR and full lane=all CI on the new head before merging #4002. Preserve the earlier successful candidate evidence as historical; do not label it new-head execution. All source closures and final archive duties remain pending until landing.
diff --git a/devlog/_fin/260908_bug6_manual_stack/071_delivery.md b/devlog/_fin/260908_bug6_manual_stack/071_delivery.md
new file mode 100644
index 00000000000..0598a4b4011
--- /dev/null
+++ b/devlog/_fin/260908_bug6_manual_stack/071_delivery.md
@@ -0,0 +1,35 @@
+# Delivery and verification ledger
+
+Certified final head `5d5d35756b9b672aecf10a64be0db1f7afc144ae` has tree `5fba579b0d10183e921657dbcf4edbd166c20ec7`. All five owned PRs merged bottom-up, and actual product dev `9ad218a9bdd34ee33004c35706d78396bf02eef2` has exactly the same tree.
+
+| Owned PR | Certified head | Actual dev merge | PR CI run / attempt |
+| --- | --- | --- | --- |
+| #3986 | `d1f61e933b0cde3df3862baed65546a5cf81066f` | `7b2223776450804a6b8a4509a115dd42ee1b9c40` | 34178540141 / 1 |
+| #3991 | `00eb47886690e7b24b0eed69b6d870c33ceade62` | `7730f666ee1acabe2cd7729ec56f4c53149d926c` | 34180674115 / 1 |
+| #3992 | `3ceef0121712b290c3d4443e9fc3f0a04cecead6` | `74292a21e6d504960ef753b403341498fd5bfe30` | 34181398746 / 1 |
+| #3993 | `727683f44e9f1daa9b6b1e2dbf93167e4ce30cc1` | `74f62f9c2914ead2fba474aa97734e322251bd46` | 34185870948 / 1 |
+| #4002 | `5d5d35756b9b672aecf10a64be0db1f7afc144ae` | `9ad218a9bdd34ee33004c35706d78396bf02eef2` | 34198172044 / 1 |
+
+Final full `ci.yml` workflow_dispatch lane=all: [34198186409](https://github.com/lidge-jun/opencodex/actions/runs/34198186409), attempt2. All 26 named jobs and mandatory execution steps were verified successful. PR #4002 CI [34198172044](https://github.com/lidge-jun/opencodex/actions/runs/34198172044) passed at the exact final head. A successful attempt2 summary includes successful jobs retained from attempt1; it does not mean those jobs executed again.
+
+## Failures and bounded repairs
+
+- Go's initial synthetic userinfo fixture failed privacy scanning; fixture construction was corrected before candidate CI34178540141 passed.
+- The preset's home-guard subprocess stalled on macOS. Its bounded execution/reap/capture repair retained all10 original fixtures and38 assertions; CI34185870948 passed. The native cause remains unproven.
+- Recovery CI34188041321 exposed shared test-budget state between synthetic homes. Isolation was corrected without dropping assertions; CI34188893148 passed.
+- Full34190287787 at f1b436324 failed Windows restart-help with an unobserved synchronous child exit at10s. The bounded asynchronous CLI harness retained8 original tests/18 assertions and added10 lifecycle controls. Independent source review and later Windows execution passed.
+- At f80f39d20, PR34193213502 attempt1 hit a macOS job cancellation at an unchanged injection-lock test boundary. Same-head full macOS1 passed the same tests; one investigated job-only rerun passed. Full34193218874 attempt1 separately timed out in an unchanged Copilot cancellation test; the same-head macOS shard passed that case. One control-only rerun passed, with21,805 main-suite passes and0 failures. Scheduler, ordering and native causes were not established.
+- After four parents landed, GitHub refused the last merge while local merge calculation was clean. Two common ancestors were observed. The final ancestry merge incorporated actual dev74f62f9c; independent review proved that only the070 record changed from the already certified f80 product. Fresh exact-head CI was obtained; no old run was relabeled as execution on the new head.
+- Full34198186409 attempt1 failed Windows3 cleanup: EPERM removing a fixed test directory in afterEach caused subsequent setup/cleanup failures. Identical product files had passed the preceding Windows3 run. Independent inspection supported one failed-job-only diagnostic rerun. Job101977029312 passed3,304 tests with0 failures; the affected TTL case passed172.72ms. The handle/permission owner remains unknown. No test threshold or product code changed for this retry.
+
+Passing jobs were retained during these job retries, not rerun. Failed/cancelled attempts remain historical evidence. No root-cause or flake-eradication claim follows from a successful retry.
+
+## Attribution, UI and documentation
+
+The carried Go intent retains `Co-authored-by: jpierrevd <265811239+jpierrevd@users.noreply.github.com>`. V2, alias and adapted #3995 coverage/docs retain `Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>`. Merge commits preserve their history. The lossy mixed-ciphertext filtering proposal from #3838 was declined; fail-closed behavior remains.
+
+Dashboard tree `b0bc09ba867906375e52cf0180caa4ea4ea95bea` equals the inspected artifact tree. Hosted artifact10039810403 from34183701289 was observed against a synthetic API: enable, exact custom Save, draft-only Restore, Clear, malformed/missing recommendation, error/retry, server switching,320/390px and desktop layouts, and keyboard focus. Two independent reviewers accepted it. Immutable screenshots are in [#3993](https://github.com/lidge-jun/opencodex/pull/3993), evidence commit924327cdd71a14a0aea1936e4c5e1f6b6b660438. Owned fixture/browser ports were closed.
+
+Docs tree `7041e912691a5150893fbf4f782734c94e49d056` equals the remotely built integrated tree. Bun1.4.0 / Node24.20.0 frozen install and build on isolated macmini-cf scratch produced425 pages; rendered CLI/API anchor and English/Korean recovery text were checked. Archive SHA-256 a51cdbd83f409472defcb7758873734edba167f116a17869ec345366e0e9063d. An early incomplete-transfer attempt was excluded from passing evidence. No docs deployment occurred.
+
+Local product tests/install/typecheck/build: **NOT RUN**. This archive changes only this unit's Markdown records; its own metadata/CI/privacy verification is separate from product execution.
diff --git a/devlog/_fin/260908_bug6_manual_stack/072_final_proof.md b/devlog/_fin/260908_bug6_manual_stack/072_final_proof.md
new file mode 100644
index 00000000000..77631244877
--- /dev/null
+++ b/devlog/_fin/260908_bug6_manual_stack/072_final_proof.md
@@ -0,0 +1,23 @@
+# Actual product landing proof
+
+Verified 2026-09-08. Final candidate `5d5d35756b9b672aecf10a64be0db1f7afc144ae` and fetched product dev `9ad218a9bdd34ee33004c35706d78396bf02eef2` have identical tree `5fba579b0d10183e921657dbcf4edbd166c20ec7`. `git diff --exit-code` returned0. Each owned head and actual merge commit is ancestral to fetched dev; #3965's existing402be7c1f merge is also ancestral.
+
+| PR | Actual merge commit |
+| --- | --- |
+| #3986 | `7b2223776450804a6b8a4509a115dd42ee1b9c40` |
+| #3991 | `7730f666ee1acabe2cd7729ec56f4c53149d926c` |
+| #3992 | `74292a21e6d504960ef753b403341498fd5bfe30` |
+| #3993 | `74f62f9c2914ead2fba474aa97734e322251bd46` |
+| #4002 | `9ad218a9bdd34ee33004c35706d78396bf02eef2` |
+
+Before each merge, current head/base, actor admin permission, maintainer roster/reviews, CI, native membership and direct-child inventory were refreshed. The dev-only maintainer-integration decision and exact verification were recorded in each owned PR body. No maintainer change request remained. Independent technical/security review duties were retained; this was not self-approval. Native stack membership was empty.
+
+The first four actual merge trees matched their serial predictions. GitHub's final merge refusal was repaired with an independently audited ancestry merge; the final actual dev tree then matched the newly certified head exactly. Full CI34198186409 attempt2 verified all26 named jobs and mandatory execution steps; PR CI34198172044 attempt1 passed. Earlier failed attempts are recorded in071.
+
+Sources3838/3944/3951 are closed, issue3907 is completed, and3965 was already merged. After actual4002 landing, issue3973 was completed and3995 was closed as consolidated. The scope excludes3997/3996. Thirty original user files matched their pinned SHA-256 values with0 missing and0 mismatched; they were excluded from all commits. Scratch/evidence directories remain untracked.
+
+## Archive-only completion record
+
+This closing change moves exactly this unit from `devlog/_plan/260908_bug6_manual_stack/` to `devlog/_fin/260908_bug6_manual_stack/`, updates its terminal records and adds this proof. Product content is unchanged. The closing PR and session receipt verify the exact old/new paths, regular-file modes and blob IDs against the reviewed delta, plus closing CI/metadata and a remote privacy scan. Documentation-only skipped product jobs are NOT RUN, not passing product executions.
+
+The closing PR's own merge SHA cannot be embedded in the commit that creates it. Its observed post-merge ancestry and exact record-only delta are verified after landing in the final session receipt and delivery report, rather than predicted here.
diff --git a/devlog/_fin/260908_c248_individual_fixes/000_plan.md b/devlog/_fin/260908_c248_individual_fixes/000_plan.md
new file mode 100644
index 00000000000..3dca83c8cbe
--- /dev/null
+++ b/devlog/_fin/260908_c248_individual_fixes/000_plan.md
@@ -0,0 +1,33 @@
+# Lane C: independently reviewable 2.48 preparation fixes
+
+Scope: satisfy-spec HOTL requested by the owner on 2026-09-08. Goal: independently land #3953, #3899 and the timezone-only part of #3950 into dev, retaining original authors. This returned/stored roadmap is the memory artifact. Baseline dev: `514350e6f79ed4539378388bc39d3fc79ff2c70c`. No resource budget was specified; native host limits apply. Tool scope: local Git/source/artifact checks, GitHub repository/Actions, and explicitly authorized A/B coordination; astra high read-only auditors. No local product tests, typechecks, builds or dependency installation, including incidental Git-hook execution. Use per-command `git -c core.hooksPath=/dev/null` for mutating Git operations, and `push --no-verify`; do not modify shared Git configuration.
+
+## Work phases and ownership
+
+`roadmap` (this docs-only full PABCD) precedes three independent delivery cycles: `privacy`, `release_notes`, `timezone`. `reconcile` depends on those deliveries and lane B's JWT evidence. The processing order is scheduling, not a code dependency: each delivery remains a separate dev-targeted PR. Reuse original #3953 if unchanged and reviewable; carry #3899 onto current dev if needed; timezone gets a new PR sourced from only commit 1d8f6ff7e8d48f33c3ce7a1b7118068754bbbe83. Never create a combined code-delivery PR or squash different bugs together. Local roadmap/outcome commits stay on this coordination branch until a separate documentation-only closeout is appropriate. A separately audited evidence-only workflow branch may add supplemental hosted platform proof without entering any delivery PR or changing its required CI.
+
+## Boundaries
+
+No JWT changes, provider/routing work, main/preview promotions, version changes, deployments, release execution, live-account/service probes, history rewriting or public reproduction of removed material. Confidential investigation stays in ignored `.tmp/c248/`. The public docs describe only approved correction scope, not sensitive values. No new runtime type/enum/field is added, so creation/serialization/deserialization/consumer field-chain work is N/A.
+
+## Verification contract
+
+The roadmap uses actual file and source-object inspection plus `git diff --check` (run in this checkout before the roadmap close). Delivery uses the repository's existing hosted CI on each PR's current head; source-sensitive suites and the actual workflow scope must be checked. Local product commands are explicitly NOT RUN. No blind retries, cancelled/skipped/pending-as-pass, broad test weakening or artificial screenshots. A docs-only scope check is not a product-suite pass. Head rewrites require fresh current-head evidence. If dev moves, classify the actual delta and do not claim an unexecuted integration tree was tested. Before merge, validate required gates and exact head; preserve unrelated destination changes and prove the landed source diff.
+
+## Integration coordination
+
+The active A and B workstreams agreed on `/ocx-248-dev-merge.lock`: atomic mkdir, owner.json with sessionId/pid/hostname/PR/SHA/acquiredAt, owned only from final refresh through landed verification. Never hold it while waiting for CI or delete another owner's lock. This is a cooperative serialization convention, not a security boundary; an uncooperative actor can bypass it. The main thread resolves collisions, missing permissions or contradictory evidence without expanding worker scope; a new worker slice requires a plan amendment, and two distinct failed workers return the slice to main.
+
+Use the repository PR template and MAINTAINERS.md. Explicit maintainer integration is allowed only after checking live identity/role, outstanding objections, required CI, and required security review. Existing PR head authors remain attributed; carried commits use cherry-pick provenance and a Co-authored-by trailer surviving squash. After landing, verify merge SHA ancestry, actual file delta, destination preservation, and authors. Close original carry PRs only then. #3950 stays open until both timezone and B JWT fixes are proven on dev; C owns final closure. A/B status contributes to a readiness report, not release authority.
+
+## Stop and outcomes
+
+DONE requires all three delivered/proven already present and #3950 reconciled; final report lists source PR, delivery PR, landed SHA, actual CI results, authors and residuals. A blocked item does not stop independent work. Missing authority or unsafe evidence is unresolved, not a successful criterion. Read goalplan/ledger after each D and continue remaining cycles. Scope does not include a fixed cost/time budget or new paid service purchases.
+
+## Roadmap audit and completion
+
+Independent astra high roadmap audit: PASS, no blocking findings. The timeout prose was aligned with the dedicated child marker used in the exact patch. The roadmap-only check is Git diff whitespace plus independent source/semantic audit; no product suite was run. Next cycle: adopt and validate the unchanged #3953 correction.
+
+## Final reconciliation
+
+Privacy and release-note cycles completed before the timezone cycle. All delivery evidence and residuals are in050_outcome.md. Original3950 closed after both BJWT and Ctimezone landing proof. This unit archives to_fin through a separate docs-only PR; no product commit is combined with this record.
diff --git a/devlog/_fin/260908_c248_individual_fixes/010_privacy.md b/devlog/_fin/260908_c248_individual_fixes/010_privacy.md
new file mode 100644
index 00000000000..6e22b7a5f20
--- /dev/null
+++ b/devlog/_fin/260908_c248_individual_fixes/010_privacy.md
@@ -0,0 +1,19 @@
+# Phase privacy: adopt the exact #3953 correction
+
+Source SHA: 05fd82807b4a0014f84b9d74d05b70a3591cb574. Source URL: https://github.com/lidge-jun/opencodex/pull/3953
+
+MODIFY the two existing records under devlog/_plan/260904_provider_quota_refresh/: 030_wp3_live_verification_and_pr.md and 031_live_verification_record.md. DELETE assets/030_accounts_refresh_button.png and assets/040_accounts_refresh_result.png. The exact after-content is the existing source SHA's Git blobs, retrieved by `git show :`; do not copy removed values into this plan. The two deletions and two post-image blobs define the complete executable patch; no other path changes. Reuse the unchanged existing PR if its head still matches; otherwise refresh and re-audit instead of force-pushing the contributor branch.
+
+Acceptance: both asset paths are absent in the candidate tree; no retained Markdown literal reference points to them; the isolated-instance and real forced-read evidence remains; the cleanup statement still says moved to Trash, never permanent destruction. Do not open/display either removed PNG. Independent privacy/semantic review checks this exact source. Current-head hosted PR scope check and aggregate must complete, with all skipped jobs described as skipped. Inspect full candidate delta and workflow equality before approving a fork run. No public operating values are recorded in new artifacts. Public documentation is the SoT target; history cleanup is out of scope.
+
+Rollback: retain source/landed SHAs, but do not automatically reintroduce removed captures; any rollback needs explicit privacy assessment. Completion uses actual merged PR state/merge SHA, touched-path equality, unchanged destination paths and author attribution.
+
+Execution decision: use a maintainer-owned carry PR with the identical source commit. The contributor checklist asks for local-CI attestation that this run cannot truthfully supply under the no-local-product-check instruction; no source rewrite or new behavior is needed. Original #3953 remains open until the carry lands, and its author is preserved in the squash trailer.
+
+C review repair: CodeRabbit requested American-English afterward at the existing isolation sentence. Accepted one-word correction in 0ed232d5c, with surrounding historical facts unchanged. Previous CI 34166442230 passed 3 scope/aggregate jobs and skipped 10 product jobs; it does not certify the new head. The new head must be checked before landing.
+
+Source refresh correction: original #3953 advanced to ca21efd29730086ede902c4701124893ce58b404 before closure. It remains OPEN; any earlier closure claim in the operational task log was premature and has been corrected. The initial carry is already landed and must not be rewritten. Audit and carry the incremental 05fd828..ca21efd2 delta as a separate privacy follow-up: MODIFY 000_plan.md, 030_wp3_live_verification_and_pr.md, 031_live_verification_record.md under the source unit; DELETE assets/010_meta_usage_quota.png and assets/020_usage_refresh_result.png. Exact post-images are sourceca21efd2 blobs, retaining the landed afterward spelling. The complete delta is retrieved with git diff05fd828..ca21efd2 scoped to those five paths. Acceptance: one retention rule covers both Accounts/Usage, textual behavior and Trash historical outcomes remain, no PNG inspected, all four capture assets and all scoped old references absent, current-head hosted scope checks and fresh source/author/landing proof.
+
+Review synthesis: #3959 exposed two issues. Accepted the source plan/actual isolation mismatch and corrected both old restart sites so completed scratch evidence supersedes the working-service restart plan (fc6b07eaf). Declined history purge as explicitly outside owner scope; the historical reachability residual is preserved, not claimed fixed. Both review threads have documented dispositions. No runtime operation was performed. Latest head needs fresh hosted scope CI.
+
+DONE: initial carry#3955 landed9c54000c9 and follow-up#3959 landed01c23aedc. Current-head hosted scope CI34167651789 success3/skipped10, independent final privacy audit PASS, all known review findings dispositioned; automatic rereview was pending at merge and not counted as successful. Both landed trees, parents, dev ancestry and actual author trailers verified. Original#3953 closed at refreshedca21efd2. History purge remains explicitly outside scope.
diff --git a/devlog/_fin/260908_c248_individual_fixes/020_release_notes.md b/devlog/_fin/260908_c248_individual_fixes/020_release_notes.md
new file mode 100644
index 00000000000..de8b38e7012
--- /dev/null
+++ b/devlog/_fin/260908_c248_individual_fixes/020_release_notes.md
@@ -0,0 +1,144 @@
+# Phase release_notes: exact leading enforcement marker normalization
+
+Source PR #3899, source SHA 4d6896cd0bd62434b4703a1956fe57a99cd4959a. MODIFY the three files below. Preserve/reuse the source PR's related numbered implementation record if carrying its whole commit; it is documentation for this same bug, not another feature. Security review covers title text handling only: no workflow, command dispatch, credentials, publishing or release execution change. SoT: structure/06_docs-and-release.md.
+
+Activation/acceptance: prefixed generated and carried notes lose only the exact leading marker in summaries and full changelog; conventional scope grouping and attribution remain; unrelated bracketed/nonleading/near-match markers remain. Hosted CI must execute tests/ci-workflows/release-notes.test.ts (via the existing shard manifest) plus required gates. Explicit security review is recorded before maintainer sponsorship/integration. No local tests or typecheck are run. Rebase/carry applies only to our branch, uses original author and -x/Co-authored-by, and exact current-head checks. Issue #3895 closes only after verified dev landing. One independent revert restores only this bug's diff.
+
+Exact source patch follows; refresh against latest dev at its P phase:
+
+```diff
+diff --git a/scripts/release-notes.ts b/scripts/release-notes.ts
+index 16627f5f9..d0a58a043 100644
+--- a/scripts/release-notes.ts
++++ b/scripts/release-notes.ts
+@@ -546,8 +546,14 @@ export function parseGeneratedNotes(body: string): ReleaseNoteCategory[] {
+ const CONVENTIONAL_COMMIT_PREFIX =
+ /^(?:feat|fix|docs|chore|refactor|perf|test|build|ci|style|revert|merge|release)(?:\(([^)]+)\))?:\s*(.+)$/i;
+
++function stripPrEnforcementPrefix(title: string): string {
++ const text = title.trim();
++ const prefix = "[WRONG BRANCH] ";
++ return text.startsWith(prefix) ? text.slice(prefix.length).trim() : text;
++}
++
+ export function cleanPrTitle(title: string, prNumber: number | null = null): { scope: string | null; text: string } {
+- let text = title.trim();
++ let text = stripPrEnforcementPrefix(title);
+ let scope: string | null = null;
+ const prefix = CONVENTIONAL_COMMIT_PREFIX.exec(text);
+ if (prefix) {
+@@ -689,7 +695,7 @@ export function renderReleaseNotes(input: {
+ changelog.push(`Full Changelog: https://github.com/${repo}/compare/${from}...${to}`, "");
+ }
+ for (const pr of allPrs) {
+- changelog.push(`- #${pr.number} ${pr.title.trim()} @${pr.author}`);
++ changelog.push(`- #${pr.number} ${stripPrEnforcementPrefix(pr.title)} @${pr.author}`);
+ }
+ parts.push(changelog.join("\n"));
+ }
+diff --git a/structure/06_docs-and-release.md b/structure/06_docs-and-release.md
+index 8c6149802..886c8fc04 100644
+--- a/structure/06_docs-and-release.md
++++ b/structure/06_docs-and-release.md
+@@ -227,6 +227,11 @@ so stable notes are the aggregate of their preview train. The raw commit dump is
+ intentionally gone — non-PR commits stay reachable via the Full Changelog compare link when
+ that link is available.
+
++Both summary bullets and full-changelog titles strip the exact leading `[WRONG BRANCH] `
++enforcement marker. Other bracketed text is preserved. Summary bullets still remove conventional
++commit prefixes and group by scope; full-changelog entries keep those conventional prefixes,
++PR numbers, and author attribution. This normalization does not change PR-target enforcement.
++
+ The deterministic renderer produces the structure but not curated prose. Maintainers who want
+ the OpenAI-style grouped summaries can run the optional local polish step against the rendered
+ body (needs an OpenAI-compatible API key):
+diff --git a/tests/ci-workflows/release-notes.test.ts b/tests/ci-workflows/release-notes.test.ts
+index 11196108d..d27036008 100644
+--- a/tests/ci-workflows/release-notes.test.ts
++++ b/tests/ci-workflows/release-notes.test.ts
+@@ -455,6 +455,20 @@ describe("rewriteTakeoverCredits", () => {
+ });
+
+ describe("cleanPrTitle", () => {
++ test("removes the enforcement marker before extracting scope and sentence casing", () => {
++ expect(cleanPrTitle(" [WRONG BRANCH] chore(release): promote validated 2.45.0 to main (#3813) ", 3813)).toEqual({
++ scope: "release",
++ text: "Promote validated 2.45.0 to main",
++ });
++ });
++
++ test.each([
++ ["[Preview] chore(release): keep this marker", "[Preview] chore(release): keep this marker"],
++ ["fix: document [WRONG BRANCH] markers", "Document [WRONG BRANCH] markers"],
++ ["[WRONG BRANCH]ish: keep this title", "[WRONG BRANCH]ish: keep this title"],
++ ])("preserves meaningful title text: %s", (title, text) => {
++ expect(cleanPrTitle(title).text).toBe(text);
++ });
+ test("strips conventional prefix, keeps scope, and sentence-cases the title", () => {
+ expect(cleanPrTitle("feat(providers): add Baseten Model APIs preset", 653)).toEqual({
+ scope: "providers",
+@@ -488,6 +502,55 @@ describe("cleanPrTitle", () => {
+ });
+
+ describe("renderReleaseNotes", () => {
++ test.each(["delta", "carried"])("removes the bot marker from summaries and full changelogs (%s)", source => {
++ const body = [
++ "## What's Changed",
++ "### Chores",
++ "* [WRONG BRANCH] chore(release): promote validated 2.45.0 to main by @lidge-jun in https://github.com/lidge-jun/opencodex/pull/3813",
++ ].join("\n");
++ const notes = renderReleaseNotes({
++ npmMetadata: "",
++ ...(source === "delta" ? { deltaPrNotes: body } : { carriedPreviewNotes: [
++ "## Chores", "",
++ "- [WRONG BRANCH] chore(release): promote validated 2.45.0 to main (#3813)", "",
++ "## Changelog", "",
++ "- #3813 [WRONG BRANCH] chore(release): promote validated 2.45.0 to main @lidge-jun",
++ ].join("\n") }),
++ });
++ expect(notes).toBe([
++ "## Chores", "",
++ "- Promote validated 2.45.0 to main (#3813)", "",
++ "## Changelog", "",
++ "- #3813 chore(release): promote validated 2.45.0 to main @lidge-jun", "",
++ ].join("\n"));
++ });
++
++ test("groups a bot-prefixed title with ordinary titles of the same scope", () => {
++ const notes = renderReleaseNotes({
++ npmMetadata: "",
++ deltaPrNotes: [
++ "## What's Changed", "### Chores",
++ "* [WRONG BRANCH] chore(release): promote verified version by @maintainer in https://github.com/lidge-jun/opencodex/pull/10",
++ "* chore(release): update notes by @contributor in https://github.com/lidge-jun/opencodex/pull/11",
++ ].join("\n"),
++ });
++ expect(notes).toContain("- Release: Promote verified version; Update notes (#10, #11)");
++ expect(notes).toContain("- #10 chore(release): promote verified version @maintainer");
++ expect(notes).toContain("- #11 chore(release): update notes @contributor");
++ expect(notes).not.toContain("[WRONG BRANCH]");
++ });
++
++ test.each([
++ "[Preview] chore(release): retain the preview marker",
++ "fix: document [WRONG BRANCH] markers (#99)",
++ "[WRONG BRANCH]ish: retain this title",
++ ])("preserves meaningful full-changelog title text: %s", title => {
++ const notes = renderReleaseNotes({
++ npmMetadata: "",
++ deltaPrNotes: `## What's Changed\n### Chores\n* ${title} by @contributor in https://github.com/lidge-jun/opencodex/pull/12`,
++ });
++ expect(notes).toContain(`- #12 ${title} @contributor`);
++ });
+ const carried = [
+ "",
+ "",
+```
+
+## C-stage correction: active release builder
+
+Accepted Codex review: actual release.yml invokes scripts/build-release-changelog.ts, whose changelog still used pr.title.trim(). The original tests certified a renderer but not this active entry. Extend the same bug fix: export stripPrEnforcementPrefix from scripts/release-notes.ts, import/use it for PR changelog titles in scripts/build-release-changelog.ts; add public buildReleaseNotes regressions in existing tests/ci-workflows/build-release-changelog.test.ts for generated-note enrichment and associated-PR fallback, asserting cleaned summary, preserved conventional changelog title/author/ID, and unrelated/embedded/near-match preservation. Keep category policy, direct-commit policy, network/dispatch and release coverage rules unchanged. The two new paths are part of this one bug, not a new delivery. Hosted current-head CI must execute both renderer test files. No local product test. Re-audit active caller and pure-string security boundary before accepting the repair.
+
+Repair source audit PASS at ef15842fc: actual builder emission and generated/associated regression paths verified. Source-of-truth paragraph corrected in be1f60f28 to distinguish active builder from standalone renderer; category selection/direct-commit policy unchanged. Final proof compares landed blobs to final reviewed candidate, not the original incomplete source. Prior run34167832861 passed16/skipped3 and showed original renderer cases onLinux/macOS; final newhead must be certified separately.
+
+DONE: PR3960 landed9c8f66b9d, finalheadbe1f60f28; CI34168481093 success16/skipped3. Both renderer files and new5 active-builder cases were observed in Linux logs; macOS lanespassed. Independent final source/security auditPASS. Exact destination60bcb9050 plus reviewed patch tree verified; coauthor present; source3899 and issue3895 closed. The combined destination tree was verified structurally, not claimed executed as the PR test tree. Local productcommandsNOTRUN.
diff --git a/devlog/_fin/260908_c248_individual_fixes/030_timezone.md b/devlog/_fin/260908_c248_individual_fixes/030_timezone.md
new file mode 100644
index 00000000000..479027db3f1
--- /dev/null
+++ b/devlog/_fin/260908_c248_individual_fixes/030_timezone.md
@@ -0,0 +1,157 @@
+# Phase timezone: extract only the Santiago fixture
+
+Source PR #3950. Carry ONLY commit 1d8f6ff7e8d48f33c3ce7a1b7118068754bbbe83 onto current dev, retaining luvs01's author and adding a surviving Co-authored-by. MODIFY only gui/tests/usage-custom-range.test.tsx. No JWT, product UI, dependency or workflow change. No UI screenshot is fabricated: describe the test-only scope truthfully in the PR. No new SoT contract beyond fixture isolation; production date interpretation is unchanged.
+
+Activation/acceptance: parent TZ absent and set cases retain exact presence/value and local Date epoch; a child Bun process is created with TZ=America/Santiago and an exact anchored test-name filter, preventing recursion by that timezone value. Child asserts skipped midnight, final-day activity and tooltip as before. Process deadline 12s, child test timeout 10s, parent test timeout 15s; timeout, signal and nonzero exit surface captured diagnostics. Reviewer must check config/preload behavior under direct child invocation and Windows Bun 1.4.0 compatibility. Hosted dashboard test gate explicitly executes this test file; inspect result and logs, not only a generic check badge. Additional fault-path testing is required only if audit reveals a reachable unprotected failure; amend this doc before any code change. Local product tests/build/typecheck/install NOT RUN.
+
+#3950 original stays open until B's separate JWT fix is independently confirmed on dev. No assumption that the original mixed PR's CI certifies this split head. One independent revert covers the timezone test only.
+
+Exact source patch follows:
+
+```diff
+diff --git a/gui/tests/usage-custom-range.test.tsx b/gui/tests/usage-custom-range.test.tsx
+index 887c31134..02df29f3d 100644
+--- a/gui/tests/usage-custom-range.test.tsx
++++ b/gui/tests/usage-custom-range.test.tsx
+@@ -154,31 +154,45 @@ for (const connected of [false, true]) {
+ }
+
+ test("America/Santiago midnight DST retains final-day activity and tooltip", async () => {
+- const previous = process.env.TZ;
+- process.env.TZ = "America/Santiago";
+- try {
+- expect(new Date(2026, 8, 6, 0).getHours()).toBe(1);
+- await mount();
+- await respond(0, "preset-marker");
+- await enter("2026-09-05T00:00", "2026-09-07T23:59");
+- await apply();
+- const gate = requests.at(-1)!;
+- const data = report(gate, "santiago-marker", "2026-09-07");
+- data.days = ["2026-09-05", "2026-09-06", "2026-09-07"].map(date => ({
+- date, requests: date === "2026-09-07" ? 7 : 0, measuredRequests: 0, reportedRequests: 0,
+- totalTokens: date === "2026-09-07" ? 700 : 0, models: [],
+- }));
+- await act(async () => gate.resolve(Response.json(data)));
+- const active = container.querySelector('.heatmap-grid .heatmap-cell:not(.heatmap-cell-0)');
+- expect(active).not.toBeNull();
+- await act(async () => active!.dispatchEvent(new testWindow.MouseEvent("mouseover", { bubbles: true })));
+- expect(container.querySelector(".heatmap-tip-date")?.textContent).toBe("2026-09-07");
+- expect(container.querySelector(".heatmap-tip")?.textContent).toContain("700");
+- } finally {
+- if (previous === undefined) delete process.env.TZ;
+- else process.env.TZ = previous;
++ if (process.env.TZ !== "America/Santiago") {
++ // Restoring an absent TZ can change Bun's effective timezone on Windows.
++ // Start the DST case in its timezone without mutating this suite's clock.
++ const timezone = { present: Object.hasOwn(process.env, "TZ"), value: process.env.TZ };
++ const localTime = new Date(2020, 8, 15, 10, 20).getTime();
++ const child = Bun.spawnSync([
++ process.execPath, "test", import.meta.path,
++ "-t", "^America/Santiago midnight DST retains final-day activity and tooltip$",
++ "--timeout", "10000",
++ ], {
++ env: { ...process.env, TZ: "America/Santiago" },
++ stdout: "pipe", stderr: "pipe", timeout: 12000, killSignal: "SIGKILL",
++ });
++ const diagnostics = `${child.stdout.toString()}\n${child.stderr.toString()}`;
++ expect(child.exitedDueToTimeout, diagnostics).not.toBe(true);
++ expect(child.signalCode, diagnostics).toBeUndefined();
++ expect(child.exitCode, diagnostics).toBe(0);
++ expect({ present: Object.hasOwn(process.env, "TZ"), value: process.env.TZ }).toEqual(timezone);
++ expect(new Date(2020, 8, 15, 10, 20).getTime()).toBe(localTime);
++ return;
+ }
+-});
++ expect(new Date(2026, 8, 6, 0).getHours()).toBe(1);
++ await mount();
++ await respond(0, "preset-marker");
++ await enter("2026-09-05T00:00", "2026-09-07T23:59");
++ await apply();
++ const gate = requests.at(-1)!;
++ const data = report(gate, "santiago-marker", "2026-09-07");
++ data.days = ["2026-09-05", "2026-09-06", "2026-09-07"].map(date => ({
++ date, requests: date === "2026-09-07" ? 7 : 0, measuredRequests: 0, reportedRequests: 0,
++ totalTokens: date === "2026-09-07" ? 700 : 0, models: [],
++ }));
++ await act(async () => gate.resolve(Response.json(data)));
++ const active = container.querySelector('.heatmap-grid .heatmap-cell:not(.heatmap-cell-0)');
++ expect(active).not.toBeNull();
++ await act(async () => active!.dispatchEvent(new testWindow.MouseEvent("mouseover", { bubbles: true })));
++ expect(container.querySelector(".heatmap-tip-date")?.textContent).toBe("2026-09-07");
++ expect(container.querySelector(".heatmap-tip")?.textContent).toContain("700");
++}, 15000);
+
+ test("Apply submits inclusive bounds once; Clear restores the held preset without custom cache entries", async () => {
+ await mount();
+```
+
+## Audit-driven amendment before implementation
+
+The source patch's explicit 15-second per-test timeout overrides its child CLI 10-second timeout. Change the final test timeout to `process.env.OCX_USAGE_SANTIAGO_CHILD === "1" ? 10000 : 15000`. Add a unique completion marker printed only after the child's last UI assertion; require the marker in the parent as well as exit/signal/timeout checks. Set the child's cwd explicitly to the dashboard root resolved from import.meta.dir. These are same-bug test integrity changes; no production code changes. Preserve the original assertions and parameterized test cases.
+
+Existing hosted Windows/macOS jobs do not run gui/tests. A supplemental verification-only branch will use the already-registered ci.yml workflow_dispatch path, with a separately reviewed minimal workflow that checks out an immutable candidate SHA and executes only focused timezone proof on GitHub-hosted ubuntu/windows/macos. This branch/workflow is excluded from delivery and never merged. Candidate PR CI remains unchanged and required; the supplemental run is independently labeled, not passed off as normal candidate workflow CI. Actions use existing pinned SHAs, contents:read only, no secrets, checkout persist-credentials:false, Bun1.4.0, frozen root and dashboard installs on the hosted machines, and bounded jobs/processes. Never run any of these commands locally. Negative controls must restore candidate bytes before the final positive run and record source identity.
+
+The proposed hosted verification starts in gui/: `bun test --isolate ./tests/usage-custom-range.test.tsx`, with TZ absent, Etc/UTC, Asia/Seoul and America/Santiago in distinct subprocess environments. Verify parent environment and next tests, child success marker, nonzero-exit/absent-marker propagation and process deadline; no fixture/process may survive teardown. Exact workflow YAML, pinned commit and control script are reviewed before dispatch. The repository's Windows product runtime suite is distinct from this Windows dashboard proof.
+
+### Exact test-integrity follow-up diff atop the original source commit
+
+```diff
+--- a/gui/tests/usage-custom-range.test.tsx
++++ b/gui/tests/usage-custom-range.test.tsx
+@@ -1,5 +1,6 @@
+ import { afterEach, beforeEach, expect, test } from "bun:test";
+ import { Window } from "happy-dom";
++import { resolve } from "node:path";
+ import { act } from "react";
+ import type { Root } from "react-dom/client";
+ import { LanguageProvider } from "../src/i18n/provider";
+@@ -154,7 +155,7 @@
+ }
+
+ test("America/Santiago midnight DST retains final-day activity and tooltip", async () => {
+- if (process.env.TZ !== "America/Santiago") {
++ if (process.env.OCX_USAGE_SANTIAGO_CHILD !== "1" && process.env.TZ !== "America/Santiago") {
+ // Restoring an absent TZ can change Bun's effective timezone on Windows.
+ // Start the DST case in its timezone without mutating this suite's clock.
+ const timezone = { present: Object.hasOwn(process.env, "TZ"), value: process.env.TZ };
+@@ -164,17 +165,20 @@
+ "-t", "^America/Santiago midnight DST retains final-day activity and tooltip$",
+ "--timeout", "10000",
+ ], {
+- env: { ...process.env, TZ: "America/Santiago" },
++ cwd: resolve(import.meta.dir, ".."),
++ env: { ...process.env, TZ: "America/Santiago", OCX_USAGE_SANTIAGO_CHILD: "1" },
+ stdout: "pipe", stderr: "pipe", timeout: 12000, killSignal: "SIGKILL",
+ });
+ const diagnostics = `${child.stdout.toString()}\n${child.stderr.toString()}`;
+ expect(child.exitedDueToTimeout, diagnostics).not.toBe(true);
+ expect(child.signalCode, diagnostics).toBeUndefined();
+ expect(child.exitCode, diagnostics).toBe(0);
++ expect(child.stdout.toString().split(/\r?\n/), diagnostics).toContain("OCX_SANTIAGO_CASE_COMPLETED");
+ expect({ present: Object.hasOwn(process.env, "TZ"), value: process.env.TZ }).toEqual(timezone);
+ expect(new Date(2020, 8, 15, 10, 20).getTime()).toBe(localTime);
+ return;
+ }
++ expect(process.env.TZ).toBe("America/Santiago");
+ expect(new Date(2026, 8, 6, 0).getHours()).toBe(1);
+ await mount();
+ await respond(0, "preset-marker");
+@@ -192,7 +196,8 @@
+ await act(async () => active!.dispatchEvent(new testWindow.MouseEvent("mouseover", { bubbles: true })));
+ expect(container.querySelector(".heatmap-tip-date")?.textContent).toBe("2026-09-07");
+ expect(container.querySelector(".heatmap-tip")?.textContent).toContain("700");
+-}, 15000);
++ if (process.env.OCX_USAGE_SANTIAGO_CHILD === "1") console.log("OCX_SANTIAGO_CASE_COMPLETED");
++}, process.env.OCX_USAGE_SANTIAGO_CHILD === "1" ? 10000 : 15000);
+
+ test("Apply submits inclusive bounds once; Clear restores the held preset without custom cache entries", async () => {
+ await mount();
+```
+
+C review amendment: use the dedicated child marker as the sole recursion guard, even when the parent already starts in Santiago. This preserves all original DST assertions and makes completion/state checks run for every parent TZ. Accepted CodeRabbit finding; final source/evidence checkout SHA will be repinned and hosted proof rerun. Prior Linux/Windows proof8223788bd remains historical, not finalhead evidence.
+
+Final candidate ce71d9171 passed independent marker-guard source re-audit. Evidence workflow7d5f1097e/run34170111719 checks out exactcandidatece71d9171; Linux/Windows/macOS each completed10scenarios, fivepositive/fiveexpectednegative, with exactfailure attribution, timeoutPIDabsence and candidatebytesrestored. Actual evidence JSON logs checked. Normal PR3967CI34170093095 pending; no completion/landing claim yet. Existing maintainer gui-screenshot-waived exception applied for test-only change after workflow/label policy inspection. No UI screenshot fabricated, no product gate waived.
+
+NormalCI attempt1 of34170093095 was cancelled at macos1 job20-minute deadline. Last emitted test was the unchanged client-connect CLI rejection case, followed by dangling-process cleanup and no completion. This root macOS lane does not include gui/tests; exact cause remains under investigation. Preserve cancellation as an unsuccessful/incomplete attempt. One same-head failed-job recheck was requested for diagnosis; a green recheck alone does not establish the unrelated runner stall is fixed. Supplemental3OS timezoneproof remains separately valid.
+
+DONE: PR3967 landedc46c22f3e with luvs01 trailer and exactcandidate file. Final candidatece71d9171 supplemental3OS run34170111719 passed all30expected scenarios. StandardCI34170093095 attempt2 passed19jobs/skipped2; attempt1 macos1 stalled/cancelled20min at unchangedclientconnect boundary remains unresolved reliability residual, not a fixedflake claim. Exact destinationbbea77a48+candidatepatch tree and devancestry verified. BJWT3962/eb4188a9 confirmed ondev; source3950 closure follows reconciliation.
diff --git a/devlog/_fin/260908_c248_individual_fixes/040_reconcile.md b/devlog/_fin/260908_c248_individual_fixes/040_reconcile.md
new file mode 100644
index 00000000000..753a2fa2ee2
--- /dev/null
+++ b/devlog/_fin/260908_c248_individual_fixes/040_reconcile.md
@@ -0,0 +1,3 @@
+# Phase reconcile: independent delivery and release readiness
+
+No product delta. MODIFY this unit's outcome record (050_outcome.md) with each original PR, actual delivery PR, reviewed head, CI run/check counts, landed SHA, author trailer and issue state. Query B for the JWT-only landing and independently verify it in dev together with C's timezone commit before closing #3950. Inspect A/B status and record readiness without doing their work or publishing a release. Move this unit from devlog/_plan to devlog/_fin only after all scoped tasks are complete; publish a separate docs-only closeout PR if needed, keeping it out of all three bug commits. Verify that closeout's scope check and diff preserve product files. No version change or promotion. Outcomes must distinguish true merged source PRs from closed carry sources.
diff --git a/devlog/_fin/260908_c248_individual_fixes/050_outcome.md b/devlog/_fin/260908_c248_individual_fixes/050_outcome.md
new file mode 100644
index 00000000000..aa085d58409
--- /dev/null
+++ b/devlog/_fin/260908_c248_individual_fixes/050_outcome.md
@@ -0,0 +1,35 @@
+# Lane C outcome
+
+All three scoped corrections landed into dev through independent bug PRs. The capture correction needed a separate follow-up after its author advanced the source PR during CI. No already-landed commit was rewritten. This record is documentation only and is not another product fix.
+
+| Source | Delivery PR | Landed SHA | Current-head CI | Scope |
+|---|---|---|---|---|
+| #3953 initial | [3955](https://github.com/lidge-jun/opencodex/pull/3955) | `9c54000c937276ba8d93ce63a922b3fe6797cbde` | [34166758020](https://github.com/lidge-jun/opencodex/actions/runs/34166758020) (3 success / 10 skipped) | Current-tree Accounts capture cleanup |
+| #3953 follow-up | [3959](https://github.com/lidge-jun/opencodex/pull/3959) | `01c23aedcdfcb913151a2ac8f7acebda58d91eee` | [34167651789](https://github.com/lidge-jun/opencodex/actions/runs/34167651789) (3 success / 10 skipped) | Consistent capture retention and isolation guidance |
+| #3899 / #3895 | [3960](https://github.com/lidge-jun/opencodex/pull/3960) | `9c8f66b9df4cdf133a16c95a95ee07ff5171a46d` | [34168481093](https://github.com/lidge-jun/opencodex/actions/runs/34168481093) (16 success / 3 skipped) | Release-note marker in both actual and standalone builders |
+| #3950 timezone only | [3967](https://github.com/lidge-jun/opencodex/pull/3967) | `c46c22f3e4d00ff31a0e6bb10f74505577806776` | [34170093095](https://github.com/lidge-jun/opencodex/actions/runs/34170093095) (19 success / 2 skipped) | Santiago subprocess isolation and oracle integrity |
+
+## Proof and attribution
+
+Each landing was serialized through the shared merge lock and checked against the then-current destination: actual merge parent, computed combined tree, dev ancestry and surviving Co-authored-by trailer. luvs01 is credited in both capture carries and the timezone carry; Joonsuh Park is credited in the release-note correction. The original source PRs were closed as carried, not described as directly merged.
+
+#3953 was closed only after its refreshed ca21efd2 follow-up was included. #3899 and issue #3895 closed after the active release builder was corrected and verified. #3950 was closed only after B's independent JWT delivery #3962 (eb4188a9f2e127f5ee2980b62d6e5bb213c43c70) and C's timezone delivery #3967 were both confirmed on dev. Product commits remain independently revertible.
+
+The release-note original patch missed scripts/build-release-changelog.ts, the actual release workflow entry. Review led to a shared normalizer and five public-builder cases covering generated and associated PR sources and negative marker preservation. Those cases and the original renderer cases were observed passing in the final Linux CI logs; the final macOS lanes also passed. The structure guide now accurately distinguishes the active and standalone renderers.
+
+Timezone final candidate ce71d917143ddcbd5675b6ba92d8b1053971cd25 was separately exercised by evidence workflow7d5f1097ec587a0ced441f475eb02d750e06b9ac in [run34170111719](https://github.com/lidge-jun/opencodex/actions/runs/34170111719). The workflow checked out that immutable candidate separately. Linux, Windows and macOS each completed ten scenarios: five positive/restored runs and five deliberately failing controls. Controls require the intended test failure and specific diagnostics, not any nonzero exit. All platforms verified the final candidate file hash recorded below. Child timeout termination and restored candidate bytes/HEAD were verified. The evidence branch is not in any delivery PR and is never merged.
+
+## Limits and remaining work
+
+- All local product tests, test:changed, typechecks, builds and dependency installs were NOT RUN. Mutating Git operations disabled hooks per command, and pushes used --no-verify. Git/diff/source and operational evidence checks are distinct from product tests.
+- Skipped jobs are not counted as passing tests. Normal PR workflows skip the full Windows runtime suite and macOS whole-pool control; the supplementary timezone run explicitly supplies Windows/macOS focused dashboard evidence, not a full runtime-suite result.
+- Timezone normal CI34170093095 attempt1 timed out after20minutes in the unchanged root macOS client-connect test. The next helper contains an unbounded synchronous child wait, but the actual stopping mechanism is unproven. Attempt2 succeeded on the same candidate without a source change. The cancelled attempt remains unsuccessful evidence and the unrelated CI reliability defect is not claimed fixed.
+- Privacy cleanup affects the current tree only. Historical blobs/links were not purged, and no claim of historical erasure is made. The working proxy was not restarted or reconfigured by this task.
+- Concurrent dev changes were preserved through actual-tree comparison. That structural proof does not imply every merged integration tree was separately executed by the candidate CI.
+- A and B were still active at reconciliation. B's JWT slice is verified; no assertion is made that their remaining changes or the overall2.48 release are complete. main/preview promotion, version changes and npm publication were outside C's authority and were not performed.
+
+## Final supplemental evidence
+
+- win32: Bun1.4.0, candidate file SHA-256 `6cbb58c96643f500cf2541ef3b7707aed072c1f981b16b49f97949536fe30f50`, ten scenarios, restored=True.
+- linux: Bun1.4.0, candidate file SHA-256 `6cbb58c96643f500cf2541ef3b7707aed072c1f981b16b49f97949536fe30f50`, ten scenarios, restored=True.
+- darwin: Bun1.4.0, candidate file SHA-256 `6cbb58c96643f500cf2541ef3b7707aed072c1f981b16b49f97949536fe30f50`, ten scenarios, restored=True.
diff --git a/devlog/_fin/260908_c_track_config_init_stack/000_plan.md b/devlog/_fin/260908_c_track_config_init_stack/000_plan.md
new file mode 100644
index 00000000000..046e2c627dc
--- /dev/null
+++ b/devlog/_fin/260908_c_track_config_init_stack/000_plan.md
@@ -0,0 +1,79 @@
+# 000_plan.md — C track: config file + init as a manual PR stack
+
+## Objective
+
+Land the C triage track (config-file and init surfaces) on `dev` as one manual,
+dependency-ordered branch chain whose tip carries every layer. The track has two
+existing contributor pull requests plus one gap discovered while planning:
+
+| Layer | Source | Surface |
+|---|---|---|
+| wp1 | PR #3900 by @x3M3x | `src/config/atomic-write.ts` Bun/Windows ENOENT |
+| wp2 | new (this unit) | `src/config/initialize.ts` sibling numeric flag |
+| wp3 | PR #3896 by @parkjs101 | `ocx init` publication recovery guidance (closes #3893) |
+
+wp2 exists on its own merit, not as glue. `publishInitialConfigNoReplace` still
+opens its temp file with the numeric spelling that Bun miscompiles on Windows,
+so shipping wp1 alone leaves first-run config publication exposed to the same
+`ENOENT`. #3900 never touches `initialize.ts`; the file overlap is between wp2
+and wp3 only.
+
+## Constraints (owner-stated, this session)
+
+- **No local product suite.** No `bun run test`, `bun run typecheck`,
+ `bun run build`, or install. Every such check is recorded **NOT RUN**.
+- **Push with `--no-verify`** on every layer.
+- **CI on the tip only.** Verified mechanism in `040`: the lower layers are
+ pushed as branches but **no pull request is opened for them** until the tip
+ has landed. `.github/workflows/ci.yml` triggers on `pull_request: {}` with no
+ draft filter, so opening a lower PR would start CI; draft status suppresses
+ nothing.
+- **Green tip merges; the rest resolve.** When the tip's exact head SHA is green
+ against a current `dev` base, merge the tip, then resolve the source PRs and
+ close issue #3893.
+- **Original authors are preserved** with full `Co-authored-by: Name `
+ trailers that survive the squash (AGENTS.md "Landing another author's work").
+
+## Build order
+
+```
+codex/c-track-init-guidance → the ONLY pull request (base dev) ← wp3 tip
+codex/c-track-initialize-flag → branch only, no PR ← wp2
+codex/c-track-atomic-write → branch only, no PR ← wp1
+──────────────────────────────── dev
+```
+
+Each branch is based on the one below, so the tip's tree is the cumulative
+result. wp2 sits between the carried PRs because wp3 inserts a line directly
+after the `openSync` call that wp2 rewrites; constructing wp2 first means that
+adjacent-hunk overlap is resolved once while carrying wp3. This is a chosen
+construction order for a single conflict resolution, not a semantic
+prerequisite — either change could be written first.
+
+## Scope boundary
+
+IN: the three layers above, their regression tests, the docs/structure text that
+#3896 already carries, and this devlog unit.
+
+OUT: `#3838`/`#3917` adapter work, any other triage track, release promotion,
+`main`/`preview`, and any behavioral change to hard-link publication, ACL
+hardening, or credential storage beyond the flag spelling.
+
+## Verifiers
+
+Local product gates are forbidden this session, so acceptance rests on
+repository CI against the tip plus read-only inspection.
+
+| Claim | Evidence | Status |
+|---|---|---|
+| Layers carry original authorship | `git log --format='%(trailers:key=Co-authored-by)'` on the tip, then on the landed commit | to run (read-only) |
+| wp2 removes the numeric spelling | `rg 'constants\.O_' src/config/initialize.ts` on the pushed tip tree | to run (read-only) |
+| Carried content really landed | tip tree vs. each source PR's pinned patch, then landed-merge tree comparison (see `040`) | to run (read-only) |
+| Layers build and pass | repository CI on the tip head SHA, base `dev` | tip only |
+| Local suite / typecheck / build | — | **NOT RUN** (owner instruction) |
+
+## Terminal outcome
+
+DONE requires: tip CI success on its exact head SHA against a current `dev`
+base, tip merged into `dev` proven by fetched ancestry and tree comparison,
+source PRs resolved with credit intact, and #3893 closed.
diff --git a/devlog/_fin/260908_c_track_config_init_stack/010_layer1_atomic_write.md b/devlog/_fin/260908_c_track_config_init_stack/010_layer1_atomic_write.md
new file mode 100644
index 00000000000..d9e76ba2bb4
--- /dev/null
+++ b/devlog/_fin/260908_c_track_config_init_stack/010_layer1_atomic_write.md
@@ -0,0 +1,72 @@
+# 010_layer1_atomic_write.md — wp1: carry PR #3900
+
+Source: [PR #3900](https://github.com/lidge-jun/opencodex/pull/3900) by @x3M3x,
+head `744eb644028492784446fe9f0f73813d5d1fe59f`, two commits
+(`52c749561`, `744eb6440`).
+
+Branch: `codex/c-track-atomic-write`, base `dev`.
+
+## Problem
+
+`src/config/atomic-write.ts` builds its exclusive-create flags numerically.
+Bun on Windows misreads that combination and drops the creation bit, so every
+private temp write fails with `ENOENT`: `ocx start`, management-API config
+saves, and OAuth credential refreshes all route through these two writers.
+
+## Change (MODIFY, carried unmodified from #3900)
+
+`src/config/atomic-write.ts`
+
+```diff
+- const descriptor = openSync(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600);
++ const descriptor = openSync(path, "wx", 0o600);
+```
+
+Applied in both `writePrivateTempFile` and `writePrivateTempFileAsync`; the now
+unused `constants` import is dropped.
+
+`tests/windows/windows-secret-acl.test.ts` gains the source-oracle guard
+"atomic secret temp writer portability", asserting exactly two portable calls.
+
+## Semantics note (audit correction)
+
+The PR description calls `"wx"` exactly `O_WRONLY | O_CREAT | O_EXCL`. Node and
+Bun actually map it to `O_WRONLY | O_CREAT | O_EXCL | O_TRUNC`. It is
+**behaviorally** equivalent here rather than bit-identical: exclusive creation
+rejects an existing path, so `O_TRUNC` can never truncate one. `0o600` remains
+a separate mode argument and still applies. Recorded so a later reader does not
+inherit the imprecise claim.
+
+## Security review (independent, read-only)
+
+No blocking finding. Exclusivity is preserved for every caller of the default
+wrappers, which include OAuth `auth.json`, Codex account credentials, service
+API tokens, `config.json`, and `ocx.pid`. Ownership is marked only after a
+successful create, so no new pre-existing-temp or symlink-following path opens.
+Windows ACL ordering (create → own → harden → identity check → write → close)
+is untouched.
+
+## Authorship
+
+Both commits are cherry-picked with `-x`, so each retains
+`x3M3x ` as its git author and records the source SHA:
+
+| Carried commit | Source commit |
+|---|---|
+| `6a0abcf90` fix: use portable exclusive config temp creation | `52c7495618f18f2847b7f9468421442c1c573da1` |
+| `24a078d80` test: guard atomic temp writes against Bun/Windows ENOENT | `744eb644028492784446fe9f0f73813d5d1fe59f` |
+
+A squash landing keeps only the squash message and drops per-commit authors, so
+`b1a7f111c` adds the `Co-authored-by: x3M3x ` trailer to
+the branch. That trailer must be carried into the tip PR squash message and
+re-read on the landed commit.
+
+## Verification
+
+An independent read-only audit of the built branch confirmed the carried
+source-and-test diff is byte-identical to #3900 pinned patch (2,176 bytes),
+that both `-x` annotations and the original author survive, that the trailer
+parses through `git interpret-trailers`, and that no other `src/` file changed.
+
+Repository CI on the stack tip only. Local suite, typecheck, and build:
+**NOT RUN** (owner instruction).
diff --git a/devlog/_fin/260908_c_track_config_init_stack/020_layer2_initialize_flag.md b/devlog/_fin/260908_c_track_config_init_stack/020_layer2_initialize_flag.md
new file mode 100644
index 00000000000..b25a419481c
--- /dev/null
+++ b/devlog/_fin/260908_c_track_config_init_stack/020_layer2_initialize_flag.md
@@ -0,0 +1,69 @@
+# 020_layer2_initialize_flag.md — wp2: sibling flag in initialize.ts
+
+New work in this unit. Branch `codex/c-track-initialize-flag`, base
+`codex/c-track-atomic-write`.
+
+## Why this layer exists
+
+wp1 fixes the two writers in `atomic-write.ts`, but
+`publishInitialConfigNoReplace` in `src/config/initialize.ts` still opens its
+temp file with the same numeric spelling. Independent inspection confirms the
+identical Bun/Windows exposure: first-run `ocx init` fails before writing or
+publishing `config.json`, leaving `publication = "not-published"` and
+`hardLinkUnavailable = false`, so the CLI prints "Initial config publication
+did not finish." and exits 1.
+
+It also sits between the two carried PRs deliberately. #3896 inserts a line
+immediately after this `openSync` call, so building wp2 first means the
+adjacent-hunk overlap is resolved once, while carrying #3896 in wp3. This is a
+chosen construction order rather than a semantic prerequisite: #3900 does not
+touch this file at all, and either change could be written first.
+
+## Change (MODIFY)
+
+`src/config/initialize.ts`
+
+```diff
+ import {
+- closeSync, constants, fchmodSync, fstatSync, linkSync, lstatSync,
++ closeSync, fchmodSync, fstatSync, linkSync, lstatSync,
+ openSync, unlinkSync, writeFileSync,
+ } from "node:fs";
+@@
+- fd = openSync(temp, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600);
++ fd = openSync(temp, "wx", 0o600);
+```
+
+`constants` is referenced only by the import and that one expression, so the
+import entry is removed with it.
+
+## Change (MODIFY, regression)
+
+`tests/windows/windows-secret-acl.test.ts` gains a sibling source-oracle test
+next to the wp1 guard, asserting exactly one portable call in
+`src/config/initialize.ts`.
+
+## Contract preserved
+
+The no-replace publication contract does not depend on the numeric spelling:
+hardening, `verifyPrivateTemp`, the single `linkSync` publication with its
+`EEXIST`/`collided` and `hardLinkUnavailable` handling, and the
+descriptor-owned `removeOwnedTemp` cleanup are all unchanged.
+
+## Out of scope: the same pattern under `src/lab/`
+
+An independent scan found three more exclusive opens sharing this combination:
+`src/lab/ledger/store.ts:153` and `:185` (recovery mutex, ledger lock) and
+`src/lab/public/private-file.ts:209` (private publication temp). They deserve the
+same portability follow-up, but Lab is an opt-in subsystem off the core request
+path, so they stay out of this track rather than widening a config-surface fix.
+
+Two further matches are not exclusive opens and must not be swept in:
+`src/codex/native-main-lock-file.ts:89` and `src/lab/fabric/scratch.ts:416`. The
+read/write sites in `src/lab/artifacts/secure-fs.ts` need individual treatment because
+`"wx"` would drop read access.
+
+## Verification
+
+Repository CI on the stack tip only. Local suite, typecheck, and build:
+**NOT RUN** (owner instruction).
diff --git a/devlog/_fin/260908_c_track_config_init_stack/030_layer3_init_guidance.md b/devlog/_fin/260908_c_track_config_init_stack/030_layer3_init_guidance.md
new file mode 100644
index 00000000000..5f755c686d6
--- /dev/null
+++ b/devlog/_fin/260908_c_track_config_init_stack/030_layer3_init_guidance.md
@@ -0,0 +1,65 @@
+# 030_layer3_init_guidance.md — wp3: carry PR #3896 (stack tip)
+
+Source: [PR #3896](https://github.com/lidge-jun/opencodex/pull/3896) by
+@parkjs101 (Joonsuh Park), head `fc78bc37d419576061b995281baf39c46655eaa9`,
+closes [#3893](https://github.com/lidge-jun/opencodex/issues/3893).
+
+Branch: `codex/c-track-init-guidance`, based on `codex/c-track-initialize-flag`.
+This is the stack tip and the only pull request opened for this unit.
+
+## Problem
+
+`ocx init` already separates denied hard-link publication from a generic
+failure, but a required permission-hardening failure falls into the generic
+message, and neither message tells the user what to do next. The user cannot
+tell why publication stopped or where to move `OPENCODEX_HOME`.
+
+## Change (carried from #3896, rebased onto wp2)
+
+- `src/config/initialize.ts`: a `hardeningFailed` flag set immediately before
+ the hardening call and cleared immediately after it returns — the assignments
+ surround `hardenInitialConfig`, which runs after `openSync`. The flag selects
+ a distinct message; `InitialConfigPublicationError` takes the matching option,
+ and both messages gain `OPENCODEX_HOME` recovery direction.
+- `docs-site/src/content/docs/getting-started/quickstart.md`: inspection before
+ retry, preserving existing configuration, choosing a supported location.
+- `structure/02_config-and-codex-home.md`: records the diagnostic distinction.
+- `tests/config/config-mutation-lock.test.ts`, `tests/service/init-eof.test.ts`:
+ permission, link, and cleanup faults, privacy-safe messages, backup
+ preservation. Both files already exist in the test-layout registries, so no
+ registry entry is added.
+- `devlog/_plan/260907_init_publication_guidance/010_implementation.md`: carried
+ as-is; on terminal closure that unit moves to `_fin/`.
+
+The rebase keeps wp2's `openSync(temp, "wx", 0o600)` and both `hardeningFailed`
+assignments around the hardening call.
+
+## Review (independent subagent audit, read-only, this session)
+
+Reviewed at `fc78bc37d`, all six files. No blocking finding:
+
+- The flag cannot be left incorrectly true. Write, verify, link, and close
+ failures all occur after it is cleared (`src/config/initialize.ts:95-115`,
+ `:129-134`). A throwing injected `io.harden` test seam would select the same
+ message, which is a seam edge rather than a production defect.
+- File I/O ordering, the no-replace guarantee, and private permissions are
+ unchanged (`:38-43`, `:98-130`).
+- The new messages are fixed text naming the `OPENCODEX_HOME` variable; they
+ interpolate no real paths, bytes, or filesystem error text, and raw errors
+ stay in the `cause` the CLI does not print (`src/cli/init.ts:262-267`).
+
+This is a static agent review, not the maintainer security review or the
+approval required by `MAINTAINERS.md`; those are recorded separately in `040`.
+
+## Authorship
+
+Carried with `Co-authored-by: Joonsuh Park `, the identity
+on the source commit. The trailer must appear in the **squash message** of the
+landed commit, and is verified on the landed commit rather than only on the
+branch.
+
+## Verification
+
+This tip is the only layer that triggers repository CI, and its exact head SHA
+must be green against a current `dev` base. Local suite, typecheck, and build:
+**NOT RUN** (owner instruction).
diff --git a/devlog/_fin/260908_c_track_config_init_stack/040_layer4_landing.md b/devlog/_fin/260908_c_track_config_init_stack/040_layer4_landing.md
new file mode 100644
index 00000000000..4413166db97
--- /dev/null
+++ b/devlog/_fin/260908_c_track_config_init_stack/040_layer4_landing.md
@@ -0,0 +1,62 @@
+# 040_layer4_landing.md — wp4: tip CI, merge, and closure
+
+## CI suppression: mechanism, not draft status
+
+`.github/workflows/ci.yml` triggers on `pull_request: {}` with **no draft
+filter** (line 7), and the `changes` job gates expensive work on the PR's own
+file list, which includes `src/**` and `tests/**`. Opening a lower-layer pull
+request — draft or not — therefore starts repository CI.
+
+The mechanism that actually satisfies "CI on the tip only" is to **open no pull
+request for wp1 and wp2**. Their branches are pushed so the tip has a real
+parent chain, but only `codex/c-track-init-guidance` gets a PR, based directly
+on `dev` so its diff is the cumulative stack. One workflow run, one subject.
+
+## Landing sequence
+
+1. Push all three branches with `--no-verify`.
+2. Open the tip PR only, base `dev`, with the full repository template
+ (Summary, Verification, Checklist) and `Closes #3893`. Record the local
+ suite as NOT RUN with the owner instruction as the reason; the Verification
+ section must not imply a local green run.
+3. Confirm the tip is based on the current `dev` head before CI. If `dev` has
+ advanced, rebase and cascade first — CI against a stale base does not certify
+ the integration tree that will actually merge.
+4. Wait for CI on the tip's exact head SHA. Skipped or cancelled checks are not
+ passing evidence.
+5. Record the merge decision. Both current maintainers hold `admin`, and
+ `MAINTAINERS.md` permits explicit maintainer integration into `dev` without a
+ second approval, provided the decision and exact-head CI evidence are
+ recorded and security review is kept separate. The credential-adjacent
+ `atomic-write.ts` carry is the security-review subject; its independent audit
+ is summarized in `010` and must be named in the merge record.
+6. Merge the tip, pinning the reviewed head SHA.
+
+## Proving the carried work landed
+
+Ancestry alone is insufficient: `dev` can contain the merge while a conflict
+resolution silently dropped a contributor hunk. Before closing anything:
+
+- Compare each source PR's pinned patch against the tip tree, documenting the
+ one intentional adaptation (wp2 rewrites the `openSync` line that #3896's
+ hunk sits next to).
+- After the merge, compare the landed tree on fetched `dev` against the
+ reviewed tip tree.
+- Re-read the landed commit's trailers to confirm both `Co-authored-by` entries
+ survived the squash.
+
+A squash landing does not make the original contributor SHAs ancestors, so
+trailer and content comparison are the credit and delivery evidence.
+
+## Closure
+
+1. Close #3900 and #3896 as landed through the tip, naming the merge commit and
+ crediting @x3M3x and @parkjs101 with the evidence above.
+2. Close issue #3893: PRs here target `dev`, and GitHub only auto-closes linked
+ issues on the default branch.
+
+## Failure handling
+
+If the tip's CI fails, fix the responsible layer and cascade the rebase upward
+(`DEV-STACK-02`) before re-running CI on the new tip head. Do not open or merge
+a lower layer independently to bypass a red tip.
diff --git a/devlog/_fin/260908_c_track_config_init_stack/050_outcome.md b/devlog/_fin/260908_c_track_config_init_stack/050_outcome.md
new file mode 100644
index 00000000000..05e19c97a18
--- /dev/null
+++ b/devlog/_fin/260908_c_track_config_init_stack/050_outcome.md
@@ -0,0 +1,82 @@
+# 050_outcome.md — terminal record
+
+## Result
+
+Landed on `dev` as `6188458ae3f4fd84ef57344b60cf3ceeed80aa6f` through
+[#3941](https://github.com/lidge-jun/opencodex/pull/3941) on 2026-09-07.
+
+| Layer | Source | Disposition |
+|---|---|---|
+| wp1 | [#3900](https://github.com/lidge-jun/opencodex/pull/3900) by @x3M3x | carried, PR closed as superseded |
+| wp2 | new here | landed |
+| wp3 | [#3896](https://github.com/lidge-jun/opencodex/pull/3896) by @parkjs101 | carried, PR closed as superseded |
+| — | [#3893](https://github.com/lidge-jun/opencodex/issues/3893) | closed |
+
+Both `Co-authored-by` trailers are on the landed squash commit. The 13 files in
+the reviewed tip hash identical to their landed counterparts, `atomic-write.ts`
+on `dev` hashes identical to #3900's pinned version, and `initialize.ts` equals
+#3896's pinned file apart from wp2's two intended substitutions.
+
+## What the plan got right
+
+The tip-only CI mechanism worked exactly as designed. `ci.yml` triggers on
+`pull_request` with no draft filter, so the first plan's assumption that draft
+status suppresses CI was wrong; opening no pull request for the lower layers is
+what actually produced one workflow subject. No Cross-platform CI run exists for
+either lower branch.
+
+Ordering wp2 between the two carried PRs also paid off as predicted. The cherry-pick
+of #3896 produced exactly one conflict — the adjacent hunk where `hardeningFailed = true`
+sits directly after the rewritten `openSync` line — and it was resolved once.
+
+## What the plan got wrong, and what caught it
+
+The first roadmap draft failed its independent audit with seven findings. Three
+mattered: the false draft-CI claim above, merging on stale-base CI evidence, and
+proving delivery by ancestry alone when a squash can silently drop a contributor
+hunk. It also misstated the file overlap as wp1/wp3 when #3900 never touches
+`initialize.ts`. The revised plan passed re-audit, and the stale-base rule
+earned itself back: `dev` moved twice during this delivery, so the tip was
+rebased and re-certified rather than merged on its first green run.
+
+## The CI failure that was not ours
+
+The pre-rebase head failed one job, `test 4/4`, in
+`prompt probe process lifecycle > the last cancellation drains the exact child`.
+Investigation attributed it to that test's final parent-side PID poll expiring at
+its 15-second internal deadline: every preceding assertion passed, the replacement
+command had already observed the old child gone, and the same test passed on macOS
+in the same run. `src/codex/prompt-text-probe.ts` imports `node:fs` only for
+`existsSync`/`statSync` and never calls the changed writers. A same-head rerun
+passed; the rebased head passed 25/25 on the first attempt.
+
+Worth recording honestly: the investigation could not name the exact mechanism.
+The replacement considered the PID absent while the parent still considered it
+alive, which PID reuse, runtime liveness behavior, or a real observation defect
+could all explain. It is a flake by evidence of non-reproducibility, not by proof.
+
+## Follow-up left open
+
+Three exclusive opens under `src/lab/` share the numeric spelling this track
+replaced: `ledger/store.ts` (two) and `public/private-file.ts`. Lab is opt-in and
+off the core request path, so they stayed out rather than widening a config-surface
+fix. The read/write sites in `artifacts/secure-fs.ts` need individual treatment
+because `"wx"` would drop read access; they are not a mechanical substitution.
+
+## Verification boundary
+
+The local product suite, typecheck, and build were **NOT RUN** by owner
+instruction. Acceptance rested on repository CI against the tip
+([run 34153124187](https://github.com/lidge-jun/opencodex/actions/runs/34153124187):
+19 jobs succeeded, 2 skipped, zero failures on the first attempt) plus independent
+read-only audits at each layer. The two skips are the dispatch-only `macos control`
+and Windows shard lanes, so Windows packaging and keyring smoke passed but the
+Windows suite itself did not run.
+
+One limit worth stating plainly: the green PR run tested the tip against the base it
+was rebased onto, and #3940 landed on `dev` between that run and the merge. The
+C-track content is byte-identical either way, and the four files #3940 touched do not
+overlap this change, but the combined tree is certified by the post-merge `dev` run
+rather than by the PR run. That run has since completed:
+[run 34153892496](https://github.com/lidge-jun/opencodex/actions/runs/34153892496) on
+`6188458ae` succeeded, 19 jobs and 2 skips, so the landed combined tree is certified.
diff --git a/devlog/_fin/260908_d_group_test_infra_stack/000_plan.md b/devlog/_fin/260908_d_group_test_infra_stack/000_plan.md
new file mode 100644
index 00000000000..35b2a65c67e
--- /dev/null
+++ b/devlog/_fin/260908_d_group_test_infra_stack/000_plan.md
@@ -0,0 +1,113 @@
+# D-group test-infrastructure delivery as a single-CI manual stack
+
+## Objective
+
+Land the two D-group test-infrastructure items on `dev` as one dependency-ordered
+branch chain whose **tip is the only pull request**, so the cumulative tree is
+verified by exactly one Cross-platform CI run. Merge the tip once that run is
+green, then settle the original pull requests and any linked issues.
+
+| Layer | Source | Content |
+|---|---|---|
+| 1 (bottom) | PR #3924 by @luvs01 | `scripts/test.ts` keeps captured lane output after a timeout; runner regressions; contributing note |
+| 2 (tip) | PR #3930 by @luvs01 | `tests/providers/cursor/cursor-stream-health.test.ts` load-scaled watchdog budgets |
+
+Both source pull requests carry exactly one commit each, authored by `luvs01`
+(`27862058+luvs01@users.noreply.github.com`), so `git cherry-pick -x` preserves
+authorship without needing a reconstructed `Co-authored-by` trailer. The trailer is
+added to the tip pull-request description anyway, because the repository squashes
+and `.github/scripts/pr-carry-attribution.cjs` reads the trailer, not prose.
+
+## Why a stack, and why only one pull request
+
+`.github/workflows/ci.yml` triggers on a bare `pull_request:` with no base-branch
+filter. That is deliberate — the comment in the file records that a
+`branches: [main, dev]` filter once silently excluded stacked child pull requests.
+The consequence for this unit is mechanical: **every open pull request starts a
+Cross-platform CI run**, whatever its base. A two-pull-request stack therefore costs
+two runs, and a child pull request based on the parent's head costs one more.
+
+The only way to get a single run covering both changes is to give the stack exactly
+one pull request, at the tip, based on `dev`. The lower layer is pushed as a branch
+for provenance and review navigation, and never gets a pull request of its own.
+Pushing a branch does not start CI either: `ci.yml`'s `push:` trigger is pinned to
+`branches: [main, preview, dev]`, and this stack pushes neither.
+
+The tip run covers the PR-enabled producers, not every job in the file. `changes`
+sets `ci: true` for `tests/**` and `scripts/**` (`ci.yml:193-194`), which this stack
+touches, so the four Linux shards, `gates`, `storage policy`, `api usage`,
+`platform-macos`, `keyring` and `docker smoke` all execute. Three job families do
+**not** run on a pull request and must never be reported as passing evidence:
+
+| Job | Guard | Status on this PR |
+|---|---|---|
+| `windows /6` | `github.event_name == 'workflow_dispatch' && (inputs.lane == '' \|\| inputs.lane == 'all')` (`ci.yml:742-743`) | SKIPPED BY WORKFLOW |
+| `macos control` | `github.event_name == 'workflow_dispatch'` (`ci.yml:633`) | SKIPPED BY WORKFLOW |
+| `npm-global ` | `needs.changes.outputs.packaging == 'true'` (`ci.yml:943`); the packaging allowlist (`ci.yml:215-229`) excludes all four files | SKIPPED BY WORKFLOW |
+
+That exclusion is acceptable for this unit: nothing here ships in the package tree,
+and `scripts/test.ts` is the test runner rather than runtime source. The Windows
+lane is dispatch-only for every ordinary pull request in this repository, so
+requiring it here would be a new policy, not this unit's job.
+
+## Dependency order
+
+Layer 1 is the runner change; layer 2 is a fixture that the runner executes. Ordering
+them the other way would put a test-timing change under an unverified runner. The
+order is a build-order statement, not an effort estimate.
+
+## Work phases
+
+| Phase | Outcome |
+|---|---|
+| wp0 | This roadmap: stack shape, single-trigger proof, merge/close order, attribution |
+| wp1 | Build both layers locally on fresh `origin/dev` with `cherry-pick -x` |
+| wp2 | Push both branches with `--no-verify`; open exactly one pull request (tip → `dev`) |
+| wp3 | Record tip CI, merge the tip, settle #3924/#3930 and linked issues |
+
+Diff-level detail for each phase: `010_phase1_stack_build.md`,
+`020_phase2_publish.md`, `030_phase3_merge_and_settle.md`.
+
+## Constraints in force
+
+The owner set these for this unit, and they override the repository's default
+verification habits:
+
+- **No local suite.** No `bun run test`, `bun test`, `bun run test:changed`,
+ `bun run typecheck`, or build used as a gate. Every such row is recorded
+ `NOT RUN (owner instruction)`, never as a pass.
+- **Push with `--no-verify`.** Local hooks are skipped by instruction.
+- **CI on the tip only.** Never open a pull request for a lower layer.
+- **One green run, then merge.** The tip's exact head SHA is the product gate.
+- **Preserve original authorship** for carried work.
+- **Close linked issues** at the moment the change is on `dev`.
+
+## Verification model
+
+The product evidence is the hosted Cross-platform CI run on the tip's exact head
+SHA — run id, head SHA, per-job conclusions — read as a job matrix, not as the
+aggregate `ci` summary alone. The three dispatch-only or packaging-gated job
+families above are recorded SKIPPED BY WORKFLOW.
+
+Merge additionally requires the current gate checks to be green on that same head:
+`enforce-target` and `hygiene` (`enforce-pr-target.yml:679-692` folds deterministic
+hygiene failures into its verdict; `pr-hygiene.yml:236-238` fails and labels on a
+violation), plus resolution of any actionable automated review finding.
+
+Landing evidence is the squash SHA GitHub returns, proven to be an ancestor of
+fetched `origin/dev`, with its tree compared against the reviewed tip. Local checks
+are `NOT RUN` by instruction and are never reported as passing.
+
+A verifier honesty note, since this unit's plan names commands it will not run:
+`bun run test` would observe `scripts/test.ts` and both test files, and
+`bun run typecheck` would observe `scripts/test.ts`. Both are in scope for the
+change and both are withheld by owner instruction, so their acceptance rows are
+delegated to hosted CI rather than claimed locally.
+
+## Terminal outcomes
+
+- **DONE** — tip CI green on its exact head, tip merged into `dev`, #3924 and #3930
+ settled with authorship preserved, linked issues closed, evidence recorded.
+- **BLOCKED** — a required merge right is missing, or CI fails for a cause outside
+ these four files.
+- **NEEDS_HUMAN** — a policy decision beyond restoring existing behavior.
diff --git a/devlog/_fin/260908_d_group_test_infra_stack/001_audit_record.md b/devlog/_fin/260908_d_group_test_infra_stack/001_audit_record.md
new file mode 100644
index 00000000000..b6689813579
--- /dev/null
+++ b/devlog/_fin/260908_d_group_test_infra_stack/001_audit_record.md
@@ -0,0 +1,53 @@
+# Audit record — roadmap gate
+
+An independent reviewer (a separate context, `gpt-6-astra` at high effort) audited
+the roadmap before any branch was built. Three rounds ran; the first two failed.
+The findings are recorded here because they changed the plan, and because two of
+them would have produced a false completion claim.
+
+## Round 1 — FAIL, four blocking defects
+
+1. **Overstated CI coverage.** The plan promised platform and packaging coverage
+ from the tip pull-request run. In fact `windows /6` and `macos control` are
+ `workflow_dispatch`-only (`ci.yml:633`, `742-743`), and `npm-global` needs
+ `packaging == 'true'`, which the packaging allowlist (`ci.yml:215-229`) does not
+ set for any of the four files. Fixed by adding an explicit RUN vs
+ SKIPPED BY WORKFLOW matrix and forbidding the skipped families from being
+ reported as passes.
+2. **CI success treated as sufficient for merge.** `enforce-target` folds
+ deterministic hygiene failures into its verdict (`enforce-pr-target.yml:679-692`)
+ and `pr-hygiene` fails and labels on a violation (`pr-hygiene.yml:236-238`).
+ `MAINTAINERS.md:61` also requires the integration decision and exact-head
+ verification to be recorded. Fixed by adding those gates and the record step.
+3. **Wrong ancestry object.** The plan checked whether the tip commit was an
+ ancestor of `dev`. A squash merge never makes the tip an ancestor, so that check
+ would have failed on a perfectly good landing — or worse, been waved through.
+ Fixed by recording the squash SHA GitHub returns and testing that.
+4. **Attribution assumed rather than controlled.** The repository sets
+ `squash_merge_commit_message: COMMIT_MESSAGES`, so the pull-request description
+ is not the landed message. A description trailer satisfies the hygiene checker
+ and still leaves the contributor uncredited in the commit. Fixed by supplying the
+ squash body explicitly and verifying the landed trailer before closing anything.
+
+## Round 2 — FAIL, two blocking defects
+
+1. **Missing administrator bypass.** `Protect dev` requires an approving review and
+ code-owner review, so the merge call is refused without `--admin`. The plan named
+ the policy exception without naming the mechanism that exercises it.
+2. **Bot findings mistaken for all findings.** The gate covered automated review
+ findings but not human ones. `MAINTAINERS.md:62-64` requires outstanding
+ maintainer change requests to be resolved or explicitly withdrawn.
+
+## Round 3 — PASS
+
+The reviewer set the phase-1 acceptance bar: freshly fetched base SHA, both
+constructed commit SHAs, evidence that layer 1 follows the base and layer 2 follows
+layer 1, both authors reading `luvs01`, both `-x` provenance lines, per-layer and
+cumulative name/numstat comparisons, blob comparisons against the source pull
+requests, and the roadmap commit accounted for separately so it stays out of the
+four-file implementation delta.
+
+## Standing note
+
+Local suite, typecheck and build are **NOT RUN** for this unit by owner
+instruction. That is a recorded absence of evidence, not a pass.
diff --git a/devlog/_fin/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
new file mode 100644
index 00000000000..003cd0ad82c
--- /dev/null
+++ b/devlog/_fin/260908_d_group_test_infra_stack/010_phase1_stack_build.md
@@ -0,0 +1,54 @@
+# Phase 1 — Build the stack locally
+
+Base: fetched `origin/dev`. Both source commits live in the `luvs01` remote
+(`https://github.com/luvs01/opencodex.git`), already configured in this checkout.
+
+## Commands
+
+```sh
+git fetch origin dev
+git fetch luvs01 e24163231edeaa09a30a99ca1746e3b573af78ae 141077f7270e2f2a0564fb036d091f0cf793b784
+
+# Layer 1 — PR #3924
+git switch -c codex/260908-d-group-l1-test-runner-output origin/dev
+git cherry-pick -x e24163231edeaa09a30a99ca1746e3b573af78ae
+
+# Layer 2 — PR #3930, tip
+git switch -c codex/260908-d-group-l2-cursor-watchdog
+git cherry-pick -x 141077f7270e2f2a0564fb036d091f0cf793b784
+```
+
+`cherry-pick -x` keeps the original author identity
+(`luvs01 <27862058+luvs01@users.noreply.github.com`>) and appends the
+`(cherry picked from commit ...)` provenance line. No `Co-authored-by` trailer is
+needed on the commits themselves because authorship is not being reassigned. The
+trailer goes in the tip pull-request description for hygiene acceptance; phase 3
+separately supplies and verifies the trailer on the landed squash commit, which is
+the only thing GitHub reads for contributor credit.
+
+## Expected change map
+
+| Layer | File | Change |
+|---|---|---|
+| 1 | `scripts/test.ts` | +81 −8 — incremental capture, retained output on timeout, bounded drain, incomplete-capture exit policy |
+| 1 | `tests/ci-workflows/test-runner.test.ts` | +147 −1 — regressions for timeout/failure/success output, split UTF-8, open pipes, read failure |
+| 1 | `docs-site/src/content/docs/contributing.md` | +6 — documents the timeout and incomplete-capture behavior |
+| 2 | `tests/providers/cursor/cursor-stream-health.test.ts` | +59 −26 — one scaled silence budget S, 2S heartbeat-only, ≥3S observed progress after first received text |
+
+Cumulative tip versus `origin/dev`: exactly those four files.
+
+## Conflict expectation
+
+None. The two file sets are disjoint, and the Cursor test file plus its
+`tests/helpers/ci-watchdog.ts` import carry identical blob SHAs at `dev` and at
+#3924's head (`dc7b572bf1` and `f8adcfe3d9`), so layer 2's preimage is unchanged by
+layer 1.
+
+## Acceptance
+
+- `git log --format='%an <%ae>'` on both new commits reports `luvs01`.
+- `git diff --name-only origin/dev..tip` lists exactly the four files above.
+- `git diff --stat` matches the per-file counts in the table.
+- Each cherry-picked tree is byte-identical to the source PR head's version of its files.
+
+Local suite: NOT RUN (owner instruction).
diff --git a/devlog/_fin/260908_d_group_test_infra_stack/020_phase2_publish.md b/devlog/_fin/260908_d_group_test_infra_stack/020_phase2_publish.md
new file mode 100644
index 00000000000..2af6acab129
--- /dev/null
+++ b/devlog/_fin/260908_d_group_test_infra_stack/020_phase2_publish.md
@@ -0,0 +1,92 @@
+# Phase 2 — Publish the stack, one pull request only
+
+## Push
+
+```sh
+git push --no-verify origin codex/260908-d-group-l1-test-runner-output
+git push --no-verify origin codex/260908-d-group-l2-cursor-watchdog
+```
+
+`--no-verify` is the owner's instruction for this unit. Neither push starts
+Cross-platform CI: `ci.yml`'s `push:` trigger is limited to
+`branches: [main, preview, dev]` (`ci.yml:26-27`).
+
+## Open exactly one pull request
+
+Tip only, targeting `dev`:
+
+```sh
+gh pr create --repo lidge-jun/opencodex --base dev \
+ --head codex/260908-d-group-l2-cursor-watchdog \
+ --title "fix(test): preserve lane output after timeouts and stabilize the Cursor stream-health watchdog" \
+ --body-file
+```
+
+The lower layer gets **no** pull request. `ci.yml` triggers on a bare
+`pull_request:` with no base filter (`ci.yml:7`), so a second pull request would
+start a second Cross-platform CI run; draft status does not suppress it either —
+no job in `ci.yml` reads a draft condition.
+
+A stacked child pull request based on the layer-1 branch is also unavailable here:
+`enforce-target` grants the wrong-base exemption only when the parent branch has
+its own **open** pull request (`enforce-pr-target.yml:536-537`), which is exactly
+what this design avoids. The tip therefore targets `dev` directly.
+
+## Description requirements
+
+`.github/PULL_REQUEST_TEMPLATE.md` requires Summary, Verification, and Checklist;
+`enforce-target` rejects thin or malformed descriptions. The description must also:
+
+- name both source pull requests (#3924, #3930) and describe the stack layering;
+- carry `Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>`. The
+ hygiene checker reads that trailer from the description or a commit message
+ (`pr-carry-attribution.cjs:190`), and the carry verbs in the description are what
+ make it demand one at all. The description trailer satisfies the gate; it does
+ **not** by itself put the trailer in the landed commit — see phase 3, where the
+ squash body carries it explicitly;
+- state honestly that local suite, typecheck and build were **NOT RUN** by owner
+ instruction, and that hosted CI on this exact head is the verification gate,
+ naming which job families are skipped by the workflow;
+- be substantive: `pr-quality.cjs` strips template boilerplate and requires real
+ content (two substantial sections, or 120+ characters across two blocks), so
+ placeholder bullets fail the gate.
+
+As a maintainer-authored pull request this needs no readiness checklist and no
+`review-ready` label (`enforce-pr-target.yml:766-768`, `1096-1103`). Do not tick a
+local-CI attestation box that was not earned — the owner forbade the local suite.
+
+No GUI files change, so the screenshot rule does not apply.
+
+## Other workflows that will fire
+
+Expected and unavoidable for any pull request: `enforce-target`, `pr-hygiene`,
+`pr-labeler`, `react-doctor`, plus CodeRabbit. `service-lifecycle` does **not**
+fire — none of the four paths is in its allowlist. These are gate/lint signals, not
+the product suite; only Cross-platform CI is the product gate.
+
+## Outcome
+
+Executed 2026-09-08 against base `942c02873`.
+
+| Ref | SHA | Pull request |
+|---|---|---|
+| `codex/260908-d-group-l1-test-runner-output` | `ab06523e6` | none, by design |
+| `codex/260908-d-group-l2-cursor-watchdog` (tip) | `8b81676ac` | [#3940](https://github.com/lidge-jun/opencodex/pull/3940), base `dev` |
+
+Both pushes used `--no-verify`. Neither started Cross-platform CI, as predicted by
+the `push` branch filter. Opening #3940 started exactly one run on `8b81676ac`; the
+first check-runs to appear were `changes`, `select windows runner`, `hygiene`,
+`label`, `resolve-pr` and `react-doctor`, which matches the expected set.
+
+The layer-1 branch has zero pull requests in any state, which is the property that
+keeps the stack to a single CI run.
+
+## Acceptance
+
+- Both branches exist on `origin` at the expected SHAs.
+- `gh pr list --head codex/260908-d-group-l1-test-runner-output` returns empty.
+- Exactly one open pull request has head `codex/260908-d-group-l2-cursor-watchdog`
+ and base `dev`.
+- Exactly one Cross-platform CI run exists for the tip head SHA. "Exactly one" is
+ scoped to the pre-merge candidate: landing on `dev` starts a separate push run,
+ and a base refresh replaces the candidate with a new head and a new run.
diff --git a/devlog/_fin/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
new file mode 100644
index 00000000000..5e6d3ece966
--- /dev/null
+++ b/devlog/_fin/260908_d_group_test_infra_stack/030_phase3_merge_and_settle.md
@@ -0,0 +1,106 @@
+# Phase 3 — Merge the tip, settle the stack
+
+## Gate
+
+The product gate is the Cross-platform CI run on the tip's **exact** head SHA.
+Record run id, head SHA, and each producer's conclusion. A cancelled or superseded
+run is not evidence, and a run on an earlier head is not evidence for the merged
+head. Read the producers, not only the aggregate `ci` check.
+
+Expected to RUN (`ci: true` via `tests/**` and `scripts/**`): four Linux `test`
+shards, `gates`, `storage policy`, `api usage`, `macos /2`, `keyring` (three OS),
+`docker smoke`.
+
+Expected to be SKIPPED BY WORKFLOW, and recorded as such rather than as passes:
+`windows /6` and `macos control` (both `workflow_dispatch`-only), and
+`npm-global ` (needs `packaging == 'true'`, which these four files do not set).
+
+Merge also requires, on the same head:
+
+- `enforce-target` success and `hygiene` success;
+- every actionable automated review finding resolved;
+- **outstanding maintainer change requests resolved or explicitly withdrawn**
+ (`MAINTAINERS.md:62-64`) — read the human reviews immediately before merging, not
+ only the bot findings;
+- a refreshed read of head, base, merge state, and the integrating actor's
+ repository permission immediately before merging.
+
+## Maintainer integration record
+
+`MAINTAINERS.md:59-63` permits a maintainer with `maintain` or `admin` to integrate
+into `dev` without a second approval, and requires the decision and the exact-head
+verification to be recorded in the pull request. Post that record as a comment
+before merging: the integrating maintainer, the exact head SHA, the CI run link,
+the job matrix including the skipped families, and the statement that local suite,
+typecheck and build were NOT RUN by owner instruction.
+
+The `Protect dev` ruleset still requires an approving review and code-owner review,
+so the merge call itself will be refused without an explicit administrator bypass.
+That bypass is the mechanism this policy exception is exercised through, and it is
+conditional: verified maintainer identity with `admin`, base `dev`, every planned
+check green on the exact head, and the integration record posted. It is never a way
+past failing CI or an unresolved objection.
+
+## Merge order
+
+1. Refresh the tip against `dev` if `dev` moved; a moved base means the green run
+ no longer describes the merge result, so re-run the gate on the new head.
+2. Merge the tip pull request into `dev` with an explicit squash body. The repository
+ sets `squash_merge_commit_message: COMMIT_MESSAGES`, so the landed message is not
+ the pull-request description: supply it directly and make it carry the trailer.
+
+ ```sh
+ gh pr merge --repo lidge-jun/opencodex --squash --admin \
+ --match-head-commit --body-file
+ ```
+
+ `--body-file` supplies the **merge commit body**, which under `--squash` is the
+ squash commit body, replacing the repository's `COMMIT_MESSAGES` default
+ (verified against the installed `gh` 2.91.0 help). `--subject` is optional and
+ only controls the title. `--match-head-commit` refuses the merge if the head
+ moved after the gate was read.
+
+ The squash body must contain, on its own line:
+
+ ```text
+ Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
+ ```
+
+3. Read the landed squash SHA from GitHub, fetch `origin/dev`, and prove:
+ `git merge-base --is-ancestor origin/dev` exits 0, the landed commit
+ message contains the trailer, and the four files on `dev` match the reviewed tip.
+
+Merging is an external state change and stays user-authorized.
+
+## Settling the source pull requests
+
+Both #3924 and #3930 were carried by `cherry-pick -x`, so GitHub will not mark them
+merged automatically. After the tip lands:
+
+- Verify the landed commit's trailer **before** closing either source pull request.
+ A closing comment is prose; only the trailer is contributor-graph data
+ (`CREDITS.md` exists because that distinction was missed 27 times).
+- Then close #3924 and #3930 with a comment naming the landed squash SHA, the tip
+ pull request, and the preserved authorship.
+- Do not delete the contributor branches on the fork; they are not ours.
+
+## Linked issues
+
+Neither #3924 nor #3930 declares a closing issue reference
+(`closingIssuesReferences` is empty for both). If none is discovered during the
+cycle, the "close linked issues" obligation is satisfied vacuously and recorded as
+such. Any issue found to be resolved by this landing is closed at the moment the
+change is on `dev`, with a comment naming the commit.
+
+## Acceptance
+
+- Tip CI: run id + head SHA + per-job conclusions on the merged head, with the three
+ skipped job families named as skipped.
+- `enforce-target` and `hygiene` green on that head; maintainer-integration record
+ posted on the pull request.
+- `git merge-base --is-ancestor origin/dev` exits 0 after fetch, the
+ landed commit carries the `luvs01` trailer, and the four files on `dev` match the
+ reviewed tip.
+- #3924 and #3930 closed after that verification; no lower-layer pull request was
+ ever opened.
+- Linked-issue status stated explicitly (closed, or none exists).
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 00000000000..12177be6ba3
--- /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/260908_provider_runtime_stack/000_plan.md b/devlog/_fin/260908_provider_runtime_stack/000_plan.md
new file mode 100644
index 00000000000..c8da992329c
--- /dev/null
+++ b/devlog/_fin/260908_provider_runtime_stack/000_plan.md
@@ -0,0 +1,65 @@
+# 000 — Plan and live manifest
+
+Unit: `devlog/_plan/260908_provider_runtime_stack`. Session `01a080e2-1dfc-7082-bff8-5043215bdd35`.
+Snapshot: 2026-09-08T12:00Z (fetch), `origin/dev` = `29bb221c3`
+(`Merge pull request #4021 from lidge-jun/codex/release-248-record`).
+Carry worktree: `/private/tmp/ocx-prs-stack-01a080e2` (linked worktree of the main checkout;
+`core.worktree` unset, toplevel verified).
+
+## Objective
+
+Land the open provider-runtime contributor PRs on `dev` as one ordinary manual dependent PR
+stack, integrated with the repository's provider discipline (test layout, provider marks,
+docs-site sections, contributor attribution), and merge the stack bottom-up into `dev`
+after a single green final-head CI run. Constraints given by the maintainer:
+
+- Never run the local product suite, typecheck, build, or install. Every one of those is
+ labelled NOT RUN in the delivery record. Hosted CI on the final head is the only proof.
+- Every mutating Git command runs as `git -c core.hooksPath=/dev/null` (the repository
+ `postmerge` hook can otherwise install dependencies and typecheck). Push with
+ `--no-verify`.
+- CI runs once, on the top of the stack. Merge only if that head is green.
+- Ordinary dependent PR bases, no GitHub native stack registration (DEV-STACK-OPT-IN-01).
+- Cherry-pick, reimplement, squash, or rebase are all permitted. Original authors stay
+ as commit authors (`cherry-pick -x`) or in a `Co-authored-by` trailer.
+- Subagents: `anthropic/claude-opus-5` unlimited; Aside browser delegation unlimited.
+- Out of scope: release/publish, `main`/`preview` promotion, unrelated subsystems.
+
+## Work-phase map (one PABCD cycle each)
+
+| WP | Scope | Doc |
+|----|-------|-----|
+| wp1 | Docs-only roadmap: this manifest, layer plan (010), conflict map (011), mark sourcing (012), secondary dispositions (013) | 000-013 |
+| wp2 | Carry L1-L3 (CodeBuddy #3340, Qoder Global #3349, Qoder CN #3350) onto `dev` with layout registration | 020 |
+| wp3 | L4 marks + display names + docs-site sections + attribution; accepted secondary layers | 030 |
+| wp4 | Publish, final-head CI, bottom-up admin merge, ancestry proof, closeouts, delivery record | 040, 060 |
+
+## Manifest (exact head at snapshot)
+
+| PR | Author | Head | Base | Mergeable vs dev | +/- | Files | Commits | Draft |
+|----|--------|------|------|------------------|-----|-------|---------|-------|
+| #3340 | Flowershangfromthebranches | `4b705e92d` | dev | clean (merge-tree) | 2108/6 | 17 | 4 | yes |
+| #3349 | Flowershangfromthebranches | `4ac98bd4d` | dev | CONFLICTING (`tests/providers/provider-connection-test.test.ts`, import-path only) | 2683/14 | 30 | 4 (3 shared with #3340) | yes |
+| #3350 | Flowershangfromthebranches | `a4e805084` | dev | conflicts inherited from #3349 | 2834/16 | 30 | 5 (4 shared) | yes |
+| #3010 | Liang-Psych | `2e3582328` | dev | CONFLICTING; OAuth/private-protocol design the maintainer review rejected | 1474/2 | 11 | 18 | yes |
+
+The three Flowershangfromthebranches PRs are already a contributor-declared chain
+(#3340 → #3349 → #3350); #3349 and #3350 GitHub diffs include the lower layers because
+each targets `dev`. The carry keeps that chain shape but rebases each layer onto its
+parent so every PR diff is layer-only (DEV-STACK-03).
+
+## Maintainer review state carried into this unit
+
+The prior maintainer reviews (grok-bot, 2026-09-03) on all three PRs left these open items,
+now dispositioned here:
+
+| Item | Disposition |
+|------|-------------|
+| AUP / terms acceptance for headless CLI proxy routing (CodeBuddy, Qoder) | Maintainer decided in this session by authorizing the landing. Recorded in 040. |
+| Provider marks missing in `gui/src/provider-icons.ts` | wp3, per the Meta precedent `81a1fc1cc` (#3338): first-party SVG with source notes, or documented initials tile when terms forbid. See 012. |
+| docs-site guide lacks a Qoder Global/CN section | wp3. CodeBuddy section already exists at `guides/providers.md:620`. |
+| Shared `coding-agent/protocol.ts` error classification broadened in the Qoder commit | Kept in L2 where the contributor put it; audit (wp2 A-phase) checks CodeBuddy fixture coverage. |
+| `qoder` promoted from free-directory reference id to runtime seed with `preserveCustomDestination` | Kept; parity test in the carried commits asserts the flag. |
+| #3010 relationship | Superseded by #3350 once landed; close with credit to Liang-Psych. |
+| Tests at `tests/` root | Blocker on current `dev`: layout guard. Fixed per layer in wp2. |
+| Draft readiness checklist (contributor-side) | Not applicable; maintainer carries the PRs under admin authority. Originals close as superseded. |
diff --git a/devlog/_fin/260908_provider_runtime_stack/010_layer_plan.md b/devlog/_fin/260908_provider_runtime_stack/010_layer_plan.md
new file mode 100644
index 00000000000..7735d51f345
--- /dev/null
+++ b/devlog/_fin/260908_provider_runtime_stack/010_layer_plan.md
@@ -0,0 +1,34 @@
+# 010 — Layer plan
+
+Stack shape (merge bottom-up, ordinary dependent bases):
+
+| # | Branch | Base | Thesis | Source commits |
+|---|--------|------|--------|----------------|
+| 1 | `codex/prs-l1-codebuddy` | `dev` | CodeBuddy Global/CN headless CLI providers + shared `coding-agent` runtime | #3340: `7e56b6399`, `f651611f1`, `18530f8e8`, `4b705e92d` (cherry-pick -x) + layout fix commit |
+| 2 | `codex/prs-l2-qoder-global` | L1 | Qoder Global PAT provider, account-scoped live model discovery | #3349: `4ac98bd4d` (cherry-pick -x, import-path conflict resolved) + layout fix commit |
+| 3 | `codex/prs-l3-qoder-cn` | L2 | Qoder CN PAT profile | #3350: `a4e805084` (cherry-pick -x) |
+| 4 | `codex/prs-l4-marks-docs` | L3 | Provider marks, display names, docs-site Qoder section, CREDITS | new maintainer commits |
+| 5+ | `codex/prs-l5-*` | L4 | Secondary PRs accepted by 013 triage, one layer each | cherry-pick -x |
+
+Layer rules:
+
+- Each layer builds at its own tip. The layout-guard fix for a layer's tests lives in
+ that layer, not deferred upward.
+- Original author preserved by `cherry-pick -x` (author field + `(cherry picked from
+ commit …)` line). Maintainer-authored repair commits carry no trailer because they
+ are not the contributor's work; the PR body names the source PR.
+- PR bodies use the repository template and carry the stack map (DEV-STACK-03).
+- Only the top layer's head gets CI. Lower PRs are opened for review navigation and
+ merge order; their own PR CI may run (`pull_request` trigger) but is not the gate.
+
+Verification plan (hosted only):
+
+1. Push all layers with `--no-verify`.
+2. If the top PR's `pull_request` CI skips platform lanes, dispatch
+ `gh workflow run ci.yml -R lidge-jun/opencodex --ref -f lane=all`.
+3. Record run id, every job conclusion; skipped/cancelled are not passing.
+4. Merge bottom-up with `--admin`, retarget the next child to `dev` after each parent
+ lands, keep parent branches until no open child targets them.
+5. After the top merge: `git fetch origin dev`; every merge SHA must satisfy
+ `git merge-base --is-ancestor origin/dev`; `git rev-parse origin/dev^{tree}`
+ must equal the certified head's tree (or a diff limited to merge-commit metadata).
diff --git a/devlog/_fin/260908_provider_runtime_stack/011_conflict_map.md b/devlog/_fin/260908_provider_runtime_stack/011_conflict_map.md
new file mode 100644
index 00000000000..9c691953606
--- /dev/null
+++ b/devlog/_fin/260908_provider_runtime_stack/011_conflict_map.md
@@ -0,0 +1,24 @@
+# 011 — Conflict map (measured)
+
+Method: `git merge-tree --write-tree origin/dev refs/pr/` and the actual cherry-picks in
+the carry worktree.
+
+| Layer | Conflicting file | Nature | Resolution |
+|-------|------------------|--------|------------|
+| L1 | none | `tests/providers/provider-registry-parity.test.ts` auto-merged | — |
+| L1 | `tests/codebuddy-adapter.test.ts`, `tests/codebuddy-protocol.test.ts` | Not a git conflict; layout guard (`tests/test-layout.test.ts`) rejects root test files since `260905_test_modularization_and_windows` | Move to `tests/providers/`, rewrite `../src` → `../../src`, `./helpers` → `../helpers`; register in `scripts/test-layout/layout.json` `explicit` and `tests/fixtures/test-layout-expected.json`. Commit `769e4208f`. |
+| L2 | `tests/providers/provider-connection-test.test.ts` | Import block: dev moved the file into `tests/providers/`; the PR adds one `setFetchQoderModelsForTests` import against the old path | Keep dev's `../../src` paths, add the Qoder import at the same depth. |
+| L2 | `tests/qoder-adapter.test.ts`, `tests/qoder-live-models.test.ts` | Layout guard, as L1 | Same move + registration. Commit `094cb93d0`. |
+| L3 | `tests/providers/qoder-adapter.test.ts`, `tests/providers/qoder-live-models.test.ts` | The CN commit edits the same import lines the L2 layout commit moved | Take the CN import set (adds `QODER_CN_PROFILE`, `resolveQoderProfile`) at the new depth. |
+
+Auto-merged without conflict (git content merge, needs the wp2 audit to confirm semantics):
+`README.md`, `docs-site/.../guides/providers.md`, `docs-site/.../reference/configuration/providers.md`,
+`src/codex/catalog/provider-fetch.ts`, `src/providers/registry.ts`,
+`src/server/management/provider-routes.ts`, `tests/adapters/adapter-*-conformance.test.ts`,
+`tests/adapters/adapter-registry-authority.test.ts`, `tests/providers/provider-registry-parity.test.ts`.
+
+Known dev-side drift since the PR base (`81a1fc1cc`, 2026-09-03) that touches carried files:
+provider namespace ownership (`bbea77a48`), Nous catalog limits (`5cd71ec91`), OrcaRouter
+PKCE (`c41232aa5`), keychain restore ownership (`924b65799`), BigModel repairs. The wp2 audit
+reads each of these against the carried edits in `provider-fetch.ts`, `model-cache.ts`, and
+`registry.ts`.
diff --git a/devlog/_fin/260908_provider_runtime_stack/012_mark_sourcing.md b/devlog/_fin/260908_provider_runtime_stack/012_mark_sourcing.md
new file mode 100644
index 00000000000..519fca90726
--- /dev/null
+++ b/devlog/_fin/260908_provider_runtime_stack/012_mark_sourcing.md
@@ -0,0 +1,23 @@
+# 012 — Mark sourcing decision
+
+Research agent (claude-opus-5) verified on the public web, 2026-09-08. Assets held outside the
+repo at `/tmp/ocx-marks/` until wp3 commits them.
+
+| id | Decision | File | Source | Terms basis |
+|----|----------|------|--------|-------------|
+| `qoder` | ship | `qoder.svg` | `https://qoder.com/favIcon.svg` (declared site icon; 73,379 B; viewBox `0 0 206 206`; byte-identical on `qoder.cn`, `qoder.com.cn`, and the schema.org Organization logo URL) | Qoder ToS (BRIGHT ZENITH, 2026-04-29) reserves rights generally, no mark-use prohibition; same posture as `meta.svg` |
+| `qoder-cn` | ship, shared asset | `qoder.svg` | same file | CN agreement (通义云启(杭州)信息技术有限公司 + Alibaba Cloud, 2026-05-20) §五(a) reserves 商标 rights without restricting third-party use |
+| `codebuddy` | initials tile, documented | none | mark exists (`…/web/ide/logo.svg`) | CodeBuddy service agreement §9.3 "Tencent Logo": no use of Tencent brand features "under any circumstances" without written consent |
+| `codebuddy-cn` | initials tile, documented | none | same | same clause on `codebuddy.cn/document/term` |
+
+Wiring consequences:
+
+- `gui/tests/provider-icons.test.ts` derives the asset stem from `providerId.split("-")[0]`,
+ so committing `qoder.svg` fails the unwired-asset check for both `qoder` and `qoder-cn`
+ until each has its own alias row (the Meta commit pinned both ids for the same reason).
+- Do not mask `qoder.svg`: light plate + dark glyph, both neutral inks, 94.5% opaque; a
+ mask collapses it into a filled box (README "plate problem").
+- Display names: `qoder` → "Qoder", `qoder-cn` → "Qoder CN", `codebuddy` → "CodeBuddy",
+ `codebuddy-cn` → "CodeBuddy CN".
+- The CodeBuddy refusal goes into `gui/public/provider-icons/README.md` because no test
+ can detect an absent mark; without the note a later pass would re-fetch the logo.
diff --git a/devlog/_fin/260908_provider_runtime_stack/013_secondary_dispositions.md b/devlog/_fin/260908_provider_runtime_stack/013_secondary_dispositions.md
new file mode 100644
index 00000000000..07f7937551a
--- /dev/null
+++ b/devlog/_fin/260908_provider_runtime_stack/013_secondary_dispositions.md
@@ -0,0 +1,23 @@
+# 013 — Secondary PR dispositions (bounded triage, read-only)
+
+Method: `gh pr view`, `git merge-tree --write-tree` against `origin/dev` and against the L3
+head `85ad0a29a`, blob reads. No bun command run. Triage agent: claude-opus-5.
+
+| PR | Author | Size | Conflicts vs dev / vs stack | C4 surface | Maintainer state | Layout | Verdict |
+|---|---|---|---|---|---|---|---|
+| #3990 Hermes source-preserving YAML | rrmlima | 5 files +106/−44 | none / none | no | approved ("머지하세요") | already mapped | INCLUDE → L5 |
+| #3988 Gemini model-tail continue nudge | rrmlima | 2 files +51/−14 | none / none | no | approved after CI | already mapped | INCLUDE → L6 |
+| #3833 Command Code native integration | rrmlima | 9 files +256/−4 | none / none | no | stale review mostly fixed | layout trap: `command-code-client.test.ts` seeds to `providers` (`layout.json:14`), explicit `clients` entry would trip the seed-mismatch check (`test-layout-tooling.test.ts:282`); needs rename or `pinnedOverrides` — design call | DEFER |
+| #3952 openai-chat freeform + Moonshot Responses | yxr1995-maker | 9 files +467/−11 | none / none | no | "지금 형태로는 merge하지 마세요"; bundles three changes; `apply-patch-envelope.ts:51-59` fence stripping can truncate legit bodies; flips `moonshot` adapter default | DEFER (split required) |
+| #3639 EntraID for Azure Foundry | chrisoro | 39 files +590/−62 | none / none | yes (new `@azure/identity` dep, new credential path) | hygiene-blocked, security review required | — | REJECT for this stack |
+| #3283 Antigravity pool + Gemini 3.8 | vanch007 | 14 files +960/−53 | 2 / 2 (`responses/parser.ts`, `server/responses/core.ts`) | yes | "merge 비추천"; competes with #2562 | — | REJECT |
+| #3282 Copilot context tier | Simon-Opopeee | 39 files +521/−14 | 8 / 8 | yes | provider guard missing, screenshot missing, hygiene-blocked | root test file | REJECT |
+| #2230 Gemini OAuth accounts | ppvia | 33 files +1637/−61 | 16 / 16 | yes (embedded OAuth client secret) | maintainer-sponsored security review mandatory | unregistered tests | REJECT |
+
+#3990 and #3988 are pairwise clean with each other and with every other candidate
+(`merge-tree` exit 0 for all combinations). Both are runtime-scope, no auth/credential/workflow
+surface, and the maintainer already approved their content. They become L5 and L6 above the
+marks layer, each cherry-picked with `-x` to keep rrmlima as author.
+
+DEFER/REJECT items are not closed by this unit; their disposition is recorded here for the
+next triage pass.
diff --git a/devlog/_fin/260908_provider_runtime_stack/020_wp2_carry.md b/devlog/_fin/260908_provider_runtime_stack/020_wp2_carry.md
new file mode 100644
index 00000000000..32677407eca
--- /dev/null
+++ b/devlog/_fin/260908_provider_runtime_stack/020_wp2_carry.md
@@ -0,0 +1,23 @@
+# 020 — wp2: carry L1–L3 with layout registration
+
+Status at write time: carried in the worktree, unpublished. Heads: L1 `769e4208f`,
+L2 `094cb93d0`, L3 `85ad0a29a` (pre-audit-fix).
+
+## Audit round 1 (claude-opus-5, adversarial, read-only) — NEAR-PASS
+
+| # | Finding | Disposition |
+|---|---------|-------------|
+| 1 | Qoder catalog branch in `src/codex/catalog/provider-fetch.ts` (4 hint calls, ~1598–1628) omits `captured.effectiveAlias`, which `45045623b` (#3601) threaded through every sibling branch. Git auto-merged because lines do not overlap. | FOLD — maintainer fix commit on L2 appends the argument to all four calls. |
+| 2 | `tests/adapters/adapter-tool-conformance.test.ts` exempts `codebuddy`/`qoder` with a bare `continue`; a future tool bridge would keep passing silently. | RESIDUAL — v1 contract is `--tools ""`, documented in registry notes and docs-site. A guard test cannot be validated locally under the no-local-suite rule; deferred to a follow-up that can run it. |
+| 3 | `src/adapters/coding-agent/protocol.ts:198` matches bare `authentication`, so vendor text like "authentication service degraded" becomes a 401 `invalid_api_key`, which drives reauth messaging and key-pool rotation. | FOLD — anchor to credential verdicts (`authentication (?:failed|error|required)`, `unauthorized`). Existing fixture "Not logged in; invalid token" still classifies 401. |
+| 4 | `qoder`/`qoder-cn` seed `noVisionModels` with the full roster, advertising image input the adapter rejects. | REBUT — this is the repository convention (`registry.ts:912`, parity test :388, CodeBuddy CN roster §二十九): membership routes images through the vision sidecar and the fail-closed strip applies to every such provider. The adapter's 400 is the defense when an image reaches it without the sidecar path. |
+
+Non-blocking notes carried: CodeBuddy Global roster has no `noVisionModels` (static, vendor
+manifest); `docs/qoder-cli-provider.md` lives outside docs-site (kept, wp3 adds the published
+section); `--effort` vs `--reasoning-effort` rests on vendor manifests.
+
+Clean under audit: registry contract shape, seed parity fields, `qoder` free-directory
+promotion + `preserveCustomDestination`, `authorityIdentity` backward compatibility,
+connection-test path ordering, layout-guard JSON (delta is exactly the four new keys),
+privacy (PAT redaction, allowlisted child env, SHA-256 fingerprint), CI path (no docs-site
+build or provider enumeration on `pull_request`).
diff --git a/devlog/_fin/260908_provider_runtime_stack/030_wp3_marks_docs.md b/devlog/_fin/260908_provider_runtime_stack/030_wp3_marks_docs.md
new file mode 100644
index 00000000000..0093ab15f1f
--- /dev/null
+++ b/devlog/_fin/260908_provider_runtime_stack/030_wp3_marks_docs.md
@@ -0,0 +1,15 @@
+# 030 — wp3: L4 marks/docs/credits, L5–L6 secondary layers
+
+L4 `codex/prs-l4-marks-docs` (maintainer-authored):
+
+- `gui/public/provider-icons/qoder.svg` from `/tmp/ocx-marks/qoder.svg` (verbatim).
+- `gui/src/provider-icons.ts`: aliases `qoder`/`qoder-cn` → `qoder.svg`; display names
+ Qoder, Qoder CN, CodeBuddy, CodeBuddy CN. No CodeBuddy asset (012).
+- `gui/public/provider-icons/README.md`: Qoder provenance + CodeBuddy refusal note (012 text).
+- `docs-site/src/content/docs/guides/providers.md`: "Official Qoder CLI (Global & CN)"
+ section after the CodeBuddy section; reference/configuration adapter list adds `qoder`.
+- `CREDITS.md`: not needed — original commits keep the contributor as author.
+
+L5 `codex/prs-l5-hermes-yaml`: cherry-pick -x `a1fe9caeb` (#3990, rrmlima).
+L6 `codex/prs-l6-gemini-tail`: cherry-pick -x `1837b8f99` (#3988; commit author is
+`root`, so add `Co-authored-by: rrmlima` via the PR body/merge commit).
diff --git a/devlog/_fin/260908_provider_runtime_stack/040_wp4_publish_merge.md b/devlog/_fin/260908_provider_runtime_stack/040_wp4_publish_merge.md
new file mode 100644
index 00000000000..cf056aafcf3
--- /dev/null
+++ b/devlog/_fin/260908_provider_runtime_stack/040_wp4_publish_merge.md
@@ -0,0 +1,16 @@
+# 040 — wp4: publish, CI, merge, prove, close
+
+1. Push six branches `--no-verify` with `-c core.hooksPath=/dev/null`.
+2. Open PRs bottom-up with explicit `--base` (L1→dev, L2→L1, …), template body + stack map.
+3. Dispatch `ci.yml` `lane=all` on the L6 head; record run id and every job.
+4. On green: merge L1 with `--admin --match-head-commit`, retarget L2 to `dev`, repeat.
+ Keep parent branches until no child targets them.
+5. Fetch `dev`; assert each merge SHA is an ancestor; compare `dev^{tree}` to the certified
+ L6 tree.
+6. Close #3340/#3349/#3350 superseded (credit Flowershangfromthebranches), #3990/#3988
+ superseded (credit rrmlima), #3010 superseded by the landed Qoder CN PAT provider
+ (credit Liang-Psych).
+7. Write 060 ledger; move unit to `_fin`.
+
+AUP decision: the maintainer authorized landing these headless-CLI PAT providers in this
+session (2026-09-08); recorded here as the maintainer decision the prior reviews asked for.
diff --git a/devlog/_fin/260908_provider_runtime_stack/050_delivery_record.md b/devlog/_fin/260908_provider_runtime_stack/050_delivery_record.md
new file mode 100644
index 00000000000..8ada5b0bc6e
--- /dev/null
+++ b/devlog/_fin/260908_provider_runtime_stack/050_delivery_record.md
@@ -0,0 +1,58 @@
+# 050 — Delivery record
+
+Snapshot: 2026-09-08T14:10Z. `origin/dev` = `e2bf1672c` (was `29bb221c3` at unit start).
+
+## What landed
+
+| Layer | PR | Merge SHA | Head SHA | Source | Author credit |
+|-------|----|-----------|----------|--------|---------------|
+| L1 CodeBuddy Global/CN | #4026 | `b77b05aa5` | `769e4208f` | #3340 (4 commits, cherry-pick -x) + layout move | Flowershangfromthebranches (author field + trailer) |
+| L2 Qoder Global | #4027 | `753ecb813` | `5adf130da` | #3349 (cherry-pick -x) + layout move + audit fix | Flowershangfromthebranches |
+| L3 Qoder CN | #4028 | `07ac34b2d` | `615c5c62c` | #3350 (cherry-pick -x) | Flowershangfromthebranches; Liang-Psych trailer for #3010 direction |
+| L4 marks/docs | #4029 | `9f0721299` | `6ba1e6750` | maintainer | — |
+| L5 Hermes YAML | #4030 | `5bb8faf7b` | `295bcf82b` | #3990 (cherry-pick -x) + fr/zh-TW sync | rrmlima |
+| L6 Gemini tail | #4031 | `e2bf1672c` | `16d49ceab` | #3988 (cherry-pick -x) + single-owner fix | rrmlima (trailer; carried commit author is `root`) |
+
+## Proof
+
+- CI: `ci.yml` `lane=all` run **34231255231** on `16d49ceab`: 26/26 jobs success. `windows 4/6`
+ failed once on `tests/codex-integration/token-guardian.test.ts` afterEach `EPERM rm` of its
+ temp dir (a file the stack does not touch); same-SHA rerun of that job passed. Earlier run
+ 34228268757 on `ba3912ce8` was cancelled when the head moved and is diagnostic only.
+- Ancestry: all six merge SHAs and all six head SHAs are ancestors of fetched `origin/dev`.
+- Tree: `origin/dev^{tree}` = `2201b9e54…` = `16d49ceab^{tree}`. Landed tree equals certified head.
+- Hygiene/enforce-target: green on every PR before merge after two repairs (trailers moved to
+ the body end where `pr-carry-attribution.cjs` reads them; L4 got pinned icon tests for
+ `missing_regression_test` and a before/after screenshot for the GUI gate).
+
+## NOT RUN (by maintainer instruction)
+
+`bun install`, `bun run typecheck`, `bun run test`, `bun run test:changed`, `bun run build:gui`,
+`bun run privacy:scan`, `bun run lint:gui` — none executed locally. Every Git mutation ran with
+`-c core.hooksPath=/dev/null`; pushes used `--no-verify`. Hosted CI is the only execution proof.
+
+## Audit dispositions
+
+Round 1 (L1–L3): blocker 1 `captured.effectiveAlias` folded (`5adf130da`); blocker 3 auth regex
+folded (same commit); blocker 2 tool-less conformance exemption → residual, follow-up; blocker 4
+`noVisionModels` → rebutted (repository convention). Round 2 (L4–L6): double `(continue)` nudge
+folded (`16d49ceab`); fr/zh-TW Hermes contradiction folded (`295bcf82b`); seven locale copies of
+the adapter list still stop at `azure-openai` (predates this unit; residual).
+
+## Closeouts
+
+#3340 (auto-closed by merge; credit comment added), #3349, #3350, #3990, #3988 closed as
+superseded with credit; #3010 closed as superseded by the PAT design with credit to Liang-Psych.
+
+## Secondary PR dispositions (not closed)
+
+DEFER #3833 (layout seed trap, design call), #3952 (split required). REJECT for this stack
+#3639, #3283, #3282, #2230 (C4 surfaces, conflicts, or maintainer-required security review).
+See 013.
+
+## Residuals for a follow-up
+
+1. Guard test proving `codebuddy`/`qoder` still expose no tool catalog (audit round 1, blocker 2).
+2. Locale adapter tables (ko/ja/zh-cn/zh-tw/fr/ru/tr reference/configuration/providers.md).
+3. `docs/qoder-cli-provider.md` lives outside docs-site; consider folding into the guide.
+4. Windows shard flake: `token-guardian.test.ts` temp-dir `EPERM` on cleanup.
diff --git a/devlog/_fin/260908_provider_runtime_stack/060_ledger.md b/devlog/_fin/260908_provider_runtime_stack/060_ledger.md
new file mode 100644
index 00000000000..cc5dbd49778
--- /dev/null
+++ b/devlog/_fin/260908_provider_runtime_stack/060_ledger.md
@@ -0,0 +1,17 @@
+# 060 — Ledger
+
+| When (UTC) | Event | Evidence |
+|-----------|-------|----------|
+| 2026-09-08T12:04 | Goal created; goalplan wp1–wp4 registered | `.codexclaw/goalplans/land-the-open-opencodex-provider-runtime-contrib` |
+| 2026-09-08T12:06 | Worktree `/private/tmp/ocx-prs-stack-01a080e2` on `origin/dev` `29bb221c3`; L1–L3 carried by `cherry-pick -x` | heads L1 `769e4208f`, L2 `094cb93d0`, L3 `85ad0a29a` |
+| 2026-09-08T12:30 | wp1 roadmap docs 000–040 written; audit NEAR-PASS (020) | this unit |
+| 2026-09-08T12:35 | wp2 audit fixes on L2 (`5adf130da`): effectiveAlias ×4, auth regex anchor; L3 cascaded | 020 |
+| 2026-09-08T12:40 | wp3: L4 `76c8a0b0b` (qoder.svg, aliases, names, README, docs-site), L5 `a49d1ad92`+`48666541b` (#3990 + fr/zh-TW sync), L6 `7bd84795b`+`ba3912ce8` (#3988 + single-owner nudge) | 030, audit round 2 |
+| 2026-09-08T12:48 | Pushed six branches `--no-verify`; PRs #4026 (L1→dev), #4027, #4028, #4029, #4030, #4031 (L6) with explicit dependent bases | GitHub |
+| 2026-09-08T12:49 | `ci.yml` `lane=all` dispatched on `ba3912ce8`: run 34228268757 (+ PR run 34228261835) | Actions |
+| 2026-09-08T13:02 | Hygiene gate: `missing_coauthor_credit` on every PR (trailers were inside the Summary, gate reads end of body) → trailers appended at body end; `missing_regression_test` on L4 → pinned Qoder/CodeBuddy icon tests added, L4 amended `6ba1e6750`, L5/L6 cascaded, force-with-lease pushed | GitHub |
+| 2026-09-08T13:24 | New top head `16d49ceab`; `lane=all` dispatched: run 34231255231 (first run 34228268757 on `ba3912ce8` kept only as diagnostic) | Actions |
+| 2026-09-08T13:55 | Run 34231255231 (`16d49ceab`, lane=all): 25/26 jobs success; `windows 4/6` failed on `tests/codex-integration/token-guardian.test.ts` afterEach `EPERM rm` of its temp dir (remove-tree retry exhausted). The stack touches no oauth/guardian/remove-tree file. Rerunning that job at the same SHA. | Actions |
+| 2026-09-08T14:00 | Run 34231255231 green 26/26 after same-SHA rerun of windows 4/6 | Actions |
+| 2026-09-08T14:07 | Bottom-up admin merges: #4026 `b77b05aa5`, #4027 `753ecb813`, #4028 `07ac34b2d`, #4029 `9f0721299`, #4030 `5bb8faf7b`, #4031 `e2bf1672c`; `origin/dev`=`e2bf1672c`; tree == `16d49ceab^{tree}` | 050 |
+| 2026-09-08T14:09 | Originals closed with credit: #3349 #3350 #3010 #3990 #3988 (#3340 auto-closed, credit comment) | GitHub |
diff --git a/devlog/_fin/260908_provider_runtime_stack/assets/031_l4_provider_marks.png b/devlog/_fin/260908_provider_runtime_stack/assets/031_l4_provider_marks.png
new file mode 100644
index 00000000000..18f4797503f
Binary files /dev/null and b/devlog/_fin/260908_provider_runtime_stack/assets/031_l4_provider_marks.png differ
diff --git a/devlog/_fin/260908_release_248/000_plan.md b/devlog/_fin/260908_release_248/000_plan.md
new file mode 100644
index 00000000000..221302a72db
--- /dev/null
+++ b/devlog/_fin/260908_release_248/000_plan.md
@@ -0,0 +1,31 @@
+# Release 2.48.0 plan
+
+Owner-authorized HOTL release train for OpenCodex 2.48.0. The owner asked for a regression check of `dev` against `main`, two promotion pull requests, merges into `main` and `preview`, and npm publication. The owner also forbade running the local test suite and required `--no-verify` for any push, so every verification claim in this unit rests on hosted CI at an exact SHA. Local typecheck, local `bun run test`, and local privacy scan are NOT RUN by instruction and are labeled that way wherever they would otherwise appear as evidence.
+
+## Candidate
+
+Release candidate: `7797586a8899c673eab48886a490e85b480c6d72` (`origin/dev` tip, 2.48.0 in package.json).
+
+Published baseline: `@bitkyc08/opencodex` `latest=2.47.0`, `preview=2.47.0-preview.20260908`. `origin/main` is `f7f890ff7` at 2.47.0; `origin/preview` is `3bef20677` at 2.47.0-preview.20260908. `dev` is 70 commits ahead of each.
+
+The candidate tip itself has no Cross-platform CI run because its only delta against `9ad218a9bdd34ee33004c35706d78396bf02eef2` is under `devlog/`, which the workflow's push path filter excludes. `git diff --name-only 9ad218a9b 7797586a8 -- . ':(exclude)devlog'` returns zero files, so `9ad218a9b` is the runtime-identical CI witness for the candidate: 19 successful check-runs, two deliberately skipped (`macos control`, the Windows shard placeholder). That equivalence is stated explicitly rather than assumed, because the promotion merge SHAs will carry their own push-event CI regardless.
+
+## Scope
+
+In scope: version metadata on the two promotion branches, promotion PRs into `preview` and `main`, merges, `release.yml` dispatch for preview and stable, and registry/tag verification. Also in scope: a PABCD repair cycle merged into `dev` if regression evidence shows a defect, followed by a repeat of the release verification.
+
+Out of scope: unrelated open PRs and issues, dev-version bumping beyond what the release requires, installed-service upgrades, account settings, and any change to branch protection or CI gates.
+
+## Work phases
+
+- wp1 — this roadmap. Pin the candidate, record the CI-equivalence argument and the promotion procedure. No product change.
+- wp2 — regression verification of the candidate against `main` using hosted evidence only.
+- wp3 — promotion branches and PRs, merged with exact-head CI.
+- wp4 — npm preview and stable publication with registry verification.
+
+## Verification and outcomes
+
+Each promotion SHA needs its own successful push-event Cross-platform CI and Service lifecycle before any publish dispatch. Publication proof is npm dist-tags, the published `gitHead`, tarball integrity, provenance, and the GitHub tag and release. `enforce-target` is expected to reject both promotion PRs because its allowed bases contain only `dev`; that is the established authorized promotion exception and is reported as failing, never as passing.
+
+DONE requires both channels published and verified with `dev` still ahead. BLOCKED is a concrete external prerequisite or a failed gate with no safe remedy. A failing gate is repaired or remains a blocker; it is never weakened, and no check is disabled to hide it.
+
diff --git a/devlog/_fin/260908_release_248/010_release.md b/devlog/_fin/260908_release_248/010_release.md
new file mode 100644
index 00000000000..9e1875d65f5
--- /dev/null
+++ b/devlog/_fin/260908_release_248/010_release.md
@@ -0,0 +1,16 @@
+# Release operation
+
+1. Pin candidate `7797586a8899c673eab48886a490e85b480c6d72` and record its CI witness `9ad218a9bdd34ee33004c35706d78396bf02eef2` (runtime-identical; devlog-only delta). Confirm published baseline tags and that `v2.48.0` and `v2.48.0-preview.*` are unused.
+
+2. Regression review of `origin/main..origin/dev`: 70 commits, 162 changed files, 25 under `src/`. Read the delta for release-blocking risk in routing, auth, credentials, release automation, and workflows. Hosted CI on the witness SHA is the mechanical evidence; the local suite is NOT RUN by owner instruction.
+
+3. Create two independent promotion branches from `origin/preview` and `origin/main`, merge the frozen candidate into each, resolve only the channel version conflict, and set `package.json` to `2.48.0-preview.20260908` on the preview branch and `2.48.0` on the main branch. The runtime tree on each branch must equal the candidate exactly apart from that one version line; prove it with `git diff` restricted to non-version paths.
+
+4. Push both branches with `--no-verify` (owner instruction), open template-complete PRs, and wait for each merge SHA's own push-event Cross-platform CI and Service lifecycle. `enforce-target` will fail on both by design; record it as the authorized promotion exception.
+
+5. Dispatch `release.yml` with `expected-sha` equal to the branch tip: dry-run first, then preview, then stable, serialized. Verify `npm view @bitkyc08/opencodex dist-tags`, published `gitHead`, tarball SHA-512, provenance, and the GitHub tag and release. Run a published-package smoke in an isolated home.
+
+6. Record the outcome in `090_delivery.md`, confirm `dev` remains ahead of both channels, and leave unrelated dirty files in the primary checkout untouched.
+
+Activation scenarios: a moved branch means refuse the dispatch and repin; a failed CI job means inspect and repair rather than rerun blindly; a post-publish smoke failure means inspect registry metadata before any retry, and never republish blindly. Rollback artifact `v2.47.0` stays published; no destructive rollback is planned.
+
diff --git a/devlog/_fin/260908_release_248/020_progress.md b/devlog/_fin/260908_release_248/020_progress.md
new file mode 100644
index 00000000000..64ac908fa9d
--- /dev/null
+++ b/devlog/_fin/260908_release_248/020_progress.md
@@ -0,0 +1,14 @@
+# Progress
+
+## wp1 — roadmap (this cycle)
+
+Candidate pinned at `7797586a8899c673eab48886a490e85b480c6d72`. CI witness `9ad218a9bdd34ee33004c35706d78396bf02eef2`: 19 successful check-runs, 2 deliberate skips. The delta between them is devlog-only, so the witness covers the candidate's runtime tree exactly.
+
+Roadmap committed as `38edcbf7c` on `codex/release-248-plan` in the dedicated worktree `/private/tmp/ocx-release-248`. The primary checkout keeps its pre-existing unrelated dirty files untouched.
+
+Local suite, typecheck, and privacy scan: NOT RUN by owner instruction.
+
+## wp2 — regression verification (next)
+
+Delta to review: 70 commits, 162 files, 25 under `src/`, ~2096 changed source lines against `origin/main`.
+
diff --git a/devlog/_fin/260908_release_248/030_wp2_regression_review.md b/devlog/_fin/260908_release_248/030_wp2_regression_review.md
new file mode 100644
index 00000000000..3e2e50712f4
--- /dev/null
+++ b/devlog/_fin/260908_release_248/030_wp2_regression_review.md
@@ -0,0 +1,39 @@
+# wp2 — regression review of the candidate against main
+
+Reviewed `origin/main..origin/dev`: 70 commits, 162 files, 25 under `src/`, about 2096 changed source lines. The question this phase answers is narrow — does anything in that delta regress behavior that `main` currently ships? The local suite is NOT RUN by owner instruction, so the mechanical evidence is hosted CI and the argument below is a source read.
+
+## What changed, by risk
+
+Credential and quota handling carries the most weight. `src/codex/routing.ts` factors the background recovery settle path into `settleCooldownRecoveryLease` and adds a manual-reset claim/settle pair. The refactor moves `cooldownSource === "reset-derived"` and the scope restriction into the shared settle helper, which reads at first glance like a new restriction on the pre-existing background path. It is not: `claimDueCodexQuotaRecoveryProbes` already filters candidates to `(scope === undefined || scope === "shared")` with `cooldownSource === "reset-derived"`, so no claim that could previously settle successfully can reach the helper and fail those conditions. `tests/codex-integration/codex-cooldown-recovery.test.ts` and `codex-reset-credit-auto-redeem.test.ts` cover both paths.
+
+`src/codex/auth-api.ts` adds a `dispatchSequence` fence around WHAM usage publication so a slow in-flight response cannot overwrite a newer published quota. The added early returns hand back the cached account info rather than publishing, which is a strict narrowing of when stale data wins.
+
+`src/server/responses/core.ts` adds combo session recall for compaction triggers and a completion callback gate. The recall path is guarded on a bare model name, an actual `compaction_trigger` input item, no configured selector, and no resolvable combo id, so a request that previously routed by explicit selector still does. The previous-response error code changed from `invalid_request_error` to `previous_response_not_found`; that is a deliberate behavior change so Codex reconnects with full input instead of terminating the task, and it is the fix's whole point.
+
+`src/router.ts` and `src/providers/default-aliases.ts` extend alias-ownership so a provider's own configured name also claims an alias, not just an explicit `alias` field. This makes an ambiguous alias resolve to nothing rather than to the wrong provider — a correctness fix with a narrow blast radius.
+
+`src/config/atomic-write.ts` replaces `constants.O_WRONLY | O_CREAT | O_EXCL` with the `"wx"` flag string, which is the same semantics expressed portably; that was the point of the change on Windows.
+
+## Coverage
+
+Forty test files changed alongside the 25 source files, and every source area above has a focused test in the same domain directory. No source change in the delta arrived without paired coverage.
+
+## Hosted evidence
+
+Push-event run on `9ad218a9bdd34ee33004c35706d78396bf02eef2` (runtime-identical to the candidate): 19 successful check-runs, 2 skipped by design.
+
+Dispatched full-lane run [34206043085](https://github.com/lidge-jun/opencodex/actions/runs/34206043085) on the exact candidate `7797586a8899c673eab48886a490e85b480c6d72` with `lane=all`, which adds the six Windows shards and the unsharded macOS control that the push event does not run.
+
+## Verdict
+
+No regression identified against `main`. The delta is corrective, each risky path narrows rather than widens behavior, and the one intentional behavior change (the previous-response error code) is the documented fix.
+
+
+## Full-lane CI outcome
+
+Run [34206043085](https://github.com/lidge-jun/opencodex/actions/runs/34206043085) on the exact candidate `7797586a8899c673eab48886a490e85b480c6d72` completed **success** after one rerun of a single job.
+
+The first attempt failed on `windows 3/6`: `provider outbound GET transport > proxy mode reaches one real proxy across outbound, connection-test, and model-discovery paths` timed out at its own 15s bound, and the spawned fixture child was killed (exit 143). That test file is not in the release delta — `git log origin/main..origin/dev -- tests/providers/provider-outbound.test.ts` is empty — and the same content passed `windows 3/6` in dispatch run 34198186409 ninety minutes earlier. Rerunning the failed job passed. The evidence points at cold-runner timing on a 15s child-spawn budget, not at anything the candidate changed.
+
+That timeout is a real fragility worth tightening later, but it is not a 2.48.0 regression and does not block this promotion.
+
diff --git a/devlog/_fin/260908_release_248/040_wp3_promotion.md b/devlog/_fin/260908_release_248/040_wp3_promotion.md
new file mode 100644
index 00000000000..5ba9997eaf5
--- /dev/null
+++ b/devlog/_fin/260908_release_248/040_wp3_promotion.md
@@ -0,0 +1,35 @@
+# wp3 — promotion pull requests and merges
+
+Both promotions are on their channels.
+
+| Channel | PR | Merge SHA | Version |
+|---|---|---|---|
+| `preview` | [#4010](https://github.com/lidge-jun/opencodex/pull/4010) | `c71474e83c92be1f39e9d8c1fe0743307ce93387` | 2.48.0-preview.20260908 |
+| `main` | [#4011](https://github.com/lidge-jun/opencodex/pull/4011) | `9a27e86992d7a014e0aa92c046199b9fac148201` | 2.48.0 |
+
+Candidate `7797586a8899c673eab48886a490e85b480c6d72` is an ancestor of both branches, verified with `git merge-base --is-ancestor` against freshly fetched refs. `git diff 7797586a8 origin/main` is empty: the main tree is byte-identical to the candidate, since the candidate already carried 2.48.0. The preview tree differs only in the channel version line.
+
+## Gate outcomes
+
+`enforce-target` failed on both, as expected and as documented in the PR bodies. Its allowed bases contain only `dev` and its one coded exception is a stacked child, so a release promotion cannot pass it. Both PRs were opened as drafts by that gate and were marked ready before the authorized admin merge. The failure is recorded as a failure; no check, protection, or target was altered.
+
+Every other check passed on both heads.
+
+## Flakes encountered, and why they are flakes
+
+`macos 1/2` on #4011 hung twice inside `tests/clients/client-connect.test.ts` after "connect transaction and offline disconnect > an unavailable config coordinator refuses before issuing any hub key", producing "killed 1 dangling process" and then a 20-minute job timeout. The same tree passed `macos 1/2` in candidate run 34206043085 and on #4010, and passed on the third attempt. Combined with the earlier `windows 3/6` timeout, both failures were child-process lifecycle timing on cold runners, in files outside the release delta.
+
+## Post-merge gates
+
+Preview `c71474e83`: Cross-platform CI success, Service lifecycle success.
+
+Main `9a27e8699`: Service lifecycle success, React Doctor success, Docs deploy success; Cross-platform CI observed in progress at the time of writing and must be green before the stable publish.
+
+
+## Merge verification commands
+
+```
+git merge-base --is-ancestor 7797586a8 origin/preview # YES
+git merge-base --is-ancestor 7797586a8 origin/main # YES
+git diff --stat 7797586a8 origin/main # empty
+```
diff --git a/devlog/_fin/260908_release_248/050_wp4_publication.md b/devlog/_fin/260908_release_248/050_wp4_publication.md
new file mode 100644
index 00000000000..672c8e0b451
--- /dev/null
+++ b/devlog/_fin/260908_release_248/050_wp4_publication.md
@@ -0,0 +1,35 @@
+# wp4 — publication
+
+Both channels are published and verified.
+
+| Channel | Version | Source SHA | npm gitHead |
+|---|---|---|---|
+| `preview` | 2.48.0-preview.20260908 | `c71474e83c92be1f39e9d8c1fe0743307ce93387` | matches |
+| `latest` | 2.48.0 | `9a27e86992d7a014e0aa92c046199b9fac148201` | matches |
+
+`npm view @bitkyc08/opencodex dist-tags` reports `{"latest":"2.48.0","preview":"2.48.0-preview.20260908"}`. GitHub releases `v2.48.0` and `v2.48.0-preview.20260908` exist at exactly those commits. Both publishes carry a signed provenance statement from GitHub Actions.
+
+Tarball integrity was checked independently: downloading `bitkyc08-opencodex-2.48.0.tgz` from the registry and hashing it locally yields `sha512-f2GmrBpUJYZ+bOT62VL1MWhNwIBkFz5JUVGrNPG+SAaWJheshmMsnHDoMyRIpyd5v50sK9uI0Ll4XZwI4PVjhA==`, identical to the `dist.integrity` npm reports and to the `integrity:` line in the publish log. The unpacked package declares version 2.48.0 and its `bin/ocx.mjs` launcher runs and correctly reports the Bun runtime requirement in an isolated `OPENCODEX_HOME`.
+
+## The dev-version gate
+
+The first stable dispatch failed at "Require dev to be ready for this release": `origin/dev` still carried 2.48.0, which does not outrank the version being released. That gate exists so `tests/ci-workflows/release-version-line.test.ts` does not go red on `dev` and on every pull request against it the moment a release ships.
+
+The repair was [#4019](https://github.com/lidge-jun/opencodex/pull/4019), a one-line `package.json` change moving `dev` to 2.49.0, with the version decided by `scripts/bump-dev-version.ts` rather than chosen by hand. It merged as `0372c43e663b25387a0a00b03b6a9ca9d4bf9048` with full CI green, after which the stable dispatch succeeded on the unchanged `main` SHA.
+
+## Registry propagation
+
+Both publishes reported "Your package is being processed" and the workflow's bounded six-attempt registry smoke ended `verification=pending` in each case. Neither was republished. Preview appeared in the registry roughly twelve minutes after acceptance, stable roughly seven; both were then verified by direct registry reads and by the independent tarball hash above.
+
+## Final branch state
+
+`dev` 2.49.0, `main` 2.48.0, `preview` 2.48.0-preview.20260908. `dev` remains ahead of both release channels.
+
+
+## Verification commands
+
+```
+npm view @bitkyc08/opencodex dist-tags --json
+npm view @bitkyc08/opencodex@2.48.0 dist.integrity dist.tarball --json
+gh release view v2.48.0 --json tagName,targetCommitish
+```
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 00000000000..7af10b83cee
--- /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 00000000000..061ec107e67
--- /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/_fin/260912_devin_acp_removal/000_plan.md b/devlog/_fin/260912_devin_acp_removal/000_plan.md
new file mode 100644
index 00000000000..5733dba051f
--- /dev/null
+++ b/devlog/_fin/260912_devin_acp_removal/000_plan.md
@@ -0,0 +1,76 @@
+# 260912 — Retire the Devin ACP adapter and give Devin the tool-catalog nudge
+
+## Why this unit exists
+
+Two Devin provider rows exist, `devin` and `devin-cli`, and both stream Cognition's
+`ApiServerService/GetChatMessage` over Connect-RPC on the `devin` adapter. They differ only in
+where the credential came from: a browser sign-in through `RegisterUser`, or the
+`devin-session-token` the installed CLI already wrote to its own `credentials.toml`.
+
+A second adapter registered under the id `devin-cli` still existed. It spawned `devin acp` and
+drove the child over Agent Client Protocol on stdio. It was unreachable under the `devin-cli`
+provider id — `routedProviderConfig` pins the adapter from the registry for any registry id — and
+reachable only through a custom-named row such as `"devin-acp"`. Nobody was routed to it.
+
+It is being removed rather than kept, because the premise that justified it turned out to be
+false. The design assumed OpenCodex could not hold a credential for the installed CLI, so a child
+process was the only way to use it. The CLI's `windsurf_api_key` is an ordinary
+`devin-session-token$`, the same credential the cloud client already speaks. Importing the
+token does everything the child did, without a placeholder `buildRequest`, a disabled
+`parseStream`, an identity-only `baseUrl` that no request may connect to, and a subprocess
+running in the operator's own tree.
+
+The nudge is the second half. Every non-OpenAI adapter that advertises a client tool catalog
+injects `buildNonOpenAIToolCatalogNudgeForTools` into its system prompt — Anthropic, Google,
+non-OpenAI `openai-chat` hosts, Kiro, Command Code. The Devin adapter does advertise a real
+catalog (proto field #10 via `mapOcxToolsToDevin`) and was the only one left without the
+paragraph. Adding it in `mapOcxMessagesToDevin` covers both provider rows at once, because they
+share the adapter. The retired ACP wire could never have used it: `session/prompt` carries prompt
+text only, with `capabilities: {}` and `mcpServers: []`, so a catalog nudge there would have
+described a contract that does not exist on that wire.
+
+## Work phases
+
+### wp1 — land PR #4411
+
+Unrelated in subject, but it is the open PR blocking this branch's base from being clean. Its
+`test 3/4`, `gates` and `macos 2/2` failures were one cause: `privacy:scan` flagged a maintainer
+email address quoted inside a carried devlog record. The address was incidental to the note.
+
+Done when: exact-head CI is green and the PR is merged into `dev`.
+
+### wp2 — retire ACP, migrate, nudge
+
+Removals:
+
+- `src/adapters/devin-cli/{acp,adapter,binary,models}.ts`
+- `tests/providers/devin-cli-adapter.test.ts`, and its rows in `scripts/test-layout/layout.json`
+ and `tests/fixtures/test-layout-expected.json`
+- the `devin-cli` import, `AdapterWire` member and registry entry in `src/adapters/registry.ts`
+- the `devin-cli` case in `upstreamProtocolForAdapter`
+- the `devin-cli` row in the adapter-registry authority map, and the wire from
+ `RUN_TURN_ONLY_WIRES`
+
+Migration. `projectDevinCliAuthMode` previously warned and changed nothing when a saved row still
+named the ACP adapter, on the reasoning that routing already pinned the transport. That reasoning
+held only for the registry id. With the adapter gone, a custom-named row has nothing pinning it
+and would throw `Unknown adapter: devin-cli` on every request, so the migration now rewrites
+**every** row naming the retired id, whatever the row is called. A row still carrying the
+identity-only `cli.devin.ai` host is repointed at the api-server in the same pass, because that
+URL was never a destination and leaving it would trade an unconstructible adapter for an
+unresolvable host.
+
+Nudge. `mapOcxMessagesToDevin` appends the shared paragraph to the system content. The wire name
+callback is `tool => tool.name`, not the default namespaced form, because `mapOcxToolsToDevin`
+writes the bare name; a nudge listing names the model is never offered is worse than none.
+
+Done when: no adapter id `devin-cli` remains anywhere, saved rows migrate with regression
+coverage, the nudge is covered by a regression test, structure/ and docs-site agree, exact-head CI
+is green and the PR is merged.
+
+## Verification policy for this unit
+
+Local product suite runs are prohibited by the maintainer. Local checks are limited to
+`bun run structure:check`, `bun run privacy:scan`, and explicitly named focused test files.
+Everything else is hosted exact-head CI. Skipped local checks are labelled NOT RUN in the PR.
+
diff --git a/devlog/_fin/260912_devin_acp_removal/090_outcome.md b/devlog/_fin/260912_devin_acp_removal/090_outcome.md
new file mode 100644
index 00000000000..890d1470ced
--- /dev/null
+++ b/devlog/_fin/260912_devin_acp_removal/090_outcome.md
@@ -0,0 +1,66 @@
+# Outcome — Devin ACP adapter retired, Devin adapter given the tool-catalog nudge
+
+Both work phases landed. This unit moves to `_fin` because every change it describes is now
+visible in public git history on `dev`.
+
+## What landed
+
+| Work phase | PR | Squash on `dev` | Exact head CI |
+|---|---|---|---|
+| wp1 — unblock and land the open sync PR | [#4411](https://github.com/lidge-jun/opencodex/pull/4411) | `2d3c05fa9e` | green on `3049b6712e` |
+| wp2 — retire ACP, migrate saved rows, add the nudge | [#4415](https://github.com/lidge-jun/opencodex/pull/4415) | `213065e30b` | green on `67fe08b5b2` |
+
+## What the premise turned out to be
+
+The ACP adapter existed because the design assumed OpenCodex could not hold a credential for the
+installed Devin CLI, so driving a `devin acp` child was the only way to use it. The CLI's
+`windsurf_api_key` is an ordinary `devin-session-token$` — the same credential
+`RegisterUser` mints for a browser sign-in, and one the cloud-direct client already speaks. Once
+the token could simply be imported, the child process bought nothing and cost a placeholder
+`buildRequest`, a disabled `parseStream`, an identity-only `baseUrl`, and a subprocess in the
+operator's own tree.
+
+Worth recording because the reasoning was inverted twice. The adapter was written on an untested
+assumption, and then kept on a second one: that leaving it registered was harmless because
+`routedProviderConfig` pinned the registry id away from it. That was true for the registry id and
+false for the custom-named row the documentation itself recommended, which is exactly the row
+that would have broken on removal.
+
+## Corrections this unit made to its own starting assumptions
+
+- The Devin CLI provider was described in-session as running over ACP. It does not, and has not
+ since the account-login unit. The live evidence was already in the request log: a
+ `"provider":"devin-cli"` row carries `"adapter":"devin"`.
+- Token usage was believed missing for `devin-cli`. It is reported. The cloud-direct client
+ decodes Cognition's `UsageStats` at proto field #28, so both provider rows record
+ `usageStatus: "reported"` with real input/output/cached counts. What is absent is account
+ quota: neither row appears in `supportsPerAccountQuota`, so the dashboard has no balance to
+ show, and Cognition exposes ACU consumption only through Enterprise-scoped endpoints.
+
+## Reviewer findings, all folded in
+
+Four independent read-only reviews ran against this work. Each finding was fixed rather than
+rebutted:
+
+- the exact-system-content assertion in `tests/providers/devin-adapter.test.ts` that the nudge
+ breaks, rewritten to a prefix plus a catalog substring;
+- an empty `src/adapters/devin-cli/` directory left behind by the deletion;
+- a second hardcoded copy of the Cognition host in the migration, now
+ `DEVIN_DEFAULT_API_SERVER`;
+- a GUI comment rewrite that was unrelated cleanup and, because the screenshot gate is
+ path-based, would have asked a comment-only diff for a screenshot of nothing;
+- on wp1, `structure/config.md` still claiming every deterministic preflight refusal leaves the
+ catalog untouched, and `refreshOutcome` reaching callers undeclared.
+
+## Known residual
+
+`projectDevinCliAuthMode` matches the retired adapter id by exact string. A hand-edited
+`"devin-cli "` or `"Devin-CLI"` would not be rewritten and would fail with
+`Unknown adapter`. Left as-is deliberately: config validation already rejects an unknown adapter
+id at load, and folding case or whitespace here would hide a typo rather than repair a known
+historical value.
+
+Locale `reference/adapters.md` pages still have no `devin-cli` section. That is pre-existing
+translation lag, not ACP residue — they never documented the retired adapter, so nothing in them
+contradicts the English source.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/000_plan.md b/devlog/_fin/260912_devin_cli_account_login/000_plan.md
new file mode 100644
index 00000000000..ea736f49429
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/000_plan.md
@@ -0,0 +1,88 @@
+# Devin CLI as an account provider
+
+**Unit:** 260912_devin_cli_account_login
+**Class:** C3 (public provider contract + a documented invariant + GUI surface)
+**Goal (host):** register devin-cli as an account provider so it appears in the
+dashboard accounts tab beside devin, by giving it a login entry that drives the
+installed Devin CLI's own auth flow, without opencodex holding a usable Devin
+bearer token.
+
+## Why this unit exists
+
+The dashboard's add-provider dialog has three tabs. Two of them (Free, Paid) are
+rendered from the preset catalog; the Accounts tab is not. In
+`gui/src/components/provider-catalog/ProviderCatalog.tsx` the preset rows are
+drawn only when `tier !== "accounts"`, and the Accounts tab instead renders
+`accountRows`, which is built from providers that have a login flow. The
+`buckets.accounts` bucket that `bucketPresets` computes is never rendered at
+all.
+
+That is why `devin-cli` is reachable only under Free today: `authKind: "local"`
+makes `isFreeProvider` true (`gui/src/provider-workspace/catalog.ts`), the same
+branch that holds Ollama, vLLM and LM Studio. Reclassifying the tier alone would
+remove it from Free and put it in a bucket nothing draws, so it would vanish
+from the dialog entirely. The only way into the Accounts tab is to become a
+provider with a login.
+
+## The constraint this unit has to move
+
+`src/providers/registry.ts` and `tests/providers/devin-cli-adapter.test.ts`
+currently pin the opposite posture:
+
+> The installed CLI carries its own credentials from `devin auth login`, so this
+> provider takes no key and the proxy never sees a token for this provider.
+
+That statement is about the **request path**, and it stays true: the adapter
+spawns `devin acp` and the child authenticates itself. What changes is the
+**dashboard path**, which gains a login entry whose job is to run the CLI's own
+auth flow and read back who is signed in. The distinction the unit must keep
+explicit, in code comments and in the tests, is:
+
+- the adapter still never reads, requests, or forwards a credential at request time;
+- the OAuth entry stores an identity marker, never a usable Devin bearer token.
+
+If those two cannot both hold, the unit stops and reports rather than inventing a
+token to satisfy the framework.
+
+## Constraints
+
+- No repository-wide local suite, typecheck, or build. Focused tests only; hosted
+ CI on the exact PR head is the gate. Push with `--no-verify`.
+- The Devin CLI is **not installed** on the development machine and must not be
+ installed as part of this unit without a separate instruction. Every code path
+ that depends on the binary needs a documented degraded behaviour and a test
+ that exercises it through an injected spawn, the way
+ `tests/providers/devin-cli-adapter.test.ts` already drives the adapter.
+- `src/lab/` must stay off the core path; nothing here touches `src/router.ts`,
+ `src/server/lifecycle.ts`, or `src/server/responses/core.ts`.
+- Target branch is `dev`.
+
+## Work-phase map
+
+Dependency-ordered; each is one full PABCD cycle.
+
+| Phase | Doc | Outcome |
+|---|---|---|
+| wp1 | this unit | Roadmap locked, every later phase written to diff level |
+| wp2 | `010_phase1_cli_login.md` | `src/oauth/devin-cli.ts`: signed-in detection, login that drives the CLI, identity-only credential |
+| wp3 | `020_phase2_reclassify.md` | Registry + OAUTH_PROVIDERS registration, invariant text, tests that pinned `local` |
+| wp4 | `030_phase3_surface_and_land.md` | Accounts-tab proof against the running service, docs/locale, PR, merge |
+
+## Open risks carried into wp2
+
+1. **CLI absent.** Signed-in detection cannot be proven end to end on this
+ machine. wp2 must therefore make the binary lookup injectable and prove both
+ branches (found / not found) with the existing `resolveDevinCliBinary`
+ override seam, and wp4 must state plainly that the live signed-in path is
+ unproven here.
+2. **No documented status subcommand.** If the CLI exposes no non-interactive way
+ to report the signed-in account, the login entry can only report "the CLI
+ reports it is signed in" without an identity. That is still enough for an
+ accounts row, but it changes the credential shape, so wp2 decides this against
+ the subagent finding recorded in `001_cli_auth_survey.md` and amends
+ `010_` before building.
+3. **Refresh.** The OAuth framework expects a refresh path. `src/oauth/devin.ts`
+ throws `invalid_grant` because Cognition mints no refresh token; the CLI entry
+ has the same shape and should reuse that posture rather than extending an
+ expiry it cannot honour.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/001_cli_auth_survey.md b/devlog/_fin/260912_devin_cli_account_login/001_cli_auth_survey.md
new file mode 100644
index 00000000000..b513a2aeb53
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/001_cli_auth_survey.md
@@ -0,0 +1,82 @@
+# 001 — What the surfaces actually require
+
+Findings from three parallel read-only investigations (subagents Carson, Gibbs,
+Rawls), recorded here so each later phase starts from evidence rather than from
+the transcript.
+
+## The Accounts tab is fed by OAUTH_PROVIDERS, not by the preset catalog
+
+`GET /api/oauth/providers` returns `listOAuthProviders()`, which is
+`Object.keys(OAUTH_PROVIDERS)` minus `chatgpt`
+(`src/server/management/oauth-account-routes.ts:137-140`,
+`src/oauth/index.ts:335-371`). The GUI turns that list into the Accounts rows in
+`gui/src/pages/providers-page-utils.ts:8-25`. A provider does **not** need to be
+in `config.json` to appear. So membership in `OAUTH_PROVIDERS` is the whole
+admission rule.
+
+## What an OAuth entry must provide
+
+`OAuthProviderDef` (`src/oauth/index.ts:184-196`) requires `login`, `refresh`,
+`providerConfig`, `defaultModel`. `providerConfig` is not hand-written: `oauthConfig(id)`
+calls `deriveOAuthProviderConfig`, which finds the registry row **only when
+`authKind === "oauth"`** and throws otherwise (`src/providers/derive.ts:350-353`).
+That is why the registry reclassification and the OAuth registration are one
+atomic change, not two independent edits.
+
+`OAuthCredentials` requires `access: string`, `refresh: string`, `expires: number`;
+`normalizeCredential` drops the whole credential if any of the three is missing or
+mistyped (`src/oauth/store.ts:447-502`).
+
+## The durable-key precedent already exists
+
+`devin` faces the same "no refresh endpoint" problem and solves it without
+inventing one: it stores the durable key as both `access` and `refresh`, sets
+`expires: Number.MAX_SAFE_INTEGER`, declares `defaultRefreshPolicy: "disabled"`,
+and its `refresh` throws `invalid_grant` so a forced refresh marks the account
+`needsReauth` instead of pretending success (`src/oauth/devin.ts:50-72, 155-166`,
+`src/oauth/index.ts:310-315`). `orcarouter-oauth` does the same. An empty
+`refresh: ""` is explicitly the wrong shape — it makes `detectOAuthWarning` report
+`stale_credentials` from the moment of login.
+
+This unit reuses that shape, with one difference that has to stay visible: for
+`devin` the stored string is a real API key; for `devin-cli` it is a non-secret
+presence marker, because there is no token for opencodex to hold.
+
+## The fail-closed check that makes this a migration
+
+`src/server/auth-cors.ts:731-737` rejects a saved provider row whose
+`authMode === "local"` when its registry entry is not local:
+
+> `provider ${name} cannot use authMode "local" — its registry entry requires ${entry.authKind} auth`
+
+`derive.ts:217-231` seeds `authMode` from `authKind`, so every config saved while
+`devin-cli` was local carries `authMode: "local"`. Flipping the registry to
+`oauth` without a migration turns those configs into a startup rejection. This is
+the single highest-risk item in the unit and `020` owns it.
+
+## Everything else `"local"` currently controls for this provider
+
+From `gui/src/provider-workspace/`: `catalog.ts:137-143` treats local as
+configuration-ready; `catalog.ts:170-174` puts it in the Free tier;
+`auth.ts:21-22` returns `null` so no auth surface is drawn; `kind.ts:12-21`
+classifies it as kind `local` for the rail filter. Under `oauth` all four change
+behaviour, which is the intent — an OAuth row gets an auth surface and a login
+button — but `030` has to look at the rail, not only the modal.
+
+From `src/providers/`: `fastwire.ts:109` returns `"none"` for local, so no
+Authorization header is attached. This matters: the `devin-cli` adapter never
+travels the fetch path at all (`buildRequest` is a placeholder), so the header
+policy is inert for it either way. `quota.ts:2899` and `key-failover.ts` skip
+local rows; under `oauth` they take the OAuth branches, which is correct because
+there is now an account to reason about.
+
+## What the Devin CLI itself stores
+
+The CLI keeps its own credential on disk as `credentials.toml`. opencodex never
+reads it; the adapter only spawns `devin acp` and the child authenticates itself
+(`src/adapters/devin-cli/adapter.ts:1-8`). The CLI is **not installed** on this
+machine, so the exact path and any non-interactive status subcommand are
+unconfirmed. `010` therefore treats both the path and the status probe as
+injected dependencies with a proven not-found branch, and `030` states plainly
+that the live signed-in path is unproven here.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/002_audit_resolution.md b/devlog/_fin/260912_devin_cli_account_login/002_audit_resolution.md
new file mode 100644
index 00000000000..dc9e6f4ef5a
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/002_audit_resolution.md
@@ -0,0 +1,101 @@
+# 002 — Audit resolution: the reclassification is the wrong mechanism
+
+Independent adversarial audit of `000`/`010`/`020`/`030` returned **VERDICT: FAIL**
+with four blockers. Three are fixable in place. The first invalidates the central
+decision, and the roadmap changes rather than arguing with it.
+
+## Blocker 1 (fatal to the original design)
+
+Flipping `authKind` to `oauth` couples the REQUEST path to a credential that
+carries no meaning. `src/router.ts:317-318` forces `authMode` from the registry
+for oauth entries, and `src/server/responses/core.ts:4323` then always calls
+`getValidAccessTokenSnapshot`, which throws `OAuthLoginRequiredError` when no
+account set exists (`src/oauth/index.ts:576-578`) and stamps
+`apiKey: resolved.accessToken` at `:4401`.
+
+Today a configured `devin-cli` row answers with no opencodex credential at all,
+because the child authenticates itself. Under the original plan every turn would
+401 until someone clicked Login, and a dashboard logout would break inference
+while the CLI stayed signed in. `020`'s boundary forbids touching
+`responses/core.ts`, so the plan could not have special-cased its way out.
+
+The audit also killed a claim in `000`: reclassifying does NOT make the row vanish.
+`providerTier` only puts the canonical OpenAI forward provider in `accounts`
+(`gui/src/provider-workspace/catalog.ts:160-181`), so an oauth preset with a
+non-loopback base URL lands in **Paid**, which is rendered. The original
+motivation sentence was wrong about the failure mode while being right that the
+Accounts tab is unreachable from the preset catalog.
+
+## The corrected mechanism
+
+Accounts-tab admission is `OAUTH_PROVIDERS` membership — `listOAuthProviders()`
+is `Object.keys(OAUTH_PROVIDERS)` minus `chatgpt`
+(`src/oauth/index.ts:369-371`, `src/server/management/oauth-account-routes.ts:139-140`).
+Nothing in that path reads `authKind`.
+
+`authKind: "oauth"` was only needed because `oauthConfig(id)` derives
+`providerConfig` through `deriveOAuthProviderConfig`, which filters on it
+(`src/providers/derive.ts:350-353`). But `providerConfig` is an ordinary
+`OcxProviderConfig` field — it can be built from the registry row directly.
+
+**So: register `devin-cli` in `OAUTH_PROVIDERS` and leave `authKind: "local"`
+alone.** The Accounts row appears; the request path keeps seeing a local
+provider, demands no token, and behaves exactly as it does today. The
+`auth-cors` migration in `020` and its whole new migration module become
+unnecessary, because no persisted `authMode` ever mismatches.
+
+This also resolves the honesty problem that made the original design
+uncomfortable: opencodex no longer needs a marker to stand in for a bearer
+token on the request path, because the request path never asks. The stored
+credential exists only so the Accounts row has a state to show.
+
+The residual risk moves to `isOAuthProvider("devin-cli")` becoming true, which
+switches on `ocx login` (`src/oauth/login-cli.ts:86-88`), changes `ocx account`
+(`src/cli/account-api.ts:83-93`), and admits the row to generic 429 failover
+(`src/oauth/generic-account-failover.ts:97-98`). wp2 must prove each of those
+three is either intended or inert for a stdio adapter, and `openUrl("")` in the
+CLI login path must not be reached.
+
+## Blocker 2 — preset duplication
+
+`dashboardPreset: true` keeps the row in `deriveProviderPresets`
+(`src/providers/derive.ts:365`), so it would show on a preset tab as well as
+Accounts. Set `dashboardPreset: false`, matching `devin` and `cursor`, and update
+the assertion at `tests/providers/devin-cli-adapter.test.ts:33` that currently
+pins it true. With `authKind` staying local the preset tab would otherwise be
+Free, not Paid, but the duplication is the same defect either way.
+
+## Blocker 3 — login cannot inherit stdio
+
+`010` said to run `devin auth login` with inherited stdio. Dashboard login is
+`POST /api/oauth/login` inside the proxy, typically a launchd process with no
+TTY. Use kiro's working shape instead: piped spawn with `stdin: "ignore"`
+(`src/oauth/kiro.ts:151-156`), surface the CLI's own output through
+`ctrl.onProgress`, and treat a login that cannot complete without a terminal as
+a reported failure rather than a hang. If the CLI turns out to require a TTY, the
+honest end state is an Accounts row that reports signed-in status and tells the
+operator to run `devin auth login` in their own terminal — wp2 decides this
+against the real binary and records which it was.
+
+## Blocker 4 — wrong label file
+
+Accounts rows use `oauthLabel` → `OAUTH_LABELS[id] ?? id`
+(`gui/src/pages/providers-shared.ts:49-59`), not `formatProviderDisplayName`.
+Without an `OAUTH_LABELS` entry the row reads `devin-cli`. `030`'s write set
+moves from `gui/src/provider-icons.ts` to `gui/src/pages/providers-shared.ts`.
+
+## Structure obligation the plan missed
+
+`structure/AGENTS.md:49` binds changes in `src/oauth/` and `src/providers/` to
+`runtime.md`, `subagents.md`, `transports/inventory.md`, and
+`providers/xai-grok.md`, not only `adapters/registry.md`. wp4 checks each for a
+sentence this change falsifies.
+
+## Effect on the work-phase map
+
+wp2 and wp3 swap emphasis: wp2 still builds `src/oauth/devin-cli.ts` (now with a
+piped spawn and no marker-as-bearer concern), wp3 becomes registration plus the
+`dashboardPreset` flip and the three `isOAuthProvider` consequences, with the
+`authKind` flip and its migration DELETED. wp4 is unchanged apart from the label
+file and the structure docs.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/003_blocker1_resolution.md b/devlog/_fin/260912_devin_cli_account_login/003_blocker1_resolution.md
new file mode 100644
index 00000000000..e91d181c5c2
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/003_blocker1_resolution.md
@@ -0,0 +1,81 @@
+# 003 — Blocker 1 resolved: import-first login, oauth classification kept
+
+`002` proposed dodging the request-path coupling by leaving `authKind: "local"`
+and registering in `OAUTH_PROVIDERS` anyway. The operator rejected the premise:
+devin-cli is not a local runtime. It is a CLI that requires a vendor account —
+demonstrated by installing it and signing in, after which
+`devin auth status` reports `Logged in (via Devin)` with its credential at
+`~/.local/share/devin/credentials.toml`. Ollama, vLLM and LM Studio have no
+account at all; grouping devin-cli with them was a taxonomy error.
+
+So the classification is `oauth`, and blocker 1 has to be solved rather than
+avoided.
+
+## The resolution
+
+Blocker 1 said: with `authKind: "oauth"`, `src/router.ts:317-318` forces
+`authMode`, `src/server/responses/core.ts:4323` calls
+`getValidAccessTokenSnapshot`, and that throws `OAuthLoginRequiredError` when no
+account set exists.
+
+That is only a defect while no credential is stored. Once the login entry has
+run, a credential exists, the snapshot resolves, `apiKey` is stamped onto a
+provider config the adapter never reads, and the turn proceeds exactly as it does
+today. Requiring one sign-in before an account provider answers is not a
+regression — it is what an account provider means, and it is what the operator
+asked for.
+
+**No change to `core.ts` or `router.ts` is needed.** The boundary in `000` holds.
+
+> **Superseded in part by `020`.** Audit round 2 disproved the startup-import half
+> of this section: `projectStartupConfigRepairs` is a synchronous projector
+> persisted through `mutatePersistedConfig`, which writes `config.json` only,
+> while `getValidAccessTokenSnapshot` reads the auth store. A boot pass there
+> cannot mint a credential, so existing installs DO need one sign-in after
+> upgrade. `020` carries the corrected, honest version. The login-time
+> import-first design below stands unchanged; only the boot-import claim is dead.
+
+## What does have to be built: import-first, so nobody is broken mid-flight
+
+A user who has `devin-cli` configured today and is signed into the CLI must not
+wake up to 401s. Kiro already solves this shape (`src/oauth/kiro.ts:335-429`):
+login imports an existing CLI session rather than starting a browser flow.
+
+wp2 therefore builds `loginDevinCli` import-first:
+
+1. `devin auth status` — confirmed present and non-interactive on 3000.10.21,
+ printing `Logged in (via Devin).` plus the credential path. This is the probe;
+ the subcommand is no longer a guess (`001` recorded it as unconfirmed).
+2. already signed in -> return the marker credential immediately, no browser.
+3. signed out -> run `devin auth login` with kiro's piped-spawn shape
+ (blocker 3), surfacing the CLI's own `Visit ... paste the code` prompt
+ through `ctrl.onAuth`/`onManualCodeInput` — that flow is confirmed: the CLI
+ prints a PKCE URL and accepts a pasted one-time code, which is exactly the
+ shape `onManualCodeInput` exists for.
+
+And wp3 adds a startup import for existing installs: when `devin-cli` is
+configured, has no stored credential, and `devin auth status` says signed in,
+store the marker so the first turn after upgrade succeeds without a click. Same
+repair pass as the other two migrations
+(`src/providers/model-rename-startup.ts`).
+
+## Blocker 2, 3, 4 — unchanged from `002`
+
+`dashboardPreset: false`; piped spawn; `OAUTH_LABELS` in
+`gui/src/pages/providers-shared.ts` is the Accounts label, not
+`provider-icons.ts`. The `로컬` badge disappears on its own once `auth` stops
+being `"local"` (`ProviderCatalog.tsx` badge ladder), which is the mark the
+operator asked to have removed.
+
+## Corrections to earlier docs from the live install
+
+- credential path is `$XDG_DATA_HOME/devin/credentials.toml`
+ (`~/.local/share/devin/...`), not `~/.config`. `010`'s path resolver changes.
+- `devin auth status` exists and is non-interactive. `010`'s "unconfirmed
+ subcommand" hedge is replaced by a real probe, and its test case 4 becomes a
+ regression guard rather than a guess.
+- A separate live defect was found and already landed on `dev` outside this unit:
+ the adapter passed `DEVIN_PERMISSION_MODE=ask`, which the CLI rejects with exit
+ 2, so every default-configuration turn failed (PR #4332, `e7f7487b3d`). This
+ unit assumes that fix is present.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/004_live_cli_probe.md b/devlog/_fin/260912_devin_cli_account_login/004_live_cli_probe.md
new file mode 100644
index 00000000000..7daa74d82d6
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/004_live_cli_probe.md
@@ -0,0 +1,63 @@
+# 004 — Live probe of the installed CLI
+
+Devin CLI 3000.10.21 installed to `~/.local/bin/devin` and signed in, so the
+guesses `001` recorded as unconfirmed are now measured. Everything below is
+observed output, not documentation.
+
+## `devin auth status`
+
+Exists, non-interactive, exit 0 when signed in:
+
+```
+$ devin auth status
+Logged in (via Devin).
+
+Credentials:
+ File: /Users/jun/.local/share/devin/credentials.toml
+ API server: https://server.codeium.com
+ Devin webapp: https://app.devin.ai
+ Devin API: https://api.devin.ai
+$ echo $?
+0
+```
+
+Signed out, before login, it printed `Not logged in.` with the same credentials
+path and a hint to run `devin auth login`.
+
+**No `--json`.** `devin auth status --json` fails with
+`error: unexpected argument '--json' found`. `010`'s probe must therefore parse
+exit status plus the literal prefix `Logged in`, not a JSON field. Parsing prose
+is fragile, so the probe treats exit 0 as authoritative and the prose only as a
+tiebreaker, and the `010` test set gains a case for a future wording change.
+
+## No identity to report
+
+`credentials.toml` holds four keys and none of them is an account identity:
+
+```
+windsurf_api_key =
+api_server_url =
+devin_webapp_host =
+devin_api_url =
+```
+
+Neither does `auth status`. So the credential this unit stores carries **no**
+`email` and **no** `accountId`. Two consequences `020` must handle:
+
+1. The Accounts row shows a signed-in state without an address. That is honest and
+ matches what the CLI itself can say.
+2. `saveCredential` upserts by `accountId ?? email`, so an identity-less
+ credential replaces the active slot rather than adding one
+ (`src/oauth/store.ts:735-818`). Multi-account is therefore out of scope for
+ this provider, and `020` should say so rather than leave a half-working
+ "Add account" button implying otherwise.
+
+## The file confirms the boundary this unit promised to keep
+
+`windsurf_api_key` is a real credential sitting in the CLI's own file. opencodex
+must not read it — doing so would turn "the CLI owns its credential" into a lie
+and would give the proxy a Cognition key it has no reason to hold. The probe
+checks **presence and exit status only**, never contents. `010`'s
+`readDevinCliSignedInState` is written that way and its test asserts no file
+bytes are read.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/005_cli_key_is_a_real_credential.md b/devlog/_fin/260912_devin_cli_account_login/005_cli_key_is_a_real_credential.md
new file mode 100644
index 00000000000..79e744b54aa
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/005_cli_key_is_a_real_credential.md
@@ -0,0 +1,83 @@
+# 005 — The CLI credential is an ordinary Cognition key
+
+This invalidates the mechanism the whole unit was built on, so it is recorded
+before anything else changes.
+
+## What was measured
+
+`~/.local/share/devin/credentials.toml` holds `windsurf_api_key` and
+`api_server_url`. Feeding those two straight into the EXISTING cloud-direct
+client:
+
+```
+host: https://server.codeium.com
+key shape: devin-session-token$ey...(189 chars)
+user_jwt minted: true
+catalog HTTP: 200
+models in catalog: 229 enabled: 229
+```
+
+and then a real turn through `streamChatEvents`:
+
+```
+REPLY: "CLIKEY-OK"
+```
+
+The CLI's key is a `devin-session-token$` — byte-for-byte the same shape
+`src/oauth/devin/types.ts` already documents for the Cognition era, and the same
+shape `ocx login devin` obtains through RegisterUser. It mints a `user_jwt`, it
+opens the full 229-model catalog, and it streams chat over
+`exa.api_server_pb.ApiServerService/GetChatMessage`.
+
+## Why that ends the argument the unit was having
+
+Every blocker the three audit rounds produced was downstream of one decision: that
+`devin-cli` has no credential opencodex may hold, so an account row would have to
+be faked with a marker.
+
+That premise is false. There is a real credential, in a file the CLI writes, in
+the format the proxy already parses.
+
+With a real token:
+
+- the marker disappears, and with it the "anything that treats this as a bearer is
+ a bug" caveat the first audit round correctly called already-false;
+- `authKind: "oauth"` is honest rather than a classification trick — the request
+ path resolves a genuine key and uses it;
+- blocker 1 evaporates: `getValidAccessTokenSnapshot` returns a working token, so
+ there is no 401-until-you-click and no upgrade story to apologise for;
+- blocker 3 evaporates: login is a file read, not an interactive paste, so the
+ `stdin` design that three rounds could not get right is not needed at all.
+ `spawnInteractive` and `DevinCliLoginChild` are deleted.
+
+This is exactly kiro's import-first shape, and now with the same substance:
+kiro imports a real token from an installed CLI's own store, and so does this.
+
+## The direction change
+
+**Before:** `devin-cli` drives `devin acp` over stdio; opencodex holds nothing;
+an account row needs a marker.
+
+**After:** `devin-cli` imports the CLI's key and routes through the cloud-direct
+Connect-RPC transport the `devin` adapter already owns.
+
+LOOP-CONTINUITY-01 requires a reason for changing direction. The reason is
+measured, above: the ACP route was chosen when the credential was believed
+unreachable, and it is not.
+
+What is genuinely given up: ACP runs Devin's own agent loop in the child, with its
+own tools and permissions. The cloud route is plain inference. For a proxy whose
+job is to expose a model to Codex and Claude Code, plain inference is the correct
+surface — the local agent loop was never the thing being exposed, and it is what
+produced the `--permission-mode` defect already fixed on `dev` (PR #4332).
+
+## What the unit becomes
+
+wp2 shrinks to a credential importer. wp3 keeps the `authKind` flip and
+`dashboardPreset: false`, and additionally repoints the adapter. wp4 is unchanged
+apart from describing the new transport.
+
+The phase documents are rewritten in `011`, `021`, `031`; `010`/`020`/`030` stay
+in place as the superseded record of the ACP design, because the audit trail that
+killed it is worth keeping.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/006_prior_art.md b/devlog/_fin/260912_devin_cli_account_login/006_prior_art.md
new file mode 100644
index 00000000000..a85cd464375
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/006_prior_art.md
@@ -0,0 +1,84 @@
+# 006 — Prior art: how everyone else uses this credential
+
+Survey of public implementations, to check that `005`'s direction is the one the
+ecosystem converged on rather than a local guess.
+
+## There is no OpenAI-shaped Devin API to point at
+
+No repo, and no Cognition page, exposes `POST /v1/chat/completions` that accepts a
+`devin-session-token`. `api.devin.ai` is a different product: cloud Devin
+**sessions** (`/v1/sessions`, `/v3/organizations/{org}/sessions`) authenticated
+with `cog_` service keys, which creates an agent VM rather than returning a
+completion.
+
+Every client that wanted an OpenAI surface built the translator itself, in front
+of Connect-RPC. That is exactly what opencodex already is, so the question
+"can we use it as an API instead of ACP" resolves to "yes, over the transport the
+`devin` adapter already owns."
+
+## The convergent pattern
+
+1. obtain a `devin-session-token$` — by RegisterUser, by CLI OAuth, or by
+ reading it from disk;
+2. send it as Metadata `api_key` to `api_server_url`, default
+ `https://server.codeium.com` — **not** `api.devin.ai`;
+3. chat over `exa.api_server_pb.ApiServerService/GetChatMessage`;
+4. list models over `GetCascadeModelConfigs` (or `GetCliModelConfigs`), optionally
+ minting a `user_jwt` first.
+
+Implementations reviewed at source level: `rsvedant/opencode-windsurf-auth` (the
+70-star original opencodex's cloud-direct client is derived from, MIT notice in
+`index.ts`), its live fork `sudokar/opencode-devin-bridge`, `ktappdev/pi-windsurf`,
+`CaiJingLong/devin-gateway`, `leookun/devin-2api`, `dwgx/WindsurfAPI` (2978 stars,
+its `DEVIN_CONNECT=1` path), and `can1357/oh-my-pi`'s first-class Devin provider.
+
+Two divergences worth knowing, neither blocking:
+
+- `CaiJingLong/devin-gateway` lists models with **`GetCliModelConfigs`** rather
+ than `GetCascadeModelConfigs`. opencodex's existing catalog path works against
+ this account (229 models, measured in `005`), so no change; recorded in case a
+ future account type answers only the CLI variant.
+- `oh-my-pi` calls **`AssignModel`** before chatting. opencodex does not and
+ streams fine, so it is not required for this surface.
+
+## Nobody else reads the CLI file for chat — and that is fine
+
+The repos that parse `credentials.toml` are usage monitors:
+`wakamex/devin-cli-usage` (`windsurf_api_key` + `api_server_url` →
+`SeatManagementService/GetUserStatus`), `robinebers/openusage`, and
+`SammySnake-d/fast-context-mcp`. The chat clients each mint or store their own
+token instead.
+
+So `011` is a new combination rather than a copied one: read the file the usage
+tools read, then use it on the transport the chat clients use. Both halves are
+independently attested, and `005` measured the join end to end. The reason nobody
+published this combination is likely that the other projects are not already
+holding a working cloud-direct client — opencodex is.
+
+## Key names confirmed by independent sources
+
+Cognition documents only that the CLI "stores your API token" in
+`credentials.toml` and never names the keys. Three unrelated projects observe the
+same two that matter:
+
+| key | role |
+|---|---|
+| `windsurf_api_key` | the durable credential, `devin-session-token$` |
+| `api_server_url` | Cognition api-server; where GetChatMessage goes |
+
+`devin_webapp_host` and `devin_api_url` are the webapp and the session-REST
+product; `011` reads neither.
+
+Token shapes in circulation: `devin-session-token$…` (current), `sk-ws-01-…`
+(older Windsurf RegisterUser), `cog_…` (official Devin session API — different
+product), `auth1_…` (web auth, not accepted as an api_key). `011`'s parser takes
+the value verbatim and lets the server judge it, which is the right posture given
+that spread.
+
+## Licensing note
+
+opencodex's `cloud-direct/` already carries the MIT attribution to
+`rsvedant/opencode-windsurf-auth` in `index.ts`. `011` adds no new derived code —
+it reads a local file and calls modules already in this tree — so no further
+attribution is owed.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/007_reference_proxy_corroboration.md b/devlog/_fin/260912_devin_cli_account_login/007_reference_proxy_corroboration.md
new file mode 100644
index 00000000000..0d7d1aac0d2
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/007_reference_proxy_corroboration.md
@@ -0,0 +1,39 @@
+# 007 — Independent corroboration from a working Devin CLI proxy
+
+A second reference implementation was supplied by the operator
+(`server (1).mjs`, 851 lines, "Devin CLI proxy: OpenAI-compatible /v1 API backed
+by `devin acp`"). It is an ACP proxy, so it is not the transport this unit
+chose — but it independently confirms the credential half of `011`.
+
+## Same path, same key, same parse
+
+```js
+// server (1).mjs:173-176
+? join(process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "devin", "credentials.toml")
+: join(process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share"), "devin", "credentials.toml"));
+...
+return readFileSync(path, "utf8").match(/windsurf_api_key\s*=\s*"([^"]+)"/)?.[1] ?? null;
+```
+
+That is the resolver `011` specifies, arrived at independently: `%APPDATA%` on
+Windows, `$XDG_DATA_HOME ?? ~/.local/share` elsewhere, and a line match for
+`windsurf_api_key` rather than a TOML dependency. Three sources now agree on the
+path and key name — this file, `wakamex/devin-cli-usage`, and the live probe in
+`004` — so `011`'s parser is not a guess.
+
+Its `readFileSync(...).match(...) ?? null` also returns null rather than throwing
+on a missing file, which is the shape `011` uses for
+`readDevinCliCredentialFile`.
+
+## What it does NOT corroborate
+
+It reads the key and then still spawns `devin acp` (`:329`), keeping one live
+child per session and compacting inside it. So it is evidence for where the
+credential lives, not for what to do with it. `005` is the evidence that the same
+key works directly against `server.codeium.com`, which this proxy never tries.
+
+Worth noting for anyone comparing: an ACP proxy inherits the CLI's agent loop and
+its per-session process, which is what makes session lifecycle, compaction and
+`--permission-mode` its own problem — the same class of defect PR #4332 fixed in
+opencodex's ACP adapter. The cloud route has none of that surface.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/010_phase1_cli_login.md b/devlog/_fin/260912_devin_cli_account_login/010_phase1_cli_login.md
new file mode 100644
index 00000000000..1aef9e65cb8
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/010_phase1_cli_login.md
@@ -0,0 +1,158 @@
+# 010 — Phase 1: the Devin CLI login entry
+
+**Work-phase:** wp2. **Write set:** `src/oauth/devin-cli.ts` (NEW),
+`tests/providers/devin-cli-login.test.ts` (NEW). Nothing else.
+
+Rewritten after audit rounds 1 and 2. Superseded guidance from the first draft:
+inherited stdio (audit blocker 3) and an `XDG_CONFIG_HOME` credential path
+(wrong, measured in `004`).
+
+## NEW `src/oauth/devin-cli.ts`
+
+```ts
+/**
+ * Devin CLI account login.
+ *
+ * The installed CLI owns its own credential. It writes
+ * \`$XDG_DATA_HOME/devin/credentials.toml\` — measured, not assumed — and that file
+ * holds a real \`windsurf_api_key\`. opencodex deliberately never reads it: the
+ * whole point of this provider is that the child authenticates itself, and
+ * lifting that key would hand the proxy a Cognition credential it has no reason
+ * to hold.
+ *
+ * What this module produces is an ACCOUNT ROW. The dashboard Accounts tab is
+ * built from OAUTH_PROVIDERS, so a provider absent from that map cannot appear
+ * there however it is classified. The stored \`access\` is the marker below — a
+ * non-secret constant, present because normalizeCredential drops any credential
+ * whose \`access\` is not a string. It is never a bearer token: the devin-cli
+ * adapter is runTurn-only, ignores provider.apiKey, and sends empty headers.
+ */
+export const DEVIN_CLI_SESSION_MARKER = "devin-cli-local-session";
+```
+
+### `devinCliCredentialsPath(env, platform)`
+
+Measured layout (`004`), not the config dir:
+
+- override `OPENCODEX_DEVIN_CLI_CREDENTIALS` (absolute only)
+- Windows `%APPDATA%/devin/credentials.toml`
+- otherwise `${XDG_DATA_HOME ?? ~/.local/share}/devin/credentials.toml`
+
+Used for a presence check only; the bytes are never read.
+
+### `readDevinCliSignedInState(deps)`
+
+```ts
+export interface DevinCliLoginDeps {
+ resolveBinary?: () => string | undefined;
+ credentialsPath?: () => string;
+ exists?: (path: string) => boolean;
+ /** Fire-and-wait. Enough for the `auth status` probe, which takes no input. */
+ run?: (bin: string, args: string[]) => Promise<{ code: number; stdout: string; stderr: string }>;
+ /**
+ * Interactive spawn, required for `auth login`.
+ *
+ * `run` cannot express that flow and an earlier draft wrongly reused it: the
+ * login child must receive a one-time code AFTER it has printed a URL, so the
+ * caller needs a live stdin handle and an incremental stdout stream, not a
+ * buffered result. The audit caught the draft specifying both kiro's
+ * `stdin: "ignore"` and a paste into that same child.
+ */
+ spawnInteractive?: (bin: string, args: string[]) => DevinCliLoginChild;
+}
+export interface DevinCliLoginChild {
+ /** Called with each chunk of stdout/stderr as it arrives. */
+ onOutput(listener: (chunk: string) => void): void;
+ /** Writes the pasted code; the implementation appends the newline. */
+ writeLine(text: string): void;
+ /** Resolves with the exit code. */
+ wait(): Promise;
+ /** Terminates the child and its group, for the deadline path. */
+ kill(): void;
+}
+export interface DevinCliSignedInState { signedIn: boolean; reason?: "not-installed" | "signed-out" }
+```
+
+1. binary missing -> `{ signedIn: false, reason: "not-installed" }`
+2. `devin auth status` — confirmed present and non-interactive. **Exit code is
+ authoritative**; there is no `--json` (`devin auth status --json` fails with
+ `unexpected argument`), so the prose is only a tiebreaker when the exit code
+ is ambiguous. Exit 0 -> signed in.
+3. non-zero exit but the credential file exists -> signed in, so a future wording
+ or exit-code change degrades to the file check instead of locking the user out
+4. otherwise `{ signedIn: false, reason: "signed-out" }`
+
+No `identity` field. `004` measured that neither `auth status` nor
+`credentials.toml` exposes an account address, so inventing one would be a lie.
+
+### `loginDevinCli(ctrl, opts, deps)`
+
+1. binary missing -> throw with `DEVIN_CLI_INSTALL_HINT` reused verbatim from
+ `src/adapters/devin-cli/binary.ts`.
+2. signed in and `!opts?.forceLogin` -> return the credential; import-first, no
+ browser, the kiro shape.
+3. signed out -> `spawnInteractive(bin, ["auth", "login"])` with **all three
+ streams piped**: `stdio: ["pipe", "pipe", "pipe"]`.
+
+ Kiro's runner uses `stdin: "ignore"` (`src/oauth/kiro.ts:151-156`) and an
+ earlier draft of this document copied it. That is wrong here, and the audit
+ caught it: kiro's CLI completes on its own and kiro then imports the token,
+ whereas this flow has to hand a one-time code BACK to the child. With stdin
+ ignored the child waits for a paste that can never arrive and the login hangs
+ until the deadline. Inheriting the proxy's stdio is equally wrong for the
+ opposite reason — dashboard login is `POST /api/oauth/login` inside a launchd
+ process with no TTY.
+
+ So: piped stdin to write the code, piped stdout **and stderr** to find the
+ prompt. Measured shape of that prompt (`004`):
+ `Visit https://app.devin.ai/auth/cli/continue?...&cli_pkce_marker=1 to sign in, then copy the code and paste it below.`
+ Scrape the URL, hand it to `ctrl.onAuth({ url, instructions })`, take the code
+ from `ctrl.onManualCodeInput()`, and write it to stdin followed by a newline.
+ The CLI answers `Login successful! Credentials stored.` on success.
+4. If no URL appears within the deadline, abort the child and throw a message
+ naming the manual path: run `devin auth login` in a terminal, then press Login
+ again — the import-first branch will pick it up.
+
+Returns:
+
+```ts
+{
+ access: DEVIN_CLI_SESSION_MARKER,
+ refresh: DEVIN_CLI_SESSION_MARKER,
+ expires: Number.MAX_SAFE_INTEGER,
+ source: "local-cli",
+}
+```
+
+No `email`/`accountId` (see `004`). Marker duplicated and a MAX expiry, matching
+the `devin` durable-key shape; `refresh: ""` would trip `detectOAuthWarning`.
+
+### `refreshDevinCliToken()`
+
+Throws `invalid_grant: the Devin CLI owns its own session. Run devin auth login again.`
+
+## NEW `tests/providers/devin-cli-login.test.ts`
+
+Registered in `scripts/test-layout/layout.json` and
+`tests/fixtures/test-layout-expected.json` under `providers`.
+
+1. binary absent -> login throws, message carries the install hint
+2. `auth status` exit 0 -> signed in; credential is marker/marker/MAX, carries no
+ email or accountId, and `exists` was never called on the credential file
+ contents (presence only)
+3. `auth status` non-zero but file present -> still signed in (wording-change guard)
+4. `auth status` non-zero and no file -> signed out
+5. signed out + login -> `spawnInteractive` receives `["auth","login"]`, the fake
+ child emits the measured `Visit ...` line, that URL reaches `onAuth`, and
+ the code from `onManualCodeInput` arrives at `writeLine`. Asserts the child is
+ never handed the proxy's own streams
+8. a login whose stdin is not writable fails loudly rather than hanging — the
+ regression guard for the `stdin: "ignore"` mistake this phase already made once
+6. login producing no URL before the deadline -> throws naming the terminal fallback
+7. `refreshDevinCliToken` rejects with `invalid_grant`
+
+## Acceptance
+
+- No import from `src/router.ts`, `src/server/lifecycle.ts`, `src/server/responses/core.ts`.
+- `bun test tests/providers/devin-cli-login.test.ts` green.
+- `src/adapters/devin-cli/` untouched in this phase.
diff --git a/devlog/_fin/260912_devin_cli_account_login/011_phase1_credential_import.md b/devlog/_fin/260912_devin_cli_account_login/011_phase1_credential_import.md
new file mode 100644
index 00000000000..26783c266f6
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/011_phase1_credential_import.md
@@ -0,0 +1,221 @@
+# 011 — Phase 1 (revised): import the CLI's credential
+
+**Supersedes `010`.** `005` measured that the Devin CLI stores a real Cognition
+key, so this phase is a file import, not an interactive login.
+
+**Work-phase:** wp2. **Write set:** `src/oauth/devin-cli.ts` (NEW), `src/oauth/devin.ts` (export
+`identityFromApiKey`, add the `providerId` parameter), `src/adapters/devin.ts`
+(consume it), `src/adapters/registry.ts` + `src/server/adapter-resolve.ts` +
+`src/server/responses/core.ts` (thread `providerId`),
+`tests/providers/devin-cli-login.test.ts` (NEW).
+
+## NEW `src/oauth/devin-cli.ts`
+
+```ts
+/**
+ * Devin CLI credential import.
+ *
+ * The installed CLI writes \`$XDG_DATA_HOME/devin/credentials.toml\` after
+ * \`devin auth login\`, and the \`windsurf_api_key\` in it is an ordinary
+ * \`devin-session-token$\` — the same shape RegisterUser returns for
+ * \`ocx login devin\`, and the same one the cloud-direct client already speaks.
+ * Measured: it mints a user_jwt, opens the 229-model catalog, and streams chat.
+ *
+ * So this is kiro's import-first login with the same substance: read a signed-in
+ * local CLI's own store and adopt the token, rather than starting a browser flow
+ * the CLI has already completed.
+ */
+```
+
+### `devinCliCredentialsPath(env, platform)`
+
+- override `OPENCODEX_DEVIN_CLI_CREDENTIALS` (absolute only)
+- Windows `%APPDATA%/devin/credentials.toml`
+- otherwise `${XDG_DATA_HOME ?? ~/.local/share}/devin/credentials.toml`
+
+Measured in `004`. Not the config dir.
+
+### `readDevinCliCredentialFile(deps)`
+
+Parses only the two keys it needs, with a minimal line matcher rather than a TOML
+dependency — the file is flat and adding a parser for four keys is not worth it:
+
+```
+windsurf_api_key = "devin-session-token$..."
+api_server_url = "https://server.codeium.com"
+```
+
+Returns `{ apiKey, apiServerUrl }` or `undefined` when the file is absent or
+either key is missing. **Never logs either value.**
+
+### `loginDevinCli(ctrl, opts, deps)`
+
+1. file missing or unparseable -> throw, message naming
+ `DEVIN_CLI_INSTALL_HINT` plus "run `devin auth login`, then press Login again".
+ No browser is ever opened: there is nothing for opencodex to authorize.
+2. `resolveDevinApiBaseUrl(apiServerUrl)` — reuse
+ `src/oauth/devin/api-base.ts` verbatim. The host comes off disk and then
+ receives the key, so it passes the same allowlist as the RegisterUser host.
+ An unallowlisted value falls back to the default rather than exfiltrating.
+3. Return the credential, reusing the newly exported `identityFromApiKey` from
+ `src/oauth/devin.ts` so an email or sub in the JWT becomes the account identity:
+
+```ts
+{
+ access: apiKey,
+ refresh: apiKey, // durable-key pattern; "" trips detectOAuthWarning
+ expires: Number.MAX_SAFE_INTEGER,
+ source: "local-cli",
+ apiBaseUrl: resolvedHost,
+ ...identityFromApiKey(apiKey),
+}
+```
+
+`ctrl.onProgress?.("Imported the signed-in Devin CLI session.")`; `onAuth` is
+never called, which is the shape `startLoginFlow` already handles for local
+imports.
+
+### MODIFY `src/oauth/devin.ts` — make host resolution provider-aware
+
+The import stores `apiBaseUrl`, but nothing reads it. `createDevinAdapter` calls
+`resolveDevinApiServer(provider.baseUrl)` (`src/adapters/devin.ts:219`), and that
+helper is hard-coded to one credential:
+
+```ts
+// src/oauth/devin.ts:26-31 — today
+export function resolveDevinApiServer(configuredBaseUrl?: string): string {
+ return (
+ validateDevinApiBaseUrl(getCredential("devin")?.apiBaseUrl) ??
+ validateDevinApiBaseUrl(configuredBaseUrl) ??
+ DEVIN_DEFAULT_API_SERVER
+ );
+}
+```
+
+Left alone, a `devin-cli`-only user is pinned to the default host regardless of
+what their CLI recorded, and a user signed into both providers would send the CLI
+key to whatever tenant the browser-login `devin` credential named — an EU or
+FedStart account crossed with a US one. Two accounts, one host.
+
+```diff
+-export function resolveDevinApiServer(configuredBaseUrl?: string): string {
++export function resolveDevinApiServer(configuredBaseUrl?: string, providerId = "devin"): string {
+ return (
+- validateDevinApiBaseUrl(getCredential("devin")?.apiBaseUrl) ??
++ validateDevinApiBaseUrl(getCredential(providerId)?.apiBaseUrl) ??
+ validateDevinApiBaseUrl(configuredBaseUrl) ??
+ DEVIN_DEFAULT_API_SERVER
+ );
+ }
+```
+
+### The adapter has no provider id today — this is the plumbing
+
+An earlier draft said "the adapter passes the provider it was built for" without
+checking that it can. It cannot: `createDevinAdapter` receives only
+`OcxProviderConfig`, which has no id, and after the flip both `devin` and
+`devin-cli` share `adapter: "devin"`, so the factory cannot infer the store key.
+The audit was right that the edit was unimplementable as written.
+
+The name IS available at the call sites — `route.providerName` at
+`src/server/responses/core.ts:1436` and `:4171` — so it only has to be threaded:
+
+```diff
+ // src/adapters/registry.ts
+ export interface AdapterFactoryContext {
+ cacheRetention?: AdapterCacheRetention;
++ /**
++ * The configured provider row this adapter serves. Needed when one adapter
++ * backs two provider ids whose credentials differ — `devin` and `devin-cli`
++ * share a transport and a token format but sign in to different accounts and
++ * can sit on different tenants.
++ */
++ providerId?: string;
+ }
+```
+
+```diff
+ // src/server/adapter-resolve.ts
+-export function resolveAdapter(providerConfig: OcxProviderConfig, cacheRetention?: "none" | "short" | "long") {
+- return createRegisteredAdapter(providerConfig, { cacheRetention });
++export function resolveAdapter(
++ providerConfig: OcxProviderConfig,
++ cacheRetention?: "none" | "short" | "long",
++ providerId?: string,
++) {
++ return createRegisteredAdapter(providerConfig, { cacheRetention, ...(providerId ? { providerId } : {}) });
+ }
+```
+
+Both `core.ts` call sites pass `route.providerName`. The parameter is optional and
+every other adapter ignores it, so no existing behaviour moves. This touches
+`core.ts` but does not make it reach `src/lab/`, which is the boundary `000` set.
+
+`createDevinAdapter(provider, context)` then resolves with
+`context.providerId ?? "devin"`, preserving today's behaviour for any caller that
+does not supply one.
+
+One easily-missed hop, flagged by the final audit: the registry factory currently
+drops the context on the floor, so the field would arrive nowhere.
+
+```diff
+ // src/adapters/registry.ts
+ devin: {
+ wire: "devin",
+ mutation: "codex-owned",
+- create: (provider: OcxProviderConfig, _context: AdapterFactoryContext) => createDevinAdapter(provider),
++ create: (provider: OcxProviderConfig, context: AdapterFactoryContext) => createDevinAdapter(provider, context),
+ },
+```
+
+`createRegisteredAdapter` already forwards `context` to `definition.create`, so
+this one line is the whole remaining gap. Test 9 fails without it, which is the
+point of routing that test through the factory.
+
+`identityFromApiKey` is likewise private at `src/oauth/devin.ts:45`; export it
+rather than copying the JWT decode into a second file.
+
+### `refreshDevinCliToken()`
+
+Throws `invalid_grant: the Devin CLI owns this session. Run devin auth login again.`
+Same posture as `refreshDevinToken`.
+
+### What is deleted relative to `010`
+
+`DEVIN_CLI_SESSION_MARKER`, `spawnInteractive`, `DevinCliLoginChild`, the PKCE
+URL scraper, and the deadline path. None of them has a reason to exist once the
+credential is readable.
+
+## NEW `tests/providers/devin-cli-login.test.ts`
+
+Registered in `scripts/test-layout/layout.json` and
+`tests/fixtures/test-layout-expected.json` under `providers`.
+
+1. file absent -> throws, message carries the install hint
+2. file present -> credential is `access === refresh === `,
+ `expires === Number.MAX_SAFE_INTEGER`, `source === "local-cli"`
+3. identity: a key whose JWT carries an email surfaces it; one without does not
+ invent one
+4. `api_server_url` pointing at a non-Cognition host -> falls back to
+ `DEVIN_DEFAULT_API_SERVER`, and the off-allowlist host never reaches the
+ credential
+5. malformed file (missing `windsurf_api_key`) -> throws rather than returning a
+ half credential
+6. neither the key nor the host appears in anything passed to `onProgress`
+7. `refreshDevinCliToken` rejects with `invalid_grant`
+8. `resolveDevinApiServer(undefined, "devin-cli")` returns the host stored on the
+ devin-cli credential and does NOT read the `devin` credential
+9. **through the adapter, not only the helper**: build the adapter with
+ `createRegisteredAdapter(devinRow, { providerId: "devin-cli" })` while a
+ `devin` credential names a different allowlisted tenant, and assert the RPC
+ host is the CLI one. A green helper test alone would not have caught the
+ crossed-tenant bug, which is exactly what the audit said
+
+## Acceptance
+
+- No import from `src/router.ts`, `src/server/lifecycle.ts`, `src/server/responses/core.ts`.
+- `bun test tests/providers/devin-cli-login.test.ts` green.
+- A live import against this machine's signed-in CLI yields a credential that
+ streams a real turn — the `CLIKEY-OK` probe in `005`, repeated through the
+ module rather than through a scratch script.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/020_phase2_reclassify.md b/devlog/_fin/260912_devin_cli_account_login/020_phase2_reclassify.md
new file mode 100644
index 00000000000..857a6d074a0
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/020_phase2_reclassify.md
@@ -0,0 +1,127 @@
+# 020 — Phase 2: reclassify local to oauth
+
+**Work-phase:** wp3. **Write set:** `src/providers/registry.ts`,
+`src/oauth/index.ts`, `src/providers/devin-cli-authmode-migration.ts` (NEW),
+`src/providers/model-rename-startup.ts`, `src/adapters/devin-cli/adapter.ts`
+(comment only), `tests/providers/devin-cli-adapter.test.ts`, NEW migration test.
+
+Rewritten after audit rounds 1 and 2. Folded: `dashboardPreset: false` (blocker
+2, missing from the first draft) and the honest upgrade story (blocker 1).
+
+Atomic: `oauthConfig("devin-cli")` throws unless the registry row already says
+`authKind: "oauth"` (`src/providers/derive.ts:350-353`), so the registry edit and
+the OAUTH_PROVIDERS entry land in one commit.
+
+## MODIFY `src/providers/registry.ts`
+
+```diff
+- // Drives the locally installed Devin CLI. The CLI owns its own credentials
+- // from `devin auth login`, so this provider takes no key and the proxy never
+- // holds one. Inference happens in the child process, which is why the
+- // destination is a stdio scheme rather than a URL.
++ // Drives the locally installed Devin CLI. The CLI owns its own credential and
++ // authenticates itself inside the child, so the proxy still never holds a
++ // Devin token and the adapter never reads one at request time.
++ //
++ // `oauth` classifies the ACCOUNT, not the transport. This is not a local
++ // runtime: unlike Ollama or LM Studio it cannot answer at all until a vendor
++ // account is signed in, and `local` grouped it with things that have no
++ // account. It is also the only classification under which the row reaches the
++ // dashboard Accounts tab, which is built from OAUTH_PROVIDERS.
+ id: "devin-cli",
+- authKind: "local",
++ authKind: "oauth",
+ featured: false,
+- dashboardPreset: true,
++ // Off, like `devin`. `deriveProviderPresets` keys the preset
++ // catalog off this flag, so leaving it true would draw the row twice: once as
++ // an Accounts login row and again as a preset tile.
++ dashboardPreset: false,
+```
+
+`note` keeps "no API key is stored by opencodex" — still true — and gains
+"Sign in from the dashboard, or run `devin auth login` yourself; opencodex only
+records that the CLI session exists."
+
+## MODIFY `src/oauth/index.ts`
+
+```ts
+"devin-cli": {
+ login: (ctrl, opts) => loginDevinCli(ctrl, opts),
+ refresh: (rt, signal, credential) => refreshDevinCliToken(rt, signal, credential),
+ providerConfig: oauthConfig("devin-cli"),
+ defaultModel: oauthDefaultModel("devin-cli"),
+ // No endpoint to refresh against: the CLI owns the session. Same posture as
+ // `devin` and `orcarouter-oauth`.
+ defaultRefreshPolicy: "disabled",
+},
+```
+
+Not in `FORCE_REFRESH_PROVIDERS`: a 401 cannot arrive from a provider that never
+sends a token.
+
+## NEW `src/providers/devin-cli-authmode-migration.ts`
+
+`src/server/auth-cors.ts:731-737` rejects a saved row whose `authMode === "local"`
+once the registry entry is not local, and `derive.ts:217-231` seeded exactly that
+value into every config saved while devin-cli was local.
+
+```ts
+export function projectDevinCliAuthMode(config: OcxConfig) {
+ const prov = config.providers?.["devin-cli"];
+ if (!prov || prov.adapter !== "devin-cli" || prov.authMode !== "local") return { config, changed: false, warnings: [] };
+ prov.authMode = "oauth";
+ return { config, changed: true, warnings: ["rewrote devin-cli authMode local -> oauth: the registry no longer classifies it as local, and the management write boundary fails closed on the mismatch."] };
+}
+```
+
+Guarded exactly like `projectStaleContextWindows`: exact old value, adapter still
+`devin-cli`, nothing else touched. Composed into `projectStartupConfigRepairs`.
+
+**What this migration does NOT do.** It cannot mint a credential.
+`projectStartupConfigRepairs` is a synchronous projector persisted through
+`mutatePersistedConfig`, which writes `config.json` and never the auth store, and
+`getValidAccessTokenSnapshot` reads the auth store. An earlier draft claimed a
+boot import would keep existing installs working; the audit disproved it. The
+honest upgrade story is therefore:
+
+> After upgrading, an existing `devin-cli` user's first turn returns
+> `OAuthLoginRequiredError` until they sign in once — one click in the dashboard
+> Accounts tab, or `ocx login devin-cli`. Because login is import-first and the
+> CLI is already signed in, that click completes without a browser.
+
+This ships in the release note and in the provider docs. wp4 verifies both the
+401-before and the success-after on this machine.
+
+## MODIFY `tests/providers/devin-cli-adapter.test.ts`
+
+```diff
+- test("is a local provider that stores no credential", () => {
++ test("is an account provider whose adapter still holds no credential", () => {
+ const entry = PROVIDER_REGISTRY.find((row) => row.id === "devin-cli");
+ expect(entry?.adapter).toBe("devin-cli");
+- expect(entry?.authKind).toBe("local");
+- expect(entry?.dashboardPreset).toBe(true);
++ // `oauth` classifies the account, not the request path.
++ expect(entry?.authKind).toBe("oauth");
++ // Off, or the row is drawn twice — Accounts login row plus preset tile.
++ expect(entry?.dashboardPreset).toBe(false);
++ expect(OAUTH_PROVIDERS["devin-cli"]).toBeDefined();
+```
+
+New assertion that the runtime posture is unchanged: extend the existing
+handshake test so `DEVIN_CLI_SESSION_MARKER` never appears in the child's env or
+in anything written to its stdin.
+
+## MODIFY `src/adapters/devin-cli/adapter.ts` (comment only)
+
+Header gains: the dashboard now carries an account row recording that a CLI
+session exists; it never produces a credential this adapter reads.
+
+## Acceptance
+
+- `bun test tests/providers/devin-cli-adapter.test.ts tests/providers/devin-cli-login.test.ts` green.
+- A config carrying `authMode: "local"` is repaired; proven by a focused test and
+ a dry run against the real saved config.
+- `listOAuthProviders()` includes `devin-cli`; `deriveProviderPresets()` does not.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/021_phase2_oauth_and_transport.md b/devlog/_fin/260912_devin_cli_account_login/021_phase2_oauth_and_transport.md
new file mode 100644
index 00000000000..2f556428269
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/021_phase2_oauth_and_transport.md
@@ -0,0 +1,151 @@
+# 021 — Phase 2 (revised): oauth classification and the cloud transport
+
+**Supersedes `020`.** The marker, the 401-until-you-click upgrade story, and the
+whole reason those existed are gone (`005`).
+
+**Work-phase:** wp3. **Write set:** `src/providers/registry.ts`,
+`src/oauth/index.ts`, `src/providers/devin-cli-authmode-migration.ts` (NEW),
+`src/providers/model-rename-startup.ts`,
+`tests/providers/devin-cli-adapter.test.ts`, NEW migration test.
+
+## MODIFY `src/providers/registry.ts`
+
+```diff
+- // Drives the locally installed Devin CLI. The CLI owns its own credentials
+- // from `devin auth login`, so this provider takes no key and the proxy never
+- // holds one. Inference happens in the child process, which is why the
+- // destination is a stdio scheme rather than a URL.
++ // The signed-in Devin CLI as an account source. The CLI writes a
++ // `devin-session-token$` to its own credentials.toml, which is the same
++ // credential RegisterUser hands `ocx login devin` and which the cloud-direct
++ // client already speaks, so this provider imports that token and streams over
++ // Connect-RPC like its browser-login sibling.
++ //
++ // It is NOT a local runtime. Unlike Ollama or LM Studio it cannot answer
++ // without a vendor account, and `local` grouped it with things that have no
++ // account at all. `oauth` is also the only classification that reaches the
++ // dashboard Accounts tab, which is built from OAUTH_PROVIDERS.
+ id: "devin-cli",
+- label: "Devin CLI (local)",
+- adapter: "devin-cli",
+- baseUrl: "https://cli.devin.ai",
+- authKind: "local",
++ label: "Devin CLI",
++ adapter: "devin",
++ baseUrl: DEVIN_DEFAULT_API_SERVER,
++ authKind: "oauth",
+ featured: false,
+- dashboardPreset: true,
++ // Off, like `devin`. `deriveProviderPresets` keys the preset catalog off this
++ // flag, so leaving it true would draw the row twice: an Accounts login row and
++ // a preset tile.
++ dashboardPreset: false,
++ liveModels: true,
++ modelContextWindows: DEVIN_MODEL_CONTEXT_WINDOWS,
+```
+
+`models` / `defaultModel` move from the eleven-entry CLI roster to the cloud
+roster, because discovery is now live against the account's own catalog. The
+context windows come from `ClientModelConfig` field #18 through the same live
+path the `devin` provider uses, so this phase also retires the hand-maintained
+`DEVIN_CLI_MODEL_CONTEXT_WINDOWS` table.
+
+### The ACP adapter stays registered, but `devin-cli` can no longer reach it
+
+An earlier draft called this an escape hatch — "set `"adapter": "devin-cli"` in
+your row and keep ACP". The audit disproved it. `routedProviderConfig` pins the
+adapter from the registry whenever the row's name matches a registry id:
+
+```
+// src/router.ts:373-376
+const resolved: OcxProviderConfig = { ...provider, adapter: registryEntry.adapter, baseUrl, ...
+```
+
+and `providerMatchesRegistryTransport` (`src/providers/registry.ts:3530-3536`)
+returns true for anything that is not a `key` destination with
+`preserveCustomDestination`, so an oauth row is always pinned. Model discovery
+pins the same way (`src/oauth/index.ts:1110-1123`), and `upsertOAuthProvider`
+(`:1463-1490`) copies `adapter` from the preset on the first login, so the saved
+row is rewritten too.
+
+So the honest statement is: **after this phase the `devin-cli` PRESET is the cloud
+transport, and nothing named `devin-cli` runs ACP.** The adapter itself stays
+registered and tested, and is reachable from a differently named custom row:
+
+```json
+"providers": { "devin-acp": { "adapter": "devin-cli", "baseUrl": "https://cli.devin.ai" } }
+```
+
+That is a real capability, not a fig leaf: a custom id is not a registry id, so no
+pin applies.
+
+### The migration therefore has to warn, not just rewrite
+
+An operator who deliberately chose ACP would otherwise switch transports silently
+on upgrade. `projectDevinCliAuthMode` gains a second, non-mutating job: when the
+saved row still carries `adapter: "devin-cli"`, emit a startup warning naming the
+change and the exact `devin-acp` snippet above. It does not attempt to rename the
+row — a rename would move a provider the user's model ids point at.
+
+## MODIFY `src/oauth/index.ts`
+
+```ts
+"devin-cli": {
+ login: (ctrl, opts) => loginDevinCli(ctrl, opts),
+ refresh: (rt, signal, credential) => refreshDevinCliToken(rt, signal, credential),
+ providerConfig: oauthConfig("devin-cli"),
+ defaultModel: oauthDefaultModel("devin-cli"),
+ // The CLI owns the session and Cognition exposes no refresh endpoint. Same
+ // posture as `devin` and `orcarouter-oauth`.
+ defaultRefreshPolicy: "disabled",
+},
+```
+
+Atomic with the registry edit: `oauthConfig("devin-cli")` throws at module load
+when `deriveOAuthProviderConfig` returns undefined (`src/oauth/index.ts:204-207`),
+which it does unless `authKind` is already `"oauth"` — the filter is at
+`src/providers/derive.ts:350-353`, the throw is in `oauthConfig`.
+
+## NEW `src/providers/devin-cli-authmode-migration.ts`
+
+`src/server/auth-cors.ts:731-737` rejects a saved row whose `authMode === "local"`
+once the registry entry is not local, and `derive.ts:217-231` seeded exactly that
+value while devin-cli was local. Rewrite `local` -> `oauth`, guarded on the exact
+old value, composed into `projectStartupConfigRepairs` beside the two migrations
+already there.
+
+Unlike `020`, this pass no longer has to apologise for a credential it cannot
+mint: the token arrives from the import at login.
+
+It must not repeat the hatch claim either. The migration leaves the saved
+`adapter` field in place, but **that field does not choose the transport** for a
+registry-named row — routing pins it (`src/router.ts:373-376`). Leaving the value
+alone preserves nothing except a signal the warning can detect. An existing ACP
+user keeps ACP only by moving to a custom-named row, which is what the warning
+tells them to do.
+
+## MODIFY `tests/providers/devin-cli-adapter.test.ts`
+
+```diff
+- test("is a local provider that stores no credential", () => {
++ test("is an account provider sourced from the installed CLI", () => {
+ const entry = PROVIDER_REGISTRY.find((row) => row.id === "devin-cli");
+- expect(entry?.adapter).toBe("devin-cli");
+- expect(entry?.authKind).toBe("local");
+- expect(entry?.dashboardPreset).toBe(true);
++ expect(entry?.adapter).toBe("devin");
++ expect(entry?.authKind).toBe("oauth");
++ expect(entry?.dashboardPreset).toBe(false);
++ expect(OAUTH_PROVIDERS["devin-cli"]).toBeDefined();
+```
+
+The ACP `describe` blocks stay exactly as they are: that adapter is still
+registered and still has to work for anyone who selects it explicitly.
+
+## Acceptance
+
+- `bun test tests/providers/devin-cli-adapter.test.ts tests/providers/devin-cli-login.test.ts` green.
+- `listOAuthProviders()` includes `devin-cli`; `deriveProviderPresets()` does not.
+- A saved `authMode: "local"` row is repaired, proven by a focused test and a dry
+ run against the real config.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/030_phase3_surface_and_land.md b/devlog/_fin/260912_devin_cli_account_login/030_phase3_surface_and_land.md
new file mode 100644
index 00000000000..c5def2e7b87
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/030_phase3_surface_and_land.md
@@ -0,0 +1,89 @@
+# 030 — Phase 3: surface, docs, and landing
+
+**Work-phase:** wp4. **Write set:** `gui/src/pages/providers-shared.ts`,
+`docs-site/` English + 7 locales, `structure/` docs, PR.
+
+Rewritten after audit rounds 1 and 2. The first draft targeted
+`gui/src/provider-icons.ts`, which is the wrong surface (blocker 4).
+
+## GUI — the label lives in OAUTH_LABELS
+
+Accounts rows resolve their title through `oauthLabel(id)` →
+`OAUTH_LABELS[id] ?? id` (`gui/src/pages/providers-shared.ts:49-59`, consumed at
+`gui/src/pages/providers-page-utils.ts:21-23`). `formatProviderDisplayName`
+already returns "Devin CLI" but oauth rows never call it, so without a new entry
+the row reads `devin-cli`.
+
+```diff
+ const OAUTH_LABELS: Record = {
+ ...
++ "devin-cli": "Devin CLI",
+ };
+```
+
+The icon needs nothing: `devin-cli` already maps to `devin.svg`.
+
+### The `local` badge disappears on its own
+
+`ProviderCatalog.tsx`'s badge ladder draws the amber `modal.badge.local` chip
+from `p.auth === "local"`, and `derive.ts:613` derives that from `authKind`.
+Once the registry says `oauth` the chip is gone and the row is an Accounts login
+row instead — which is the mark the operator asked to have removed. Nothing to
+edit; wp4 verifies it rather than assuming it.
+
+### Rail, not only the modal
+
+`gui/src/provider-workspace/auth.ts:21-22` returned `null` for local, and
+`kind.ts:12-21` classified the row as kind `local`. Under `oauth` both change:
+the row becomes a login kind and an auth surface is drawn. Check that surface
+does not offer an API-key field for a provider that takes no key — that would be
+a regression this unit introduced.
+
+## Docs
+
+English:
+
+- `docs-site/src/content/docs/reference/adapters.md:458-459` — "none held by
+ opencodex ... stores no key and asks for none." Keep the true half, add the
+ account row and the one-time sign-in.
+- `docs-site/src/content/docs/guides/providers.md:196` — same sentence in the
+ table.
+
+Locale copies of that sentence, all of which assert opencodex stores no key (still
+true; the sign-in sentence is added): `ko:117`, `ja:119`, `zh-cn:111`,
+`zh-tw:116`, `fr:130`, `ru:128`, `tr:143`.
+
+Upgrade note, from `020`: an existing user's first turn after upgrade returns
+`OAuthLoginRequiredError` until one sign-in. Import-first makes that click
+complete without a browser.
+
+## structure/
+
+`structure/AGENTS.md:49` binds `src/oauth/` and `src/providers/` changes to
+`runtime.md`, `subagents.md`, `transports/inventory.md` and
+`providers/xai-grok.md` — not only `adapters/registry.md`, which the first draft
+named alone. Read each and update any sentence this change falsifies;
+`structure/adapters/registry.md:16-25` says the two Devin adapters have
+"separate credentials", which stays true and gains the account row.
+
+## Proof against the running service
+
+1. `GET /api/oauth/providers` lists `devin-cli`.
+2. `GET /api/provider-presets` does NOT list it (blocker 2 regression guard).
+3. The Accounts tab renders a **Devin CLI** row beside devin, with no `local`
+ badge — screenshot, which the PR needs anyway because `gui/` changed.
+4. `ocx login devin-cli` completes without a browser against the already
+ signed-in CLI, and a `codex exec -m devin-cli/swe-2` turn answers afterwards.
+5. The 401-before / success-after upgrade behaviour is exercised, not asserted.
+
+## Landing
+
+Branch from a freshly fetched `origin/dev`, push `--no-verify`, PR into `dev`
+with the screenshot, hosted CI green on the exact head, merge, prove ancestry
+from a fetched `origin/dev`.
+
+## Acceptance
+
+Every goalplan criterion met with fresh evidence, including an explicit statement
+of the one-time sign-in required after upgrade.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/031_phase3_surface_and_land.md b/devlog/_fin/260912_devin_cli_account_login/031_phase3_surface_and_land.md
new file mode 100644
index 00000000000..d6fc0d7bc56
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/031_phase3_surface_and_land.md
@@ -0,0 +1,67 @@
+# 031 — Phase 3 (revised): surface, docs, and landing
+
+**Supersedes `030`.** Same surfaces, new transport, and the proof list changes
+because there is now a real credential to prove with.
+
+**Work-phase:** wp4. **Write set:** `gui/src/pages/providers-shared.ts`,
+`docs-site/` English + 7 locales, `structure/` docs, PR.
+
+## GUI
+
+Accounts rows title through `oauthLabel(id)` → `OAUTH_LABELS[id] ?? id`
+(`gui/src/pages/providers-shared.ts:49-59`, consumed at
+`gui/src/pages/providers-page-utils.ts:21-23`). Without an entry the row reads
+`devin-cli`.
+
+```diff
+ const OAUTH_LABELS: Record = {
++ "devin-cli": "Devin CLI",
+ };
+```
+
+The icon already maps to `devin.svg`. The amber `local` badge disappears on its
+own once `auth` stops being `"local"`; verify rather than assume. Check the rail
+too: `provider-workspace/auth.ts` and `kind.ts` both change branch under
+`oauth`, and the auth surface must not offer an API-key field for a row whose
+credential is imported.
+
+## Docs
+
+The English sentences at `docs-site/src/content/docs/reference/adapters.md:458-459`
+and `docs-site/src/content/docs/guides/providers.md:196` currently describe a
+stdio ACP provider that holds no key. Both halves change: the preset now imports
+the CLI's token and streams over Connect-RPC. The seven locale copies of the
+table row follow (`ko:117`, `ja:119`, `zh-cn:111`, `zh-tw:116`, `fr:130`,
+`ru:128`, `tr:143`).
+
+Say plainly what is imported and what is not: opencodex adopts the
+`windsurf_api_key` the CLI already wrote, and never reads anything else from that
+file.
+
+Also document the surviving ACP escape hatch: an explicit
+`"adapter": "devin-cli"` still drives `devin acp`.
+
+`structure/AGENTS.md:49` binds `src/oauth/` and `src/providers/` changes to
+`runtime.md`, `subagents.md`, `transports/inventory.md` and
+`providers/xai-grok.md`. `structure/adapters/registry.md:16-25` says the two
+Devin adapters have "separate transports, separate credentials" — the credential
+half is now false for the preset and must be corrected.
+
+## Proof against the running service
+
+1. `GET /api/oauth/providers` lists `devin-cli`; `GET /api/provider-presets` does not.
+2. `ocx login devin-cli` imports without opening a browser, against the already
+ signed-in CLI.
+3. `codex exec -m devin-cli/` answers — the `CLIKEY-OK` shape, now through
+ the shipped provider rather than a scratch script.
+4. The model list is the live catalog, and a spot-checked context window matches
+ `ClientModelConfig` field #18 rather than a static table.
+5. Accounts tab screenshot: a **Devin CLI** row beside Devin, no `local` badge.
+ The PR needs it anyway because `gui/` changed.
+
+## Landing
+
+Branch from a freshly fetched `origin/dev`, push `--no-verify`, PR into `dev`
+with the screenshot, hosted CI green on the exact head, merge, prove ancestry from
+a fetched `origin/dev`.
+
diff --git a/devlog/_fin/260912_devin_cli_account_login/090_outcome.md b/devlog/_fin/260912_devin_cli_account_login/090_outcome.md
new file mode 100644
index 00000000000..f1b6cfa9b56
--- /dev/null
+++ b/devlog/_fin/260912_devin_cli_account_login/090_outcome.md
@@ -0,0 +1,62 @@
+# 090 — Outcome
+
+**Merged to `dev` as `b09ef15c6f` (PR #4335).** PR head `2930a0a3bc` is an
+ancestor of a freshly fetched `origin/dev`.
+
+## What shipped
+
+`devin-cli` is an account provider that imports the credential the installed
+Devin CLI already holds and streams over Cognition's Connect-RPC api-server. It
+appears in the dashboard Accounts tab beside Devin, with no `local` badge, and is
+gone from the preset tabs.
+
+Supporting changes that were not obvious at the start: tenant selection became
+provider-scoped, because `resolveDevinApiServer` read a fixed `devin` credential
+slot and would have crossed two accounts onto one host as soon as a second
+provider shared the adapter. That required threading a provider id through
+`AdapterFactoryContext`, `resolveAdapter` and both `core.ts` call sites.
+
+## Evidence
+
+| Check | Result |
+| --- | --- |
+| `GET /api/oauth/providers` | includes `devin-cli` |
+| `GET /api/provider-presets` | excludes it |
+| `ocx login devin-cli` | `{"loggedIn":true,"source":"local-cli"}`, no browser |
+| `GET /v1/models` | 42 `devin-cli/*` rows from live discovery |
+| `codex exec -m devin-cli/swe-2` | `CLOUD-OK` |
+| context windows | `swe-2` 262,000 · Claude/GPT 1,000,000 · Gemini/GLM/Kimi 1,048,576 · Grok 500,000, from field #18 |
+| dashboard | Accounts tab screenshot, Devin CLI row signed in |
+
+Focused tests 128 pass / 0 fail, 18 of them new. GUI 1963 pass / 0 fail.
+`structure:check` and `privacy:scan` pass. Hosted CI green on `2930a0a3bc`.
+
+## What did not work, and what killed it
+
+LOOP-PESSIMIST-01. The first design was wrong and took three audit rounds to
+die. It kept the ACP transport and invented a marker credential so the provider
+could have an account row without holding a token. Reviewers killed it in stages:
+the request path would have 401'd every turn until someone clicked Login; the
+`stdin` design could not deliver the one-time code it also required; the label
+surface was the wrong file.
+
+What actually ended it was not an argument but a measurement. Reading the CLI's
+`credentials.toml` showed an ordinary `devin-session-token`, and feeding it to
+the client already in this tree returned a real answer. Every blocker downstream
+of "there is no credential we may hold" then evaporated, including the two that
+had already been patched around.
+
+The lesson worth carrying: three rounds were spent refining a design whose
+premise nobody had tested, and the test took one minute. When a plan's central
+constraint is an assumption about someone else's system, measure it before
+designing around it.
+
+## Residual
+
+- The seven `two-lock xAI refresh` failures seen while checking this work
+ reproduce on pristine `origin/dev` and are unrelated; they remain open.
+- `identityFromApiKey` returned nothing for this account's token, so the Accounts
+ row shows "signed in" without an address. That is what the CLI itself can say.
+- Multi-account is out of scope: an identity-less credential replaces the active
+ slot rather than adding one.
+
diff --git a/devlog/_fin/260913_subagent_v1_default_encryption_guard/010_roadmap.md b/devlog/_fin/260913_subagent_v1_default_encryption_guard/010_roadmap.md
new file mode 100644
index 00000000000..5e5ac7e883d
--- /dev/null
+++ b/devlog/_fin/260913_subagent_v1_default_encryption_guard/010_roadmap.md
@@ -0,0 +1,63 @@
+# Sub-agent surface: v1 as the install default, with an approval guard on base and v2
+
+## Why this unit exists
+
+The v2 collaboration surface cannot deliver a task from a ChatGPT-native parent to a
+routed child. Codex mints that child task as backend `encrypted_content`, and only the
+ChatGPT backend holds the key, so an external provider receives ciphertext it cannot
+read. OpenCodex already fails closed on it (`unreadable_encrypted_agent_task`) and
+documents the limitation as issue #92, but the product still ships `base` as the install
+default, and `base` pins Sol and Terra to v2. A new user who delegates from Sol to Grok
+therefore meets the failure on their first attempt, with no warning that the mode they
+never chose is the reason.
+
+This unit makes v1 the default, and makes base and v2 a choice the operator confirms
+after reading what it costs.
+
+## Locked decisions
+
+**D1 — an absent `multiAgentMode` key still means base.** Roughly fifteen call sites read
+`config.multiAgentMode ?? "default"`, and the management API deletes the key when the
+operator selects base. Re-pointing that fallback to `"v1"` would make the key's absence
+ambiguous: it could no longer distinguish "never configured" from "explicitly base". So
+what changes is the written default — `getDefaultConfig()` now emits an explicit
+`multiAgentMode: "v1"`, exactly the shape `subagentModels` already uses. The resolver is
+untouched, and the merge that could push a default into an existing document runs only
+on the schema-repair path.
+
+**D2 — an existing operator is asked, not flipped.** Rewriting a stored
+`multiAgentMode: "v2"` during an update would change a setting its owner deliberately
+made, on a machine they are not looking at. Instead the runtime computes an advisory,
+the GUI raises it once, and only the operator's answer writes. That is also what makes
+the two buttons meaningful: 계속하기 keeps the current mode, v1으로 바꾸기 applies v1.
+
+**D3 — the advisory is a version counter, not a boolean.** `multiAgentSurfaceAdvisoryVersion`
+mirrors `subagentModelsVersion`. When the encryption limitation is fixed upstream, or when
+a later release needs to say something different about the same setting, bumping the
+constant re-raises the notice for everyone without adding another key.
+
+**D4 — one dialog component, two triggers.** The approval dialog shown when the operator
+selects base or v2 and the post-update advisory differ only in their heading and body
+text. They offer the same two actions and link to the same guide, so they are one
+component with a `reason` discriminator rather than two that drift apart.
+
+**D5 — the guide is a real published page, not an anchor.** The dialog links to
+`https://opencodex.me/guides/subagent-v1-default/`, a new docs-site page whose job is to
+make the failure legible: an inline SVG showing the same delegation succeeding under v1
+and dying at the provider boundary under v2.
+
+## Work phases
+
+| Phase | Outcome |
+| --- | --- |
+| wp1 | This roadmap, with the decisions above locked before any code moves. |
+| wp2 | Runtime: `getDefaultConfig()` emits v1, the advisory state and its version constant exist, and `GET/PUT /api/v2` carry them. |
+| wp3 | GUI: the approval dialog, both triggers wired, and translations for all nine locales. |
+| wp4 | The docs-site guide and its SVG, plus the link from the dialog and the existing surface guide. |
+| wp5 | Focused tests, typecheck, PR to `dev` with a UI screenshot, exact-head CI, merge. |
+
+## Out of scope
+
+Fixing the encryption limitation itself. `agentTaskRecovery` and `plaintextV2AgentMessages`
+remain experimental and default-off; this unit changes which surface an operator lands
+on by default, not what v2 can carry.
diff --git a/devlog/_fin/260913_subagent_v1_default_encryption_guard/020_runtime_default_and_advisory.md b/devlog/_fin/260913_subagent_v1_default_encryption_guard/020_runtime_default_and_advisory.md
new file mode 100644
index 00000000000..28a4a55e507
--- /dev/null
+++ b/devlog/_fin/260913_subagent_v1_default_encryption_guard/020_runtime_default_and_advisory.md
@@ -0,0 +1,63 @@
+# wp2 — Runtime: v1 default and the advisory state
+
+## What changes
+
+`src/config/multi-agent-surface.ts` (new) owns three things:
+
+- `MULTI_AGENT_SURFACE_ADVISORY_VERSION = 1`.
+- `resolveMultiAgentMode(config)`, the single reader that turns a stored value into
+ `"v1" | "default" | "v2"`. It keeps the existing rule that an absent key means
+ `"default"`; it exists so the advisory and the catalog agree on one answer.
+- `multiAgentSurfaceAdvisoryRequired(config)`, true when the resolved mode is not `"v1"`
+ and the stored `multiAgentSurfaceAdvisoryVersion` is below the constant.
+
+`getDefaultConfig()` gains `multiAgentMode: "v1"` and
+`multiAgentSurfaceAdvisoryVersion: MULTI_AGENT_SURFACE_ADVISORY_VERSION`. A fresh install
+has nothing to advise about, so it starts acknowledged.
+
+`src/types/config.ts` gains the optional `multiAgentSurfaceAdvisoryVersion?: number`, and
+`configSchema` in `src/config.ts` accepts it as a positive integer with `.catch(undefined)`
+so a hand-edited value cannot fail the parse.
+
+## Management API
+
+`GET /api/v2` adds one response-only object:
+
+```json
+{
+ "multiAgentSurfaceAdvisory": {
+ "required": true,
+ "mode": "v2",
+ "recommended": "v1",
+ "version": 1,
+ "docsUrl": "https://opencodex.me/guides/subagent-v1-default/"
+ }
+}
+```
+
+`PUT /api/v2` accepts `multiAgentSurfaceAdvisoryAcknowledged: true`, which stores the
+current constant and is idempotent. It composes with an existing `multiAgentMode` write in
+the same body, because `v1으로 바꾸기` sends both in one request and must not leave the
+advisory raised if the mode write succeeded.
+
+It stays a boolean in, number out. A client that could post an arbitrary version could
+silence a future advisory it has never seen.
+
+## What deliberately does not change
+
+The catalog stamping in `src/codex/catalog/sync.ts`, the `keepNativeChatGptOnV1` hybrid,
+and every `?? "default"` fallback. This phase moves the written default and adds one
+piece of state; it does not renegotiate what the three modes mean.
+
+## Tests
+
+New tests go into existing files. `tests/test-layout.test.ts` requires every new test file to
+be registered in both `scripts/test-layout/layout.json` and
+`tests/fixtures/test-layout-expected.json`, and a file named after this route does not resolve
+through the regex seeds. Extending files that already own these subsystems is the smaller
+change and keeps the guard quiet.
+
+`tests/server/config.test.ts` — a fresh config resolves to v1 and starts acknowledged.
+`tests/codex-integration/codex-v2-gate.test.ts`, in its existing management-API parity
+describe block — the advisory is required for a v2 config and for a config with no key, not
+required after acknowledgement, and a combined mode plus acknowledgement write persists both.
diff --git a/devlog/_fin/260913_subagent_v1_default_encryption_guard/030_gui_approval_dialog.md b/devlog/_fin/260913_subagent_v1_default_encryption_guard/030_gui_approval_dialog.md
new file mode 100644
index 00000000000..71c36d74353
--- /dev/null
+++ b/devlog/_fin/260913_subagent_v1_default_encryption_guard/030_gui_approval_dialog.md
@@ -0,0 +1,45 @@
+# wp3 — GUI: the approval dialog and its two triggers
+
+## Component
+
+`gui/src/components/SubagentSurfaceWarningModal.tsx`, modelled on
+`OAuthTosWarningModal.tsx`: a native `