{"type":"meta","schema":"session-state/v1","slug":"arch-deepening","topic":"Architecture deepening 15-25 (tray/account subsystem) via run-plan-codex","created":"2026-07-02","exec_mode":"run-plan-codex","scheduler":"dag-parallel","land_mode":"pr","base_branch":"save-local-main-before-remote-dashboard-20260701","gate0_cmd":"python3 -m pytest -q","codex_impl_model":"gpt-5.4-mini","codex_impl_effort":"high","codex_review_model":"gpt-5.4","codex_review_effort":"low","status_enum":["BLOCKED","PENDING","WORKING","WRITTEN","REVIEWED","COMMITTED"],"truth_rule":"this file is ground truth for INTENT (goals/directions/task list); git is ground truth for DONE-NESS — reconcile statuses against git, never trust a COMMITTED line with no commit","refresh_how":"native Read of THIS file — NEVER ctx_execute/ctx_search","refresh_what":"meta + every goal/methodology/intent/direction/gated record + all WORKING and BLOCKED tasks","refresh_when":["wave boundary","any task status change","after a compaction system-reminder","before calling advisor"],"update_rule":"on status change, rewrite that task's single line in place; append new tasks/session_memory as new lines","report_rule":"at session end emit brief: done / blocked / deferred / canceled — NO next action","executor_notes":"run-plan-codex reads: meta.base_branch, meta.gate0_cmd, meta.codex_impl_model/effort, meta.codex_review_model/effort; per task t.wave, t.files, t.blocked_by, t.seat. It IGNORES t.deps and meta.scheduler — cross-wave ordering is enforced purely by t.wave. base_branch is a fail-closed sentinel: setup HALTS until it is set to the reconciled ref."}
{"type":"goal","text":"Turn shallow God-object-bound modules (Indicator, account_registry, routing) into deep modules with stable seams so the tray/account subsystem is testable through interfaces and AI-navigable; fix the fail-open routing bug and the divergent health-freshness bug as part of the relevant deepenings. Full audit scope 15-25."}
{"type":"methodology","text":"Contract-first, behavior-preserving extraction. Each wave moves one concern behind a new non-GTK, unit-testable seam and re-points callers + tests, no user-visible change except the two explicit bug fixes. Refactors that break private-method-coupled tests MUST re-target the new seam in the same task, never weaken assertions to pass gate0. Full-suite gate0 (python3 -m pytest -q) per task = cumulative regression net."}
{"type":"intent","text":"The refactor is worth doing only because it makes the subsystem testable and navigable; a green gate0 achieved by relaxing/deleting tests defeats the entire point. The codex review leg exists to reject that failure mode. Best-practice deepening, not hacky line-shuffling."}
{"type":"direction","verbatim":"Reconcile with origin/main first"}
{"type":"direction","verbatim":"i will run the session in another sesion, just prepare the plan"}
{"type":"direction","verbatim":"Everything deepening 15-25"}
{"type":"deferred","text":"Do NOT invoke run-plan-codex from this session","reason":"User will run the session elsewhere; this session only prepares the plan artifacts."}
{"type":"gated","id":"g1","category":"input","needs":"origin/main <-> local reconcile completed AND meta.base_branch set to the reconciled ref (currently sentinel RECONCILE-SET-BASE-BRANCH). Working tree clean on that base; uncommitted edits to indicator.py/cdx.py/tests committed into it. Re-verify per-task files after merge.","why":"Audited targets (830-line indicator.py, account_registry.py shared-state ~185-319) live only on save-local-main-before-remote-dashboard-20260701, NOT on origin/main (diverged, 994-line indicator lineage). run-plan-codex branches from a committed ref and never sees uncommitted work; building on the wrong lineage would make W4 extract code that isn't there.","blast_radius":"entire run builds on wrong tree if base_branch left unresolved/wrong","options":["proceed","abort"],"default":null,"status":"RESOLVED","answer":"proceed: base_branch=save-local-main-before-remote-dashboard-20260701 (no merge from origin/main; audited targets only exist on local branch per this gate's own rationale). Pending edits (cdx.py, tests) committed at 0f26ba6 before setup. NOTE: user question timed out with no response — agent decided on timeout, not a user confirmation. This also means origin/main's 5 commits were NOT reconciled in, contradicting the earlier direction \"Reconcile with origin/main first\" (line 5) — needs user sign-off.","resolved_by":"agent-on-timeout","source":"author","binds_meta":"base_branch"}
{"type":"anchor","path":"docs/specs/2026-07-02-arch-deepening-design.md","what":"spec"}
{"type":"anchor","path":"docs/architecture-gap-audit-2026-07-02.md","what":"source audit (gaps 15-25)"}
{"type":"task","id":"w1.t1","wave":1,"desc":"Move embedded tests out of production modules; make plain pytest collect the full suite (audit #26/#27/#31).","status":"COMMITTED","deps":[],"blocked_by":[],"requires_decision":null,"files":["pytest.ini","device_auth.py","device_auth_dialog.py","tests/test_device_auth.py","tests/test_device_auth_dialog.py"],"blocker":null}
{"type":"task","id":"w1.t2","wave":1,"desc":"Add CONTEXT.md domain vocabulary + docs/adr/0001 (record-architecture-decisions) (audit #32/#33). Disjoint from w1.t1 -> parallel.","status":"COMMITTED","deps":[],"blocked_by":[],"requires_decision":null,"files":["CONTEXT.md","docs/adr/0001-record-architecture-decisions.md"],"blocker":null}
{"type":"task","id":"w2.t1","wave":2,"desc":"HealthSnapshotStore: single owner of health-cache read/write/freshness/atomicity; absorb the 3 divergent freshness rules into one policy (audit #16 + freshness bug). Callers become thin.","status":"COMMITTED","deps":[],"blocked_by":[],"requires_decision":null,"files":["health_store.py","health_client.py","cdx.py","cld.py","indicator.py","tests/test_health_store.py","tests/test_health_client.py","tests/test_cdx.py","tests/test_indicator.py"],"blocker":null}
{"type":"task","id":"w2.t2","wave":2,"desc":"Fail-open routing fix: unknown-to-registry slug not selectable via optional known_slugs param (default None = current behavior); registry-checked seed_routing_rules (audit #23 + #10). File-local (no cdx/cld edits) -> disjoint from w2.t1, parallel.","status":"COMMITTED","deps":[],"blocked_by":[],"requires_decision":null,"files":["routing_resolver.py","systray_codex_switcher.py","tests/test_routing_resolver.py","tests/test_systray_codex_switcher.py"],"blocker":null}
{"type":"task","id":"w3.t1","wave":3,"desc":"Tool-qualified account identity: AccountRef(tool, slug), neutral account_home field, tool-qualified tray keys so shared slugs across Codex/Claude don't collide (audit #19 + #8).","status":"COMMITTED","deps":["w2.t1"],"blocked_by":[],"requires_decision":null,"files":["account_registry.py","indicator.py","cdx.py","cld.py","tests/test_account_registry.py","tests/test_indicator.py"],"blocker":null}
{"type":"task","id":"w4.t1","wave":4,"desc":"Extract SharedCodexState (Codex-specific merge/symlink engine) out of the generic AccountRegistry (audit #18). Target code exists only on the local lineage -> depends on reconcile (g1).","status":"COMMITTED","deps":["w3.t1"],"blocked_by":[],"requires_decision":null,"files":["shared_codex_state.py","account_registry.py","tests/test_shared_codex_state.py","tests/test_account_registry.py"],"blocker":null}
{"type":"task","id":"w5.t1","wave":5,"desc":"CommandRouter owns resolve->exec; ToolAdapter/CodexAdapter/ClaudeAdapter carry per-tool differences; kill the cdx:->cld: string leak; wire real known_slugs into RoutingResolver here (audit #17).","status":"COMMITTED","deps":["w2.t1","w2.t2","w3.t1"],"blocked_by":[],"requires_decision":null,"files":["command_router.py","cdx.py","cld.py","tests/test_command_router.py","tests/test_cdx.py","tests/test_cld.py"],"blocker":null}
{"type":"task","id":"w6.t1","wave":6,"desc":"Subprocess protocol adapters: CodexAppServer (JSON-RPC rate limits) + DeviceAuthProtocol (stream parse) as explicit modules with typed contracts (audit #22). Kept on LOCALITY grounds only, not pluggable.","status":"COMMITTED","deps":[],"blocked_by":[],"requires_decision":null,"files":["codex_appserver.py","device_auth_protocol.py","health_client.py","device_auth.py","tests/test_codex_appserver.py","tests/test_device_auth.py"],"blocker":null}
{"type":"task","id":"w7.t1","wave":7,"desc":"Scheduler seam: concurrency policy (daemon thread spawn, GLib idle dispatch, refresh-generation/pending bookkeeping) leaves Indicator (audit #25). First of the strictly-sequential indicator.py breakup.","status":"COMMITTED","deps":["w2.t1"],"blocked_by":[],"requires_decision":null,"files":["scheduler.py","indicator.py","tests/test_scheduler.py","tests/test_indicator.py"],"blocker":null}
{"type":"task","id":"w8.t1","wave":8,"desc":"Tray view-model: build_tray_model(...) -> TrayModel derives title + menu-item state as pure data; GTK only renders (audit #24).","status":"COMMITTED","deps":["w7.t1"],"blocked_by":[],"requires_decision":null,"files":["tray_model.py","indicator.py","tests/test_tray_model.py","tests/test_indicator.py"],"blocker":null}
{"type":"task","id":"w9.t1","wave":9,"desc":"Device-auth operation module: repair/add orchestration (process start, prompt read, commit/rollback, collision) emits FlowEvent iterators; tray only shows dialogs + renders events (audit #20).","status":"COMMITTED","deps":["w8.t1"],"blocked_by":[],"requires_decision":null,"files":["device_auth_operation.py","indicator.py","device_auth.py","tests/test_device_auth_operation.py","tests/test_indicator.py"],"blocker":null}
{"type":"task","id":"w10.t1","wave":10,"desc":"Indicator becomes a thin GTK adapter wiring Scheduler + TrayModel + DeviceAuthOperation + HealthSnapshotStore; test_indicator.py shrinks to public-surface wiring (audit #15).","status":"COMMITTED","deps":["w7.t1","w8.t1","w9.t1"],"blocked_by":[],"requires_decision":null,"files":["indicator.py","tests/test_indicator.py"],"blocker":null}
{"type":"session_memory","note":"run-plan-codex.js ignores t.deps and meta.scheduler; ordering is by t.wave only. deps here are documentation of real dependencies for humans/fix-rot. Within-wave tasks (W1, W2) are genuinely file-disjoint so same-wave parallel is safe."}
{"type":"session_memory","note":"gate0 runs in a raw bash -c outside RTK; meta.gate0_cmd is python3 -m pytest -q (bare pytest on PATH unverified there). Full suite chosen over per-file for cross-module regression coverage (~0.45s)."}
{"type":"session_memory","note":"Highest risk W7-W10: test_indicator.py is private-method-coupled (~1909 lines). Failure mode = codex making gate0 green by relaxing/deleting tests. Review leg must reject; assertions move to new seam, never weaken."}
