Skip to content

TUI: unify asynchronous completion identity gates#652

Draft
TZZheng wants to merge 8 commits into
Lingtai-AI:mainfrom
TZZheng:codex/issue-645-pr4-async-envelope
Draft

TUI: unify asynchronous completion identity gates#652
TZZheng wants to merge 8 commits into
Lingtai-AI:mainfrom
TZZheng:codex/issue-645-pr4-async-envelope

Conversation

@TZZheng

@TZZheng TZZheng commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

This is the PR 4 milestone from #645: replace path-specific asynchronous identity checks with one fail-closed envelope and acceptance predicate, while preserving visible behavior.

  • Routes exactly eight logical completion paths through one identity protocol: initial rebuild, steady refresh, session persist, older-page history, exact history count, refresh tick, liveness pulse, and external-editor completion.
  • Validates the relevant project/store, target directory, address fingerprint, generation, source-cache identity, store version, and tick/pulse epoch before visible mutation.
  • Revalidates current inventory identity for target-bound work and removes the superseded ad hoc/generation-only gates.
  • Keeps refresh settlement token-exact and non-publishing, retains the existing 250 ms liveness animation without adding another physical scan, preserves MainAggregateWriter, and makes no resource-cancellation claim.
  • Closes a destructive-reset lifetime gap found during independent review: the root event loop now retires active/suspended store ownership and Mail-local async identity before returning the filesystem-deletion command; cleanup-start is at-most-once and final Done is active-completed-state-bound.

Dependency and stacked review base

Depends on #648 and #649.

Both prerequisite PR heads live in the contributor fork rather than as branches in the upstream repository. GitHub therefore cannot use their local composition as this upstream PR's base without creating a temporary upstream branch. This PR is opened as a draft against main while those prerequisites remain open; GitHub's current rendered diff consequently includes their changes.

The independently reviewed PR-4-only range begins after synthetic composition commit 717e9d800a2a3038a1eed6826a0fe156e43caf7f, whose exact parents are:

PR-4-only binary patch SHA-256 (717e9d80..aaf1021c):

561766105f1d72abfafd06b33ff38c7a69c25c8cefc8f972939057bb75cc8a4f

Do not treat the combined main..HEAD view as PR4 attribution. After #648 and #649 land, the draft can be refreshed against canonical main; any changed source bytes require fresh exact-HEAD validation and both independent reviews again.

Contract and implementation

The shared predicate fails closed on unknown kinds, missing or extra identity-mask fields, invalid ownership/target binding, changed inventory address, stale generation/cache/version/epoch, and captured work that attempts to weaken current inventory policy.

Every producer captures identity at the substantive launch boundary, and every consumer runs the same acceptance predicate before publication. projectMailRefreshRequestMsg remains coordination rather than a ninth completion family; delayed human-location work re-enters the predicate immediately before its side effect. The old acceptRefresh, acceptsTick, projectMailRuntimeGate, message-local identity checks, and generation-only editor gate are removed rather than bridged.

The Nirvana sequence is now:

  1. Confirm emits a private cleanup-start handoff.
  2. The root event loop validates active/confirmed/not-done/not-started state, latches cleanup-start, and synchronously invalidates project-mail ownership.
  3. Only then does it return the destructive cleanup command.
  4. The completed screen may remain open safely; final Done is accepted only from that state and repeats invalidation defensively.
  5. Cancel/Esc before confirmation preserves the lifetime unchanged.

Test-first evidence

The committed sequence preserves the intended RED boundaries:

  • 083ff62c: source/symbol inventory contract RED before implementation.
  • 8ec1870c: direct predicate matrix RED with 55 intended assertion failures, not syntax/import/environment noise.
  • 79c808b8: minimum shared envelope/predicate; focused normal and race green while runtime installation remained red.
  • ba01e821: nine producer/consumer installation contracts RED before installation.
  • aaf1021c: installation plus both post-review corrections.

Two additional review-driven contracts were also captured before their fixes:

  • destructive deletion-to-final-Enter window: late refresh/persist/older completions failed as intended while cancel already passed;
  • replay/state binding: duplicate cleanup-start returned a second destructive command and premature Done recreated/reset outside the completed screen.

An earlier test-authoring attempt that compared a non-comparable struct was rejected and is not counted as RED evidence.

Validation

Validated at exact clean HEAD aaf1021c0cd25eda70f4f89b1b48def6336ec307:

  • gofmt and git diff --check;
  • permanent source/symbol inventory and all nine installation contracts;
  • corrected three-mutation AST proof, including byte-for-byte source restoration;
  • focused normal and -race suites, including the destructive-window and replay/state contracts;
  • internal/tui normal and race suites excluding the known unrelated portal case;
  • module tests excluding that portal case;
  • go vet ./...;
  • semantic ANATOMY validation: 39 checked ranges, 9 required lifecycle clauses, 52 stale citations/claims rejected.

After both reviewers completed, the parent reran the exact focused normal and race suites with isolated temporary caches: both passed and the worktree remained clean.

Known unrelated caveat: TestPortalURLTimeoutKillsChild can fail because its fake portal never writes a PID. The file is untouched by this PR and the same failure was reproduced independently; it is not hidden as a candidate pass.

Independent exact-HEAD reviews

Both reviews used fresh detached worktrees and re-adjudicated the earlier destructive-lifetime BLOCK; every changed byte invalidated the old verdicts.

  • Codex CLI 0.144.1 / gpt-5.6-sol / high / real read-only sandbox: VERDICT: PASS, no BLOCKER/HIGH/MEDIUM/LOW findings. Report SHA-256: 147c9d6e990a3da1cac5fb6fdb74d75d7567916aeebb0a91d8cdd6d8a3637365.
  • Claude Code 2.1.206 / sonnet / high / mutation tools disabled: VERDICT: PASS, no BLOCKER/HIGH/MEDIUM findings. Report SHA-256: 143af57058b4732dbbf3d40ca4f7d1a627c1650b5f54d314feaf6957afceba99.

Claude recorded three non-blocking LOW notes for transparency: a historical local evidence-log naming ambiguity, the external semantic-ANATOMY validator's lack of explicit async_envelope.go entries (the file is structurally covered by permanent source-inventory tests and was manually inspected), and functionally redundant older-page envelope capture from the same frozen value snapshot. None changes the accepted runtime identity or visible behavior.

Same-PR cleanup, bridge, and rollback

  • Superseded per-path acceptance policies are deleted in this PR; there is no old/new compatibility bridge.
  • Rollback is the previous per-message gates. Because visible behavior is intentionally unchanged, rollback is independent of ProjectMailStore ownership.

Part of #645.

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