Skip to content

TUI: add a safe home-agent rail with isolated thread state#658

Draft
TZZheng wants to merge 99 commits into
Lingtai-AI:mainfrom
TZZheng:codex/issue-645-pr5-safe-agent-rail
Draft

TUI: add a safe home-agent rail with isolated thread state#658
TZZheng wants to merge 99 commits into
Lingtai-AI:mainfrom
TZZheng:codex/issue-645-pr5-safe-agent-rail

Conversation

@TZZheng

@TZZheng TZZheng commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

This is the PR 5 milestone from #645: deliver the first selectable ordinary-Agent rail with target-bound cold thread state, durable unread discoverability, and the minimum safe width/input contract while Main remains aggregate temporarily.

  • Adds a root-owned home-agent rail sourced from accepted inventory, with Main plus eligible readable ordinary Agents.
  • Activates ordinary rows through a rail-specific, fail-closed identity check without changing global Enterable.
  • Reuses one project ProjectMailStore, global mailbox cache/snapshot, scanner, tick loop, and inventory lifecycle; selecting an ordinary Agent does not create or scan a second global cache.
  • Rebinds one reusable Mail presentation to bounded target ThreadState for projection, send/reply, history, telemetry, editor, draft, focus, and generation.
  • Adds exact 83/84/85 width behavior, X-aware mouse partitioning, keyboard/focus, resize, paste, copy-mode, stale-row, visit-hide/return, and Main-return contracts.
  • Persists per-row unread state from accepted snapshots and advances only after accepted/rendered publication boundaries, including hidden/re-entry, restart, same-timestamp, removal/re-add, and write-failure behavior.
  • Uses same-target coalescing plus stale-result rejection rather than claiming true filesystem cancellation; work that has already started remains accounted for honestly.
  • Removes the superseded full-model ordinary activation path and the old syncCurrentThreadFromMail projection mirror in the same PR.

Dependencies and stacked review shape

Depends on #646 and draft #652. Draft #652 itself depends on #648 and #649.

The prerequisite heads live in the contributor fork rather than as an upstream composition branch. This PR is therefore opened as a draft against main while those prerequisites remain open. GitHub's rendered main...HEAD view includes the prerequisite work as well as PR 5.

Fresh pre-publication coordinates:

  • canonical main: 10c2822812add0ae07ed26ffcfff8d6cec202a4f
  • candidate HEAD: 356a5d3dd01c6c591077db4c1042ff8ae91d4658
  • candidate tree: 9ce20516a52b5f5a992aecb1edfd341775b26329
  • main...HEAD: 99 commits, 92 files, 14,756 additions, 642 deletions
  • binary patch SHA-256: 39bfd24f184b7f126c71c5b2e1b3fb2bfc6fc95caa069d796f559a5e366bebe0

The independently attributable historical PR-5 implementation range is 64b601ea1c2e7238e171063f4a025630c5934a6e..fa65f856dab3a418b1cfa4cf8d72119871adaf99: 86 commits, 67 files, 8,953 additions, 274 deletions; binary patch SHA-256 108dc9bfd31d63b6852d5f34afccff1f48312d94a9b4964fa6f0a559f36241e9.

64b601ea..HEAD is not a PR-5-only range after the branch integrated canonical main; it contains canonical bytes as well. After prerequisite PRs land, refreshing this draft may change source bytes. Any such change requires fresh exact-HEAD validation and both independent reviews again.

Contract and implementation

One owner, bounded projections

ProjectMailStore owns the one project-wide cache, accepted snapshot, scanner, scan mutex, refresh accounting, tick loop, and inventory view. ThreadState owns only target-specific event/inquiry history, viewport, draft/pending editor state, and local generation. Ordinary activation rebinds the existing Mail presentation rather than constructing another MailModel or copying the global cache.

Main uses MainAggregateWriter; ordinary/direct/cold projections use NoPersist. The write gate is enforced inside the filesystem session cache, so caller intent cannot cause an ordinary projection to rewrite Main's aggregate session files.

Fail-closed activation and publication

The rail is derived from accepted root inventory. Ordinary activation checks canonical directory, verified manifest address provenance, readability, accepted row identity, stale/phantom status, and existing path/PID safety before changing the active target. A stale accepted row schedules the existing root display-inventory refresh and preserves the prior thread/draft/focus/binding until a latest accepted replacement is installed.

Async completion is accepted only when its project/store, route, target, generation, source-cache identity, store version, and owner lifetime still match. Rejected work may settle its physical token, but cannot publish content or advance live/cached/durable unread state.

Layout and input

The root LayoutBudget remains the only horizontal partition owner. The 24-column rail is hidden when it would leave fewer than 60 chat columns:

  • width 83: hidden;
  • width 84: rail 24 / content 60;
  • width 85: rail 24 / content 61.

Render order and mouse hit-testing use the same partition. Keyboard and mouse share one activation path; paste remains chat-bound when chat owns focus; copy-mode keys/selections coexist with the rail; cross-project visits hide the home rail and cannot route rail input.

Durable unread

RailUnreadStore persists exact max-timestamp ID sets so a new item at the same timestamp is still recognized after restart. Unread reconciliation runs only after accepted state publication. Stale completions, stale addresses, transient write failures, removal/re-add, hidden agents, and Mail re-entry preserve the last-good boundary instead of inventing success or resurrecting rejected content. All new status text is localized in en/zh/wen.

Test-first and integration evidence

The committed history preserves real pre-implementation contract failures rather than syntax/import/environment failures. Representative boundaries include:

  • cold target identity/load/projection/send/history/editor REDs before their minimum implementations;
  • rail geometry, render/input, inventory selection, visit fencing, and durable unread REDs before visible activation;
  • stale accepted-row rescan RED e7e5eca4 before fix 648c1169;
  • aggregate Main-return RED f7e36515 before fix cbad3539;
  • one-owner structural RED fafad9c6 before consolidation 1cad3702;
  • locale/provenance RED 35b8e096 before the original complete candidate 54748f94;
  • production-faithful regressions for visit-return hybrid persistence and an older-page in-flight latch before their focused guards;
  • real merged-tree failure TestMainDoesNotOwnHumanLocationUpdate after canonical integration 3390033f, before the three-line ownership repair 18a5ff3a.

Coverage-only additions that passed on first semantically complete execution are recorded as coverage, not mislabeled as RED evidence.

Canonical integration and final release repairs

Canonical main advanced while the original candidate was under review. The branch integrated it at 3390033f; the real merged-tree normal suite then caught canonical startup's three-line fs.UpdateHumanLocation path reintroducing a second updater outside ProjectMailStore. Commit 18a5ff3a removes exactly those three lines. Focused GREEN plus the coherent exact-head module gate established that ProjectMailStore remains the sole production owner.

The first exact-18a5ff3a Claude review passed. Codex found no runtime defect but blocked release because two touched ANATOMY files still pointed locale/bootstrap/upgrade claims at semantically unrelated pre-launcher tui/main.go ranges. Commit 356a5d3d changes only:

  • tui/ANATOMY.md
  • tui/internal/config/ANATOMY.md

That delta is 15 additions / 15 deletions. Every non-document path and every other document path is byte-identical to fully tested 18a5ff3a. The corrected citations were checked semantically against current function and statement boundaries, not merely for numeric range validity.

Validation

Compiled/tested candidate bytes at 18a5ff3a

  • git diff --check and clean merge-tree against canonical main;
  • all 280 ANATOMY source citations in bounds;
  • full TUI module go test ./...;
  • full TUI module go test -race -p 1 ./...;
  • go vet -p 1 ./...;
  • go build ./...;
  • exact HEAD and clean worktree.

All 15 TUI module packages passed normally and under the serial race detector. Empty diff/vet/build logs represent successful zero-output commands, not skipped checks.

Exact documentation HEAD 356a5d3d

  • exact HEAD/tree/parent and clean state;
  • merge-tree equals the candidate tree;
  • exact 18a5ff3a..356a5d3d delta: one commit, two Markdown files, 15 additions / 15 deletions;
  • explicit exclusion diff: no non-document byte delta;
  • all 31 current tui/main.go citation occurrences in the two files (25 + 6) semantically audited;
  • 16 corrected source-range anchor groups complete, with no missing anchors;
  • real TestArchitectureEntryLinks PASS;
  • git diff --check PASS.

A second full normal/race/vet/build run was intentionally not repeated after the docs-only commit: every compiled/tested input is byte-for-byte identical to the already gated 18a5ff3a tree.

Immediate pre-publication preflight

Canonical main was fresh-fetched and remained exactly 10c28228; the merge base remained canonical main, the merge-tree remained the candidate tree, issue #645 and prerequisite PRs remained open, the worktree remained clean, and no candidate branch or duplicate PR already existed remotely.

Independent exact-HEAD reviews

  • Claude Code / Sonnet 5 / sonnet / high / plan-mode read-only: PASS at exact 356a5d3d. It independently reproduced all coordinates/hashes and the two-file identity proof, audited every changed sentence and current tui/main.go citation against source, reran the architecture entry test, and found the previous documentation blocker resolved with no new runtime or navigation defect.
  • Codex / gpt-5.6-sol / high / read-only: PASS at exact 356a5d3d. It independently reproduced all six controlling ranges and hashes, proved the exclusion diff empty, semantically audited all 31 citation occurrences and the location-update ownership seam, reran the architecture entry test, and found no critical/high/medium/release-blocking low issue.

Any source or test byte change expires both verdicts.

Same-PR cleanup, bridges, and rollback

Removed in this PR:

  • ordinary activation that constructed a new full MailModel;
  • App.syncCurrentThreadFromMail and its reverse projection mirror;
  • duplicate/global-cache target-switch scaffolding and test-only mirror helpers;
  • stale anatomy claims about a zero-width rail, obsolete async-kind/file-count descriptions, and pre-launcher startup ranges.

Planned bridges:

Rollback removes the rail and target switching and returns to one aggregate Main. PRs 1–4 may remain because they are behavior-preserving foundations. A failure in width/input, accepted-inventory activation, one-owner projection, or durable unread blocks PR 6.

Part of #645.

TZZheng added 30 commits July 14, 2026 00:53
# Conflicts:
#	tui/internal/tui/ANATOMY.md
TZZheng added 29 commits July 15, 2026 05:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant