Loose notes on things spitballed but not implemented yet. Add to this file as ideas come up; don't bother with formal design until something is ready to actually pick up. Each entry: what / why-it-matters / where to start.
The historical "★ v3.0.0-beta.N — SHIPPED" sections below are kept as a
record of how we got to 3.0.0. They are NOT ongoing work; everything in
those sections shipped on 2026-05-26 as part of the v3.0.0 stable
release. See CHANGELOG.md for the cleaned summary or
docs/SESSION_LOG_2026-05-26_v3.0.0_release.md for the full
play-by-play.
Real work to pick up next. Ordered roughly by impact.
-
Per-provider API key entry — the multi-model catalog has slots for OpenAI / Gemini / OpenRouter models but no UI to enter their API keys. Extend
AuthPanelbeyond its current Anthropic-only shape. Each provider stores its key encrypted via ElectronsafeStorage(same pattern as the GitHub PAT). ~3-4 days. Probably the highest- leverage next move because it unlocks the API half of the catalog for real use. -
macOS code signing + notarization. Without it users see "App is damaged" Gatekeeper warning on first launch. Requires Apple Developer Program ($99/yr) +
osxSignconfig in electron-builder keyed to env vars set in CI (APPLE_ID,APPLE_APP_SPECIFIC_PASSWORD,APPLE_TEAM_ID). Workflow change in.github/workflows/release.yml. -
Model comparison view. Run the same prompt against multiple models in parallel, show results side-by-side with a diff highlight. Requires parallel pane management + a synced-input mode. UI-heavy work; own push.
-
Embedding-RAG over past sessions. The catalog already includes embedding models (Qwen3 Embedding 0.6B, BGE-M3, Nomic). A real RAG pipeline needs: vault index → chunking strategy → vector store → query UI. ~1-2 weeks.
-
Per-loaded-model VRAM tracking. Resource panel shows RAM by bucket but not VRAM per model. Requires vendor GPU SDKs (NVML for NVIDIA, Metal Performance Shaders Counters for Apple Silicon, ROCm SMI for AMD). Or query Ollama's
/api/psHTTP endpoint which reports loaded-model sizes — simpler but only covers Ollama-managed models. -
Auto-updater test coverage. The beta-skip gate (Gate 4 in
UpdaterService.start()) was added blind — write a unit test for the/-beta\./iregex so a future version naming change (e.g.,3.1.0-rc.1) doesn't accidentally re-enable the updater on a pre-release. -
macOS / Linux first-launch onboarding parity with Windows. The v3 NSIS bootstrap handles Node + Claude CLI install at install time on Windows. macOS / Linux defer to the first-launch CliAuthOnboarding modal. The modal works but UX-wise the Windows install feels smoother. Worth investigating a deb/rpm postinstall hook + a DMG bundled-Node approach to close the gap.
-
"Open externally" wiring for FileTreePanel. Right now the panel's "Open externally" button just copies the path + shows an alert (kept as deferred in MULTI_MODEL.md). Could add
shell.openPath()via a new IPC with the same allowlist pattern the GitHub/models external IPCs use. -
Squirrel pipeline removal. README still mentions the legacy
npm run make/npm run publish(forge + Squirrel) as an escape hatch. Now that v3 is shipped and stable, audit whether any user is still on that path; if not, remove the scripts + forge config.
The sections below were the running work-in-progress notes for the beta.1 → beta.2 → beta.3 → 3.0.0 push. Everything in them shipped. Kept for cross-referencing in case a regression appears that traces back to one of these changes.
Installer: Claude-Code-Studio-3.0.0-beta.3-Windows.exe (91 MB) at
C:\Users\extra\OneDrive\Desktop\claude-code-studio-installers\.
Done in this push:
- Resource monitoring split into claude / models / ollama-daemon
buckets —
ResourceMonitorrebuilt with O(n) process-tree walk; PtyRegistry now tracks per-pane category at spawn time; ResourcePanel UI renders the new buckets when present. Backward-compatible. - Cost rates bumped to May 2026 Anthropic pricing (Haiku $1/$5). Disclaimer expanded re local models being free + uncounted.
- GitHub error classification in GitHubPanel.extractError — 401 / 403 / 404 / network all get friendly one-line messages instead of raw stack traces. Rate-limit messages parse the reset timestamp when present and say "Resets at HH:MM."
- File directory navigator — new
Filessidebar entry between Resources and Cost. Lazy tree (1 dir level per IPC call), path- traversal guarded, max 2000 entries per call. Recent-projects list with persistence at<userData>/recent-projects.json. --dangerously-skip-permissionstoggle in Settings → Claude CLI. Persisted at<userData>/cli-flags.json; PtyManager reads at spawn time and prepends only when noopts.command(i.e., bundled Claude CLI). Model PTYs never affected.- Danger Zone in Settings — Reset user data (wipes 18 named JSON/ JSONL files; leaves Chromium profile intact) + Uninstall Claude Code Studio (spawns NSIS uninstaller, quits app). Both gated by confirmation.
- NSIS uninstaller now MB_YESNO prompts to also remove the user- data JSON files (default No so a planned reinstall keeps settings). Lists every file explicitly.
- Models panel running list survives panel re-mount via new
MODELS_LIST_RUNNINGIPC (rehydrates fromPtyRegistry.listModelPanes()on mount). - EmbeddedTerminal warns when paneId is stale (yellow placeholder
after 1.5s if
listRunning()doesn't return the paneId). - Auto-updater now skips beta builds entirely (Gate 4 in
UpdaterService.start()checks/-beta\./i.test(app.getVersion())). Fixes the v1.0.0latest.yml404 stack trace. - Status bar shows current git branch + dirty dot, polled every 30s + on focus.
- About row in Settings reads from
app:versionIPC (was hardcoded "2.0.0").
Deferred (each its own ≥1 week push):
- Per-provider API key entry (extend AuthPanel)
- Model comparison view (parallel pane + synced input + diff)
- Embedding-RAG over past sessions
- VRAM tracking per loaded model (requires vendor GPU SDKs)
Both items in this section (easier uninstall + auto-updater 404 fix) landed in beta.3 and then v3.0.0 stable. Kept here only so the trail from the beta.2 testing-screenshot → beta.3 fix is searchable.
- Easier uninstall → done. Danger Zone in Settings (Reset + Uninstall). Cross-platform uninstall flow (Windows NSIS / macOS Finder / Linux pkg-mgr). NSIS uninstaller prompts to also remove userData.
- Auto-updater 404 on beta builds → done. Gate 4 in
UpdaterService.start()checks/-beta\./i.test(app.getVersion())and short-circuits to disabled.
After packaging beta.1 and trying it on a real machine, three concrete bugs surfaced that needed immediate fix before further testing:
-
Version drift across the UI —
TitleBar.tsxwas hardcoded tov1.0.0(predating v2),StatusBar.tsxwas hardcoded tov2.0.0, and the installer reported3.0.0-beta.1. Three different versions on screen. Fixed by adding anapp:versionIPC backed byapp.getVersion()and rendering it from both labels. -
"Sign in to Claude" sent
claude logininto a running Claude session — the embedded PTY auto-spawns Claude, so the active pane is always a running Claude session, never a bare shell. The button wiredsendToActivePane('claude login'), which Claude interpreted as chat text and replied "I notice you typed claude login as a message rather than as a shell command." Fixed by sending Claude's in-session/loginslash command instead. -
Auth modal popped on transient
claude doctorfailures —CliService.getStatusreportedauthenticated: falseon ANY non-zero doctor exit (network blip, telemetry timeout, anything), which then popped the onboarding modal even when the user was clearly authenticated. Fixed by only flipping toauthenticated: falsewhen stderr explicitly mentions auth phrases; everything else defers to Claude itself to prompt for login as needed.
Plus the Ollama-bundle-in-installer removal — see the MULTI_MODEL.md update for that one.
All four fixes shipped on commit 16f3701 on feature/multi-model- scaffold (testing remote), packaged as Claude-Code-Studio-3.0.0- beta.2-Windows.exe.
Status: Built. The full-scope catalog + Ollama bootstrap + hardware
detection + recommendation engine landed on feature/multi-model-scaffold
on 2026-05-26. See docs/MULTI_MODEL.md for the design + research,
and _backups/2026-05-26-pre-fullscope/ for the pre-change snapshot.
Shipped this push:
- 33-model curated catalog seeded into
ModelRegistry(covers general chat, frontend, backend, polyglot code, reasoning, vision, long context, edge, embedding — across all 5 hardware tiers). OllamaServicewrapper: detect, list installed, pull with progress, cancel, delete.HardwareDetection: RAM/CPU/GPU probe →toaster/low/mid/high/workstationtier classification.ProjectLanguageDetect: cwd →frontend/backend/data/systems/ etc. via package.json / pyproject.toml / Cargo.toml / etc.ModelRegistry.recommend(): scores models against hardware + project, surfaces top picks with reason strings.PtyManager+PtyRegistrygeneralized: now accept arbitrarycommand+argsso any model can spawn into a pane.- NSIS installer bootstrap: detects Ollama at well-known install paths
- PATH; if absent, downloads OllamaSetup.exe via
curl.exeand runs silently with/verysilent /norestart.
- PATH; if absent, downloads OllamaSetup.exe via
ModelsPanelUI: hardware-tier banner, recommendations row, role + tier + search filters, per-model card with full metadata, pull progress bar, license disclosure, launch + kill flow.
Still deferred (documented in MULTI_MODEL.md):
- In-panel xterm output viewer for launched models (today: PTY runs in background, output reaches the renderer via existing TERMINAL_DATA but no Models-panel-local viewer is mounted)
- Pop-out windows per model
- "Add custom model" form (catalog-side CRUD is wired; UI is not)
- Per-provider API-key entry UI (extend AuthPanel beyond Anthropic)
- macOS + Linux installer Ollama bootstrap (Windows shipped this push; POSIX install-OllamaSetup logic follows the same curl pattern)
Original brainstorm (preserved for context):
Today Studio has one terminal driving one CLI (Anthropic's claude).
Expand it to a multi-model surface where the user can run any combination
of remote-API-backed and locally-hosted models in parallel, with the
same GUI shell (terminal panes, resource monitor, etc.) around each.
-
API Models (current behaviour generalised)
- Talk to a remote API for inference. Lightweight on the user's machine — just network + render.
- Each model is a separate CLI/process spawned in its own pane.
- Tabs/sub-panels per model: Claude (Anthropic), GPT-x (OpenAI), Gemini, etc. — whichever the user adds.
- Auth per provider stored encrypted via the existing
safeStoragepattern.
-
Local Models
- Run inference on the user's hardware (GPU/CPU). No network call for the actual model — though the binary itself is downloaded once.
- Source for binaries: a curated registry/database (could be our own JSON manifest hosted on GitHub or a backend) that lists model + size + URL + hash + runtime requirements.
- User picks a model from a catalog → Studio downloads to local
storage (similar to the Phase 4 Node bootstrap pattern — fetch,
verify SHA, extract) → spawns the model via a runtime
(
llama.cpp,ollama,vllm, custom — depends on format). - First-use bootstrap dialog matching the existing Claude CLI onboarding UX: "This model is X GB. Download now?"
- Each model lives in its own pane (existing split-panes infra works).
- Resource monitor needs per-pane breakdown (already noted as C4 in earlier polish list) — multi-model makes that essential.
- Cap concurrent local models by available RAM/VRAM. Reuse the PtyRegistry pattern with a resource-aware admission control.
- Right now everything's a pane inside one window. For multi-model the user will want to pop a model out to a separate window (separate monitor / side-by-side workflow).
- Electron supports multiple BrowserWindows; need a window manager service that tracks which panes are in which window, persists across restart, and forwards IPC between windows.
PtyRegistry→ generalize beyond "claude" to any model CLI; addmodelmetadata to each pane.- New
ModelRegistryservice: catalog of API + Local models, per-model config (auth, runtime, install path). - New
ModelDownloadService(mirror Phase 4 bootstrap): handles local model fetch + verify + extract. - Renderer: new "Models" panel with two top-level tabs (API / Local), catalog browser, per-model launch button.
- Settings panel: per-provider auth (extend the existing AuthPanel pattern that's currently Claude-specific).
- Window manager service in main process for pop-out support.
- Catalog hosting: GitHub-Pages-hosted JSON? Cloudflare Worker? Own schema vs. piggyback on Hugging Face / Ollama registry?
- Local-model runtime: bundle our own (llama.cpp prebuild for each OS) or shell out to an existing tool (ollama, lm-studio)?
- License / TOS exposure: local models often have non-commercial licenses; surface that prominently in the catalog UI.
- GPU detection: nvidia-smi / Metal / Vulkan probe → only show models the user's hardware can run.
- Multi-API tabs (just adding GPT/Gemini providers): ~1 week
- Local-model catalog + download + run: ~3-4 weeks (includes building the runtime wrapper)
- Pop-out windows: ~1 week (Electron multi-window is fiddly but well- documented)
- Total v3.0 work: ~6-8 weeks if all built sequentially; faster with parallel agents on independent pieces.
Status: 4 of 9 phases shipped on feature/bootstrap-installer. See
INSTALLER_REDESIGN.md and
MIGRATING_FROM_V1.md for the design + upgrade
path. Per-phase journal at
journal/config/INSTALLER_REDESIGN.lmm.md.
Phases complete: 1 (design) · 2 (forge→builder hybrid) · 3 (PtyManager runtime path) · 4 (NSIS bootstrap macros) · 8 (this docs update).
Phases remaining for v1.1.0-rc1:
- Phase 5: branded NSIS UI assets (placeholders OK for rc1; real art before final)
- Phase 6: first-launch CLI auth onboarding modal (
claude doctordetection, one-clickclaude login, "install CLI now" soft-fail recovery). Hard blocker for rc1 per Phase 4 red-team M5. - Phase 7:
update-electron-app→electron-updatermigration - Phase 9: integrated cross-feature red-team + clean-VM test, tag rc1
Phase 4 shipped online installer only. The offline variant (Setup.exe
bundles Node + the CLI tarball inside the installer payload, no network
needed during install) is deferred to a Phase 4b based on user-reported
install-failure rate. Trigger to implement: first user report of install
failure due to network issues. Estimate: +130 MB on the release asset
size; same NSIS script flow but reads files from $PLUGINSDIR\ instead of
downloading. See INSTALLER_REDESIGN.md Phase 4 red-team H1 for the full
rationale.
Status: Phase 5 shipped with a local-stub auth backend that
implements the HTTP contract but stores everything in
<userData>/auth-users.json. The contract is real and frozen
(documented in src/main/auth-service.ts:14-26); only the server is
missing. Likewise, Phase 6 vault sync uses GitHub as its data store,
which works but isn't a "real" database for things like cross-account
analytics, leaderboards, shared snippets, etc.
What's needed:
- Pick a backend platform. Original plan (per
HANDOFF.mdhistory) was Cloudflare Worker because it's free-tier-friendly and the AuthService HTTP contract was designed against it. Alternatives: Supabase, Pocketbase, a small self-hosted Express server. Whatever ships needs the four endpoints insrc/main/auth-service.ts:POST /auth/register,POST /auth/login,POST /auth/logout,GET/PUT /settings. - Pick a data store. Cloudflare D1 (SQLite) or KV both work for the
current minimal schema (users + per-user
SyncedSettings). Postgres if you want real relational queries. - Password hashing: backend should use scrypt or argon2id, not bcrypt. Salt + per-user.
- Session tokens: opaque 32-byte random strings, store hashed in DB,
return raw to client, expire on TTL. The client already encrypts
the token at rest via
safeStorage. - Once a real backend exists, flip the UpdaterChannel + Sync settings
to point at it; the per-user
auth-synced-settings.<uuid>.jsonlocal-stub keying (Phase 5 C1 fix) is only for local-stub mode, HTTP mode delegates to the backend.
Schema sketch (minimal):
users (id uuid pk, email text unique, password_hash text, salt text, created_at)
sessions (token_hash text pk, user_id uuid fk, issued_at, expires_at)
synced_settings (user_id uuid pk, theme text, lmm_enabled bool, lmm_variant text, updated_at)Future schema growth ideas:
snippets_synced— let users share their Phase 7a snippet library across devices (currently device-local only)cost_history_synced— multi-device cost aggregation (Phase 7e)vault_index— metadata about pushed vaults so the user can see "I have N vaults across M devices" without enumerating GitHub reposfeedback/crash_reports— if you ever turn on telemetry, there's an obvious backend to send it to
Decisions deferred:
- Whether to make signup invite-only or open
- Whether to support social login (GitHub OAuth, Google OAuth)
- Rate limiting strategy (per-IP, per-account, per-endpoint)
- Recovery flow if user loses password (email-based reset requires picking an email sender — Resend, Postmark, SES…)
Relevant existing files:
src/main/auth-service.ts— HTTP contractsrc/shared/types.ts—AuthBackend,AuthCredentials,SyncedSettingssrc/renderer/components/auth/AuthPanel.tsx— backend switcher UIdocs/security-reviews/SECURITY_REVIEW_PHASE5.md— auth-side threat model
Status: v1.0 ships Windows-only via Squirrel.Windows. The forge
config has MakerZIP({}, ['darwin']) which would produce a darwin zip
on a Mac build host but the build machine has to be macOS. Linux is
not supported at all yet (no maker, no plugin-fuses Linux variant).
What's needed:
- A macOS build host. Apple won't let you cross-compile signed Mac
builds from Windows — you need either a Mac, a Mac mini cloud
rental (MacStadium / MacInCloud), or GitHub Actions
runs-on: macos-latest. - Add
@electron-forge/maker-dmgfor a proper .dmg installer. - Add
@electron-forge/maker-zip(already present) so the auto-updater has an artifact format it can read. - Code signing. Required for distribution. Costs $99/year (Apple Developer Program). Without it, users get the "App is damaged or can't be opened" gatekeeper warning. With it, the app is trusted on first launch.
- Notarization. Apple-mandated as of macOS 10.15+. After signing,
upload the .app to Apple's notarization service via
notarytool(built into Xcode CLI). Notarization stapling makes the app launch without network on the user's machine. - Update
forge.config.tspackagerConfigwithosxSignandosxNotarizeoptions keyed to env varsAPPLE_ID,APPLE_APP_SPECIFIC_PASSWORD,APPLE_TEAM_ID. - The auto-updater (
update-electron-app) works on Mac without changes — same GitHub Releases backend, just different artifact shape that Squirrel.Mac understands. - The tray icon (Phase 7d) needs a macOS template image variant
(black PNG,
@2xretina). Without it the tray shows the Windows colored icon which looks wrong against the Mac menu bar.
Native module rebuild:
- node-pty needs to rebuild against the macOS Electron ABI. The
scripts/patch-node-pty.jspostinstall script is currently Windows-specific (patcheswinpty.gyp+ Spectre mitigation). Skip the patches onprocess.platform === 'darwin'.
Status: harder than macOS because Squirrel doesn't support Linux and there's no single installer format.
Options:
@electron-forge/maker-debfor Debian/Ubuntu@electron-forge/maker-rpmfor Fedora/RHEL@electron-forge/maker-appimage(community) for distro-agnostic AppImage@electron-forge/maker-flatpakfor Flatpak / Flathub
Auto-updater: update-electron-app doesn't support Linux out of
the box. Either skip auto-update on Linux (most distros have their
own package manager) or wire a custom updater that downloads the
appropriate format from GitHub Releases.
node-pty on Linux: uses POSIX pty (forkpty) so no winpty/conpty
hassle, but does require glibc compatible with the build host. The
prebuilt binary in node_modules/node-pty/prebuilds/linux-x64/
should "just work" if Electron's Node version matches.
Tray icon: Linux tray support is wildly inconsistent across DEs (GNOME requires an extension, KDE works natively, etc). Default to disabled-on-Linux for the tray feature.
Reported: 2026-05-22 (user, post-v1.0 install)
Severity: Medium (cosmetic flicker; doesn't crash)
Status: 2026-05-23 — MOVED TO PR on branch fix/terminal-resize-loop.
Two distinct flicker mechanisms found and fixed in TerminalPanel.tsx:
- Self-sustaining loop —
fit()+resize-IPC ran on every ResizeObserver tick. Now gated behind aproposeDimensions()equality check (fitIfChanged()): a converged grid is a no-op, so the fit→ResizeObserver→fit feedback can't sustain. - Panel-open ratchet (the one the user actually saw) — the pane
flex containers lacked
min-width: 0, so their defaultmin-width: autokept them as wide as the old xterm content when a 320px panel opened. The container only caught up one column per fit(), crawling to the right size over ~1.5s. AddingminWidth/minHeight: 0lets the box shrink to its allotted size in the same layout pass, so xterm fits once and settles. Verified on Linux (real app via CDP): panel-open now settles the grid in one ~66ms step (was 30+ steps over 1.6s); forced 90px squeeze settles to a single stable width. NOTE: original report was a Windows install — re-confirm there before closing the issue.
Symptom: With a sidebar panel open (Resources / Compact / GitHub / etc.) AND the window shrunk such that the terminal area is narrower than the panel's preferred width, the terminal starts auto-adjusting its size in a loop. Visually you see the terminal rapidly flashing between two sizes.
Likely root cause:
src/renderer/components/terminal/TerminalPanel.tsxuses aResizeObserverwith a 50ms debounce (set in Phase 1).- The right-panel container in
src/renderer/App.tsxhas a fixedwidth: 320, minWidth: 320. - When the parent window shrinks below
320 + xterm-min-cols, the flex layout has no good answer. xterm fits to its container, which changes the container size (because xterm uses tabular cells that round to integer column counts), which triggers ResizeObserver again, which re-fits, which re-changes the size... - Phase 7c (split panes) introduced react-resizable-panels which may
compound this — multiple terminal panes each running their own
fit()cycle.
Where to look:
src/renderer/components/terminal/TerminalPanel.tsx— the ResizeObserver +fit.fit()call sitesrc/renderer/App.tsx— the right-panelwidth: 320, minWidth: 320rule (it's flex-shrink: 0 implicitly)src/renderer/components/terminal/SplitLayout.tsx(Phase 7c) — each pane has its own resize observer
Fix ideas:
- Increase debounce from 50ms to ~150ms — makes the loop converge faster but doesn't fix the root cause
- Detect "no-change-in-cols-or-rows" before calling
electronAPI.terminal.resize()and skip — prevents the IPC echo from re-triggering - Compare current xterm cols/rows against the proposed fit result; only commit if different — same idea, different layer
- Set a minimum window-content-width on the BrowserWindow so the
terminal can't be squeezed below xterm's
MINIMUM_COLS(usually 20) - Move the right panel to overlay-mode (absolute-positioned) when the window is narrow, instead of letting it eat from terminal width — fundamentally avoids the squeeze
Reproduction steps:
- Open the app
- Click any sidebar panel (e.g. Resources)
- Drag the window's right edge inward to shrink it
- As the terminal area gets narrower than a certain threshold, the flash/loop starts
Reported: 2026-05-23 (Linux dev verification) Severity: Low (cosmetic; Linux is dev-only today — the shipped Windows build may be unaffected, see below)
Symptom: In the Resource Monitor panel, the Claude memory readout shows "Claude NaN%" and the "Claude Memory" card shows "NaN MB". Claude CPU reads fine (0%+), and the System CPU/RAM gauges are correct — only the per-process Claude memory is NaN. "Claude Processes: 1" is correct, so the process IS being found; the value is just missing.
Likely root cause: In src/main/resource-monitor.ts, the per-process
RAM is summed as claudeRam += proc.mem_rss. On Linux,
systeminformation's si.processes().list[].mem_rss comes back
undefined for the matched process, so claudeRam becomes
0 + undefined = NaN, which then propagates into both ramPercent and
ramMB in the emitted snapshot. CPU is unaffected because proc.cpu is
populated. On Windows the field is presumably populated, so the shipped
build likely looks correct — needs confirmation on Windows.
Where to look:
src/main/resource-monitor.ts:73—claudeRam += proc.mem_rsssrc/main/resource-monitor.ts:100-101—ramPercent/ramMBderive fromclaudeRam, so a single NaN poisons bothgetProcessTree()(same file) — its element type declaresmem_rss: number, but the runtime value can beundefinedon Linux
Fix ideas:
- Coalesce at the source:
claudeRam += proc.mem_rss || 0(and similarly guardproc.cpu). Cheapest fix; kills the NaN regardless of platform. - Guard the snapshot: if
claudeRamisn't finite, surface0/ "—" rather than lettingNaNreach the UI. - Verify the units while you're in there.
ramMBdivides by1024 * 1024(assumes bytes), butsysteminformationdocumentsmem_rssin KB on some platforms/versions — if so the MB figure is off by ~1024× even once it's no longer NaN. Confirm per-platform before trusting the number.
Reproduction steps:
- Run the app on Linux (
electron-forge start) - Let a Claude pane spawn, then open the Resources panel
- Claude memory row shows "NaN%"; "Claude Memory" card shows "NaN MB"
When you come back to one of these:
- Read the section
- If you decide to do it, move the section out to a real plan / PR
- If you change your mind, leave a note here explaining why
- New ideas: append a new top-level section. Keep entries short — one or two paragraphs of "why" + a list of relevant existing files is usually all that's needed to remember context later