Skip to content

feat(agent): generic ACP runtime + omp backend - #126

Merged
SoloJiang merged 46 commits into
mainfrom
feat/omp-acp-runtime
Jul 28, 2026
Merged

feat(agent): generic ACP runtime + omp backend#126
SoloJiang merged 46 commits into
mainfrom
feat/omp-acp-runtime

Conversation

@SoloJiang

@SoloJiang SoloJiang commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add a generic ACP runtime (src-tauri/src/acp/) — JSON-RPC 2.0 framing, session/update → ChatEvent map, permission option mapping, global multiplexed child pool.
  • Register omp as the first AcpBackend (connection-shaped AcpAdapter), detect/Settings/UI identity, min version 17.1.0, priority last.
  • Engine path: spawn_acp_turn + consumer streams text/tools/usage; AskRegistry for session/request_permission; MCP via session/new HTTP specs; empty inject for ACP tools.
  • Parity pieces: slash cache, terminal omp --resume, canRewind + jsonl cut-before rewind (ACP fork is full-history only; S0 proved hand-cut load).

Design notes

  • Approach C (generic ACP, not omp-only client). Codex app-server stays on its own dialect.
  • Spec/plan live under gitignored docs/ (not in this PR).
  • S0 fixtures under src-tauri/tests/fixtures/acp/ from live omp 17.1.1.

Verification

cd src-tauri && cargo test        # 1481 lib passed, 1 ignored (live omp_acp_pong) + integration suites
pnpm build && pnpm test           # 151 passed
git diff --check

Every new guard in the review rounds below was mutation-tested: reverting the
guard turns its test RED, and the files are restored byte-exact afterwards.

Merge + review rounds (post-handoff)

Merged 59 commits of main (#150 PR/MR state machine, #146/#153 ask
allowlist, #151 quota failover, #149 engine routing, #152 auto-merge).

The previous merge had dropped main's teardown_for_switch rewrite instead of
reconciling it: stop_quiet had independently grown a fifth element on each
side (acp_asks here, was_busy on main), leaving a 4-tuple destructuring
against a 5-tuple return — the compile failure that had rust-test red on both
platforms. It returns a named StopQuietOutcome now, so a future field cannot
silently invalidate a call site.

Twelve Codex findings across two rounds, all real, all fixed — highlights:

  • Permission risk was misclassified for every ACP request. intent_key is
    a lossy grant-cache token (execute:bash), so comparing it to bare verbs
    never matched and shell commands scored as generic tool calls. Now classified
    from the toolCall itself via a PermissionIntent mapped exhaustively.
  • Always-grants could leak across files. The grant key was the stringified
    rawInput, which is identical (often empty) for two edits whose paths live
    only in toolCall.locations. Keyed on a length-prefixed identity covering
    every location.
  • Two hard-stop races: the turn-ownership epoch is now advanced in the same
    critical section that takes the ACP client, and a wedged session is abandoned
    (id cleared, client retired under an explicit policy) even when a shared
    pooled client legitimately survives for another route.
  • Agent processes outlived the UIshutdown_all had no production caller;
    wired into RunEvent::ExitRequested and awaited.
  • Rewind could cut at the wrong message — the {system}\n\n{user} prepend
    was guessed as "before the last blank line"; the real system prompt is now
    stripped exactly, and a changed prompt declines rather than mis-cutting.

Follow-ups

  • Cross-engine rewind boundary. Rewinding the first message after a switch
    TO omp fails (omp_user_not_found). The dispatched text carries a context
    digest that is deliberately absent from the persisted row, and more
    fundamentally the omp jsonl begins at the switch while the DB ordinal counts
    the whole session — the two disagree about the history range. The failure is
    safe (the fork runs before any stop/truncate, so the session stays intact)
    and is pinned by a test so a future matcher loosening cannot turn it into a
    silent wrong cut.
  • Queued prompts outrun a pending command refresh. An alias change with
    apply_to_existing during a busy turn sets pending_command_refresh, but the
    queue drain dispatches straight to client.prompt, bypassing send — the
    only path that bounces. Already-queued messages therefore finish on the old
    executable. Fixing it means carrying the deferred refresh through the idle
    transition without disturbing bus_read_pos (the bus wake's position relative
    to the queue), which on_turn_end advances in the same step.
  • Session Info lists engine skills for omp. SessionInfoPanel merges
    meta.engineSkills for omp, and gather_omp scans .agents/.claude, so
    Weft-materialized built-ins can be listed for a session with an empty
    workspace selection. Display-only — nothing keys off that list — and the right
    fix is an exhaustive tool-to-policy mapping rather than another special case.
  • ACP connection-generation fencing (one change, two symptoms). Request ids
    restart at 1 on every spawn, and per-frame dispatch (resolve,
    on_session_update, on_permission) does not check the reader's
    connection_gen — so a late response from a replaced child can satisfy a new
    connection's initialize or prompt. EOF cleanup IS fenced; per-frame dispatch
    is not. Relatedly, a handle that is spawned but still awaiting initialize is
    not yet in POOL, so the exit drain cannot see or cancel it; that wants an
    in-flight registry rather than another relocated check.
  • Writer-failure path is not fenced to its connection generation.
    read_loop clears Inner only when connection_gen matches; the writer /
    flush-timeout failure path carries no generation, so a stale task can reap a
    replacement child and fail its initialization.
  • Two ACP race errors reach the toast in English. The stop/teardown and
    connect/prompt races return hardcoded sentences that notifySendFailed shows
    verbatim. They want the stable-code-plus-catalog treatment
    acp_session_open_failed already has.
  • Freeze marker ordering. The native id is taken before stamp_freeze_marker
    runs, so a FAILED marker write (locked/full DB) leaves no witness to restore
    it from. Reachable only when the DB cannot be written at all.
  • PowerShell resume quoting (src/lib/resume.ts). A quoted executable needs
    the call operator (&) to run. Belongs with Windows support — release.yml
    builds macOS only, so there is no way to test it today.
  • Live GUI smoke (lead/worker omp turn, permission card, stop, rewind) — the
    one thing not covered by the suites above.
  • The drain-barrier reasoning-tail clear has no unit test: it lives inside
    acp_consumer, which needs a live AppHandle + Db.
  • Optional: native ACP image blocks; richer always-allow session cache UI.

Generic ACP framing and session/update mapping with unit tests from
live omp 17.1.1 spike fixtures. Runtime/backends land next.
OmpBackend + AcpAdapter (connection), TOOL_PRIORITY/TOOLS/min 17.1.0,
ToolIcon/i18n/type union. Settings will list omp via detect_tools.
Global per-backend omp acp child; JSON-RPC demux to session subscribers;
prompt uses long timeout (cancel aborts); permission replies are idempotent.
spawn_acp_turn + consumer stream ChatEvents; permission via AskRegistry;
queue drain loops without recursive futures. inject no-ops for ACP tools
and exposes acp_mcp_servers for session/new.
discover_slash reads ACP available_commands cache; tool-command bounce
shuts down the ACP child; rewind cuts ~/.omp session jsonl before the
matched user message (handles system-prompt prepend suffix match).
Connection-shaped omp has no resident stdin when idle; bus wakes and
coordinator nudges were Noop. Treat ACP like per-turn for hidden_delivery
and dispatch spawn_acp_turn from send_hidden_inner.
omp is not per_turn and not claude, so ensure_running_locked bailed with
"unknown lead tool omp" before send could reach spawn_acp_turn. Early-return
Ok(None) for ACP tools the same way as per-turn dialects.
Holding self.inner across request("initialize") deadlocked the first
connection (request_timeout needs the same lock). Also release POOL.clients
before ensure_connected. Live omp_acp_pong_live passes.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9742d68dfa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/adapters/mod.rs
Comment thread src-tauri/src/acp/runtime.rs Outdated
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/acp/runtime.rs Outdated
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/lead_chat/rewind.rs Outdated
Comment thread src-tauri/src/lead_chat/rewind.rs Outdated
Comment thread src-tauri/src/acp/runtime.rs
Comment thread src-tauri/src/acp/runtime.rs
- permission pending set by request id (reuse-safe); always-cache by intent
- drop locks before await; serialize first child create; pass command pin
- stop_quiet cancels/unsubscribes ACP session
- resubscribe when runtime lost route; MCP by thread kind (concierge=global only)
- rewind: prepend-only suffix match, missing file errors, no symlink walk
@SoloJiang

Copy link
Copy Markdown
Owner Author

Review pass (Codex P1/P2) — addressed on 82c6150

P1

Finding Fix
ensure_running unknown tool omp Early-return for is_acp_tool (2842573)
Permission id reuse (id:0) pending_permission_ids insert/remove; always-cache keyed by intent
Stop/takeover leaves ACP running stop_quiet cancel + unsubscribe before clear
Curator/concierge MCP wrong MCP from thread.kind (concierge=global only, no bus)
Deadlock on initialize drop(inner) before request("initialize") (601dd85)
Resubscribe after reconnect Always check is_subscribed; re-subscribe if route lost
write_raw under mutex Drop guard before any await in on_permission

P2

Finding Fix
First-create race CREATE_LOCK + recheck pool
Command pin client(backend, program) from session-effective command
Rewind suffix too broad Suffix only when body has system-prepend \n\n
Missing session file Err instead of silent fresh session
Symlink walk Skip symlinks + visited canonical dirs
Images v1: text/files appendix only (same as non-claude); native ACP image blocks = follow-up

Verification

cargo test --lib acp::                         # 22 passed
cargo test --lib acp::runtime::tests::omp_acp_pong_live -- --ignored  # ok
cargo test --lib hidden_delivery                # 3 passed

Head: 82c6150

Merge origin/main (canonical Always keys #89) and wire ACP
SessionEvent::Permission through ask::action_key + 6-arg request.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 560968421c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/lead_chat/rewind.rs Outdated
Comment thread src/lib/types.ts
Comment thread src-tauri/src/lead_chat/rewind.rs Outdated
Comment thread src-tauri/src/detect.rs
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/acp/runtime.rs Outdated
- acp_emit_turn_end bails when stopped/reset_epoch advanced
- track+cancel ACP permission asks on stop/skill-refresh
- curator skips ACP-only default (omp) for next installed CLI
- rewind system-prepend match requires prefix+separator+user
- bound omp session walk; exclude omp from skill discovery UI
- parse session/new|resume configOptions into SessionOpen meta
@SoloJiang

Copy link
Copy Markdown
Owner Author

Round 2 review (7 threads) — addressed on e81bdfe98fd26f75f2a04252e9408aec0e505491

P Finding Fix
P1 late overwrites stopped epoch guard + early return
P1 omp in curator exec → next non-ACP CLI
P1 permission ask survives stop cancelled on stop/refresh
P2 rewind blank-paragraph false positive system-prepend = prefix+\n\n+user only
P2 skill discovery pending forever exclude omp
P2 unbounded session walk caps + skip artifact dirs
P2 blank model in Session Info parse configOptions on session open

Local: cargo test --lib acp:: → 23 passed.

Later user prompts with blank paragraphs no longer steal rewind ordinals.
Regression test covers first-vs-later prepend behavior.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 186de3ab0c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/lead_chat/rewind.rs Outdated
Comment thread src-tauri/src/acp/runtime.rs Outdated
Comment thread src-tauri/src/acp/runtime.rs
Comment thread src-tauri/src/acp/map.rs
Comment thread src-tauri/src/curator.rs Outdated
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
- recheck stopped/epoch immediately before session/prompt
- rewind first-user is row order (seen_users), not match count
- permission timeout bound to per-request generation (id:0 safe)
- buffer session/update until subscribe (slash commands)
- cap ACP tool output at 16k; curator honors command overrides
- keep usage_update context; persist ACP thinking as reasoning
@SoloJiang

Copy link
Copy Markdown
Owner Author

Round 3 (8 threads) — a73e8c5140ba6775132d152a9f819ec65b428415

P Fix
P1 stop→prompt race epoch/stop recheck before + inside prompt task
P2 first-user rewind seen_users row order, not match count
P2 permission timeout generation token per pending id
P2 pre-subscribe updates buffer until subscribe, then drain
P2 tool output cap_output 16k
P2 curator override tool_command::command_for
P2 context gauge keep usage_update.used, ignore billing totals
P2 thinking last_reasoning + LeadStateInfo

cargo test --lib acp:: 23 ok; rewind omp match tests ok.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a73e8c5140

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/acp/runtime.rs Outdated
Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src-tauri/src/acp/map.rs
Comment thread src-tauri/src/acp/runtime.rs Outdated
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/acp/runtime.rs
- buffer session/update only while opening; purge on unsubscribe
- connection generation so stale read_loop cannot drop reconnect
- fail/shutdown via proc_registry::reap (OMP tool descendants)
- resume+load both fail → error (no stale native id)
- seed Session Info MCP from injected specs; empty commands clear
- tauri.conf withGlobalTauri for MCP bridge E2E

Live verified via Tauri MCP: Oh My Pi lead issue #7, prompt→pong,
model xai-oauth/grok-4.5 + context gauge 24k/500k.
@SoloJiang

Copy link
Copy Markdown
Owner Author

Live E2E (Tauri MCP) + round-4 fixes — 6cf087d00442953bac6e9a5247dbd78df05541dc

Live path (Oh My Pi)

  1. WEFT_MCP_BRIDGE=1 pnpm tauri dev + driver_session
  2. Set default tool → omp
  3. New issue feat(session): unify worker surface + auto-recover interrupted sessions on restart #7 OMP lead live path (tool badge: Oh My Pi)
  4. Send: Reply with exactly one word: pong. Do not use any tools.
  5. Got assistant pong; context gauge 24k / 500k, model xai-oauth/grok-4.5

Code (this commit)

Item Fix
P1 unsub buffer replay buffer only while opening_sessions; purge on unsubscribe
P1 process tree proc_registry::reap on fail/shutdown
P2 connection gen stale read_loop cannot drop reconnect
P2 load fail no stale native id
P2 empty commands clear slash cache
P2 Session Info MCP seed from injected MCP specs

cargo test --lib acp:: → 24 passed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6cf087d004

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src-tauri/src/acp/runtime.rs Outdated
Comment thread src-tauri/src/lead_chat/rewind.rs Outdated
Comment thread src-tauri/src/acp/runtime.rs Outdated
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/acp/permission.rs Outdated
- map agent_thought_chunk → Activity "thinking" with accumulated tail
- clear thinking chip on first text/tool; frontend empty-name clears
- gather_omp cwd skills; enable skill discovery for omp
- seed+Init MCP/model after ACP open; lead_session_meta overlays engine
- metaFromSnapshot/Init/fillMetaHoles set mcpAuthoritative so filtered
  weft_* internals show "no user MCP" instead of forever-pending
omp/grok often skips agent_thought_chunk; emit Activity thinking
when session/prompt starts so the busy line is not a blank wait.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8fdd2d07db

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src-tauri/src/acp/runtime.rs Outdated
Comment thread src-tauri/src/commands.rs
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/lead_chat/rewind.rs Outdated
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Drop injected/discovered skill subgroups for one deduped chip list.
Stop filtering weft_bus/planner/global from Session Info so omp sessions
show the MCP they actually have (status tagged "weft").

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 035b458d4c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/lead_chat/rewind.rs Outdated
Comment thread src-tauri/src/acp/runtime.rs
Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src/state/store.tsx
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Comment thread src-tauri/src/lead_chat/rewind.rs Outdated
- Consumer epoch (P1). `acp_consumer` read `reset_epoch` after being spawned,
  so a Stop/rewind/switch landing in that gap made it adopt the NEW generation
  and treat buffered events from the abandoned session as live. Captured at
  subscription time and passed in, so a route keeps the generation that
  created it.

- Reconnect retired mid-handshake (P1, a regression from last round). The
  retry-reap I added fires when `pending` drains — which happens the moment
  `initialize` resolves on a reconnected handle, while no route exists yet. It
  shut down the client the caller was about to use, whose `session/new`
  then failed with `ACP not connected`. `ever_had_session` separates "done
  with its routes" from "has not opened one", and it lives in `may_retire`
  rather than at the call site so the predicate stays testable.

- Queued-prompt errors (P1). The follow-up error branch skipped the drain and
  went straight to rollback, which clears `inner.current` without finalizing
  it — leaving `streaming` rows behind and letting consumer updates land on a
  reset turn. It now drains and finalizes in the same order the success path
  does.

- Force-reset notice (P1). Emitted prose, now a stable token localized by the
  catalogs — the shape this PR already uses for `acp.permission_required`. I
  argued last round that Rust could not reach the catalogs; that was wrong,
  and the counter-example was in this same PR. The mapping moved to
  `src/lib/noticeTokens.ts` so both sides are pinned: a Rust test asserts the
  literal, a frontend test asserts every token resolves in BOTH catalogs.

- Symlinked session root (P2). `is_dir()` follows a symlink and the other
  checks only reject one at the final component, so a redirected root put the
  whole scan outside the tree. The root is now checked with
  `symlink_metadata`, and — the actual boundary — the selected path must
  canonicalize to somewhere under the canonical root. That predicate is
  unreachable via the scans (they refuse symlinks first), so it is tested
  directly rather than left as an unverifiable guard. It does NOT cover hard
  links; `canonicalize` does not resolve them.

- Slash-palette clearing (P2). `discoverSlash` returns an empty list for an
  authoritative empty palette AND for transient failures, and the call site
  stored it unconditionally — despite its own comment promising otherwise.
  Both the worker and lead paths now keep the existing list on empty.

- Rewind ids (P2). Derived from the clock, so two concurrent OMP rewinds on
  one tick produced the same id and their truncating `fs::write`s overwrote
  each other. Uses the existing random `new_uuid_v4`; the clock helper is gone.

Also serialized the four HOME-overriding rewind tests behind one lock — cargo
runs tests on threads and they were racing each other's roots.

Verification
- `cargo test`: 1507 lib passed (was 1501), all integration suites, 0 failed
- `pnpm build` clean; `pnpm test` 154 passed (+3); `git diff --check` clean
- Mutation-tested all four new guards — dropping the root symlink check,
  comparing path strings instead of real locations, drifting the notice token,
  and removing the never-opened-a-route guard all go RED

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@SoloJiang

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dab6718e32

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/acp/permission.rs Outdated
Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src-tauri/src/acp/runtime.rs Outdated
Comment thread src-tauri/src/acp/runtime.rs
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
Four of these sit on code this PR's earlier rounds touched.

- Pathless reads (P1). My previous round established "an empty set is Unknown,
  never ReadOnly" for multi-path requests, then contradicted it one branch over
  by tiering a pathless request from its verb. For a write that is right — the
  verb alone establishes mutation. For a READ it is not: `classify_file("Read",
  "")` answers `ReadOnly`, which a read-only session/issue grant releases with
  no card, so a sparse request carrying its target in `title`/`content` could
  read a credential unseen. Split into an explicit per-verb pathless verdict.

- Sparse grant identities (P1). `grant_identity` keyed on `rawInput` +
  `locations` only, so a request that omits both — valid, and described in
  `title`/`content` — collapsed to one identity, and an Always granted to the
  first `execute` silently approved a different one. Every action-bearing field
  now joins the key; absent stays distinguishable from present-but-null.

- Auto-approve raced teardown (P1). Only the waited-for-a-human branch
  rechecked stop state before replying; the Always/Full branch has no ask to
  cancel and rechecked nothing, so a Stop landing between the `reject_now`
  sample and the `auto_decision` verdict reached the wire as an allow — queued
  ahead of `session/cancel`, starting a tool after the user stopped the turn.
  The check moved to the single path every branch leaves through, so "never
  allow after teardown" is an invariant of the reply rather than something each
  branch has to remember. It now covers `reset_epoch` too (rewind/switch).

- Shutdown raced client creation (P1). `shutdown_all` did not take
  `CREATE_LOCK`, which `client()` holds from lookup through `ensure_connected`
  and releases only after inserting — so the drain could run entirely between
  spawn and insert, find an empty pool, and let the exit path terminate with an
  untracked agent still running. Shutdown now serializes with creation and sets
  a flag; a creation that finds it reaps its own child instead of inserting
  into a pool nobody will drain again.

- Orphan after a failed open (P2). `resolve`'s retry declines while
  `expecting_session` is still standing, and the failure paths cleared that
  counter without retrying — leaving a route-less child the engine never
  published, unreachable by any later stop/switch. Clearing the LAST
  expectation now retries retirement; a successful open marks its route first,
  so it is untouched.

Verification
- `cargo test`: 1508 lib passed, all integration suites, 0 failed
- `pnpm build` clean; `pnpm test` 154 passed; `git diff --check` clean
- Mutation-tested both new predicates — restoring the verb fallback for
  pathless reads and narrowing the grant identity back to two fields both
  go RED

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@SoloJiang

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a8438de2d3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/lead_chat/rewind.rs
Comment thread src-tauri/src/acp/permission.rs Outdated
Comment thread src-tauri/src/engine_routing.rs
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
- Both path sources (P1). `tool_paths` returned structured `locations` and
  only fell back to `rawInput` — treating them as alternatives. A request could
  put an ordinary file in `locations` and a credential in `rawInput.path`, be
  tiered `ReadOnly` off the ordinary one, and be released by a read-only grant
  with no card. Both sources are collected now, de-duplicated, worst tier wins.

- OMP in automatic fallback (P2). Adding OMP to `candidate_list` so manual pins
  could resolve also made it reachable from the legacy-fallback arm, which
  contradicts the closed Codex/Claude automatic pool and could route curator
  work to an engine the curator refuses. Excluded there exactly as OpenCode is;
  a test pins both halves of the contract (manual pin resolves, automatic
  fallback never selects it).

- Force-reset notice kind (P2). Posted via `ask_human`, so Needs-you rendered
  an answer box whose reply would inject a stray bus message into the session
  that was just reset. It is a persistent, non-answerable notice —
  `notify_human_action_required`. The IM bridge also forwarded the raw token,
  so `bus::notice_text` now resolves tokens for consumers that cannot reach the
  webview catalogs (IM sends on its own fixed locale).

- Post-switch rewind (P2): PINNED AS A LIMIT, NOT FIXED. The report is
  accurate — a rewind of the first message after a switch to OMP always fails.
  The dispatched text is `system + context_digest + user` while the row holds
  only `user`, and the digest is kept out of the DB deliberately (PR #139: it
  can carry paths and pasted secrets), so nothing matches.

  Not fixed here because the failure is already the safe outcome — `fork_omp_at`
  runs before any stop/truncate, so the session is left fully intact — and the
  available "fix" is to accept any trailing segment after a blank line, which is
  precisely the mis-cut the previous round tightened this matcher to prevent.
  A real fix must anchor the OMP-native history boundary: the jsonl begins at
  the switch while the DB ordinal counts the whole session, so the two disagree
  about more than the digest. That is cross-engine rewind semantics, not a
  matcher tweak. A test pins the current refusal so a future loosening cannot
  turn it into a wrong cut silently.

Verification
- `cargo test`: 1514 lib passed, all integration suites, 0 failed
- `pnpm build` clean; `pnpm test` 154 passed; `git diff --check` clean

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@SoloJiang

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: df423afaa3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src-tauri/src/acp/map.rs
Comment thread src-tauri/src/lead_chat/rewind.rs
Two of the P1s are residue from this PR's own earlier fixes.

- Abandoned route left registered (P1). The stale-native-id clear I added two
  rounds ago cleared the engine and DB ids but left the runtime `SessionRoute`
  and its consumer in place, so late text/tool/permission events kept arriving
  in this engine after the retry opened a different session — and the surviving
  route pinned the pooled client against retirement. It now cancels and
  unsubscribes first, like every other abandonment path.

- Teardown admitted work it would then discard (P1). Bumping `reset_epoch`
  early closed one direction of this race: work reserved BEFORE a teardown can
  no longer land after it. The other direction stayed open — a send arriving
  DURING the teardown captures the already-bumped epoch, passes every check,
  and has its queued work replaced by `TurnState::default()` when the teardown
  reacquires the lock. The epoch cannot express "mid-teardown", so that window
  is now an explicit `tearing_down` flag that `send_reservation_valid` refuses.

- Permission cards outlived a dead prompt (P1). On a transport failure the
  consumer stays blocked on the `AskRegistry` receiver, so nothing retired the
  card: it remained actionable for the full hour, and Always/Full would persist
  a grant for a request whose native session was already gone. Both prompt
  failure paths — first and queued — now cancel the tracked asks.

- Writes labelled as reads (P2). `tool_name_from_raw` excluded `write` from its
  mutation kinds, so the timeline gave a file write a read's name and icon
  while the permission layer classified the same call as a mutation.

- Truncated scan reported as missing (P2). A bucket past the 4096 scan cap made
  `find_omp_session_file` answer `Ok(None)`, which surfaces as
  `omp_session_not_found` — telling the user their session is gone when the
  scan merely stopped looking. Truncation is now tracked through both the
  preferred scan and the fallback walk and reported as its own error. Reaching
  the HIT cap is deliberately NOT truncation: the session was found.

Verification
- `cargo test`: 1517 lib passed (was 1514), all integration suites, 0 failed
- `pnpm build` clean; `pnpm test` 154 passed; `git diff --check` clean
- Mutation-tested the three new guards — dropping the teardown gate, collapsing
  `write` back into `read`, and removing the truncation error all go RED

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@SoloJiang

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab038af647

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src-tauri/src/bus/notice_text.rs Outdated
Comment thread src-tauri/src/acp/runtime.rs Outdated
- Notice copy duplicated across languages AND runtimes (P1). Last round I added
  a Rust table so the IM bridge could render the force-reset token, which left
  the same two sentences transcribed in `bus/notice_text.rs` and in
  `src/i18n/{en,zh}.ts` — a catalog edit would silently give remote and in-app
  users different text. Now `src/i18n/notices.json` is the single source: the TS
  catalogs import it, Rust bakes it in with `include_str!`, and a test on each
  side asserts the rendered copy equals the shared file. A malformed file
  degrades to empty rather than panicking, so a Rust test asserts it parses and
  carries both languages — otherwise that degradation would ship as raw tokens.

- Process-wide creation lock (P2). `CREATE_LOCK` was held across
  `ensure_connected`, whose handshake waits up to 60s, so one slow or broken
  command pin blocked the first send of every OTHER `(backend, program)` key —
  defeating the isolation the pool key exists to provide. Creation and
  retirement now take a per-key lock under a read guard; only `shutdown_all`
  takes the write guard, which preserves the drain-vs-create serialization
  added last round.

Verification
- `cargo test`: 1520 lib passed (was 1517), all integration suites, 0 failed
- `pnpm build` clean; `pnpm test` 155 passed (+1); `git diff --check` clean

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@SoloJiang

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5dd2b7ef4c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/bus/notices.generated.json
Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src-tauri/src/lead_chat/rewind.rs
Comment thread src-tauri/src/acp/runtime.rs Outdated
Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src-tauri/src/lead_chat/engine.rs
… latency

- Freeze recovery lacked both guards the force-reset path got (P1 x2). I fixed
  one path and left its twin: freeze recovery neither reserved the engine
  (`tearing_down`) across its cancel/unsubscribe/finalize/marker/native-id
  cleanup, nor retired a client whose cancel was ignored. So a send admitted
  mid-cleanup could resume the session being abandoned, and the next send could
  retrieve the same pooled process still running the frozen prompt. Both
  mirrored, plus `native_id` is TAKEN rather than cloned, as force-reset does.

- Notice copy authored outside the catalogs (P1). Last round's `notices.json`
  made a third localization source, which is what AGENTS.md forbids. The
  catalogs are authoritative again; `scripts/gen-notice-copy.mjs` mirrors the
  listed tokens into `notices.generated.json` for the IM bridge to render, and
  `noticeCopy.test.ts` fails if the mirror drifts.

- Truncated-scan code reached the UI raw (P2). `omp_session_scan_truncated` is
  new this PR, and `RewindDialog` already had the mapping table it belonged in
  — I added the code without its entry. Distinct copy from "not found" on
  purpose: the session likely exists, the scan stopped at its cap.

- Exit blocked on in-flight handshakes (P2, from last round's fix). The write
  guard I added made `ExitRequested` wait out a 60s handshake — a hung-looking
  desktop app. The mutual exclusion moved to the pool lock itself: `client()`
  checks the shutdown flag and inserts under it, `shutdown_all` sets the flag
  and drains under it. A racing creation either lands before the drain or reaps
  its own child, so no untracked survivor, and exit never waits.

Verification
- `cargo test`: 1521 lib passed, all integration suites, 0 failed
- `pnpm build` clean; `pnpm test` 155 passed; `git diff --check` clean

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@SoloJiang

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fde95eab48

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/session/SessionInfoPanel.tsx
Comment thread src-tauri/src/acp/runtime.rs
Comment thread src-tauri/src/lead_chat/engine.rs
Both of these are mine — one a regression from the previous round, one the same
omission for the third time.

- Forced reset did not reserve the engine (P1). `stop_quiet` got `tearing_down`,
  then freeze recovery got it, and `force_acp_turn_reset` — the third path with
  the identical shape — still did not. It matters most here:
  `reset_frozen_appserver_turn` clears `turn.busy` AND `interrupting`, so a send
  arriving during the cancel/unsubscribe/retire/DB-clear awaits captures the
  already-bumped epoch, is admitted onto a fresh session, and then has its
  native id cleared and an idle state emitted over it by the older reset.

- Pooled handle escaped the shutdown check (P1, from last round). Moving the
  mutual exclusion onto the pool lock covered the fresh-handle path only. An
  EXISTING handle returns early, and `shutdown_all` can drain it and clear its
  `inner` between the lookup and `ensure_connected` — which then spawns a
  replacement child that the early return hands back without re-inserting,
  leaving a child nothing tracks and an exit path already past. That path now
  re-checks under the pool lock and re-inserts if the drain removed it.

Verification
- `cargo test`: 1521 lib passed, all integration suites, 0 failed
- `pnpm build` clean; `pnpm test` 155 passed; `git diff --check` clean

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8cd25d3f36

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src-tauri/src/acp/runtime.rs
Comment thread src-tauri/src/lead_chat/engine.rs Outdated
The fourth gap in the same guard, and the one that made the previous three
incomplete rather than merely partial.

`send_reservation_valid` checks `tearing_down`, but hidden delivery — a bus
wake, a plumbing send — never passes through it, and `spawn_acp_turn` did not
check it either. So all three teardown paths that take the reservation
(`stop_quiet`, freeze recovery, forced reset) were invisible to a bus wake: it
started a fresh native session mid-cleanup, and the older reset then cleared
that session's native id and emitted idle over its active turn.

Enforced through one predicate used by both entry points, so they cannot drift,
and testable without an `AppHandle`. A skipped bus wake is not lost — the
coordinator re-delivers on its next sweep — whereas one admitted mid-teardown
has its turn reset out from under it. `spawn_acp_turn` checks too, as defence in
depth: it is the single gate every ACP turn passes.

Verification
- `cargo test`: 1522 lib passed, all integration suites, 0 failed
- `pnpm test` 155 passed; `git diff --check` clean

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 79aacbdc61

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src/lib/resume.ts
Comment thread src-tauri/src/acp/permission.rs Outdated
Comment thread src-tauri/src/acp/permission.rs
Both fixes are on the sparse-request shape ACP permits: no `rawInput`, no
`locations`, the action described only in `title`/`content`.

- Empty card body (P1). The detail builder read `rawInput` and paths only, so
  such a request produced an EMPTY body — leaving the human to blind-approve or
  refuse, and both are product failures. `content` is used as a fallback (not an
  addition: for an ordinary request it would restate `rawInput` as `{"command":
  "ls"}` plus `$ ls`), so ordinary cards are unchanged.

  This one is provable from inside the module rather than from an agent that
  emits the shape: `grant_identity` ALREADY treats `content` as action-bearing,
  which it must, or two different content-only actions share one Always grant.
  The two halves of the authorization decision have to agree on what the action
  is — one cannot key grants on a field the other refuses to display.

- Untranslatable title (P2). An absent title defaulted to the literal `"tool"`,
  which is non-empty, so the branch that emits `acp.permission_required` — the
  token `ConfirmationCard` maps through both catalogs — was unreachable. Every
  locale showed `tool`. The catalog entry and the mapping already existed; only
  the placeholder stood in the way.

Scoped deliberately: the freeze-marker ordering and the PowerShell resume
quoting from the same review round are follow-ups. The first needs a DB write to
fail (where the user's problem is not the rewind anchor), and the second is for
Windows, which `release.yml` does not build.

Verification
- `cargo test`: 1524 lib passed, all integration suites, 0 failed
- `pnpm build` clean; `pnpm test` 155 passed; `git diff --check` clean
- Mutation-tested: ignoring `content`, restoring the `"tool"` literal, and
  appending `content` instead of falling back to it all go RED

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b9b20fab42

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/acp/runtime.rs
Comment thread src-tauri/src/lead_chat/engine.rs
Comment thread src-tauri/src/acp/runtime.rs
petros-double-test1 and others added 2 commits July 28, 2026 16:02
`interrupt()` sets `interrupting` WITHOUT bumping `reset_epoch` — deliberately,
so a queued message survives interrupting the current turn — and it cancels
whatever prompt is live at that moment. A Stop landing after `on_turn_end` has
promoted a queued item but before it is dispatched therefore cancels NOTHING:
the old prompt has ended, the new one has not started. The dispatch guard
checked only the epoch, which still matched, so the queued prompt ran anyway —
executing tools — until it finished on its own or the 8s forced reset fired.

Stop not stopping is the one behaviour a user cannot be asked to tolerate, so
this is fixed rather than deferred with the other two findings from this round.

The guard is now a named predicate covering every admission condition
(`stopped`, `interrupting`, `tearing_down`, epoch), matching the other three
admission points rather than being a bespoke epoch comparison — the drift
between them is what produced this gap.

The two connection-lifecycle findings from the same round (untracked in-flight
handles at exit, frames not fenced to their connection generation) are recorded
as follow-ups: both need connection-layer rework, both have narrow triggers, and
that layer has produced the most regressions across these rounds.

Verification
- `cargo test`: 1525 lib passed, all integration suites, 0 failed
- `pnpm test` 155 passed; `git diff --check` clean
- Mutation-tested: reducing the guard back to the epoch comparison goes RED

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`rust-test (ubuntu-latest)` failed on this branch with a `Broken pipe` panic at
`hook_test_support.rs:51` in `codex_hook_ignores_a_non_loopback_route_without
_posting`, while macOS passed and 30 consecutive local runs passed.

Not a flake in the environment — a race the test drives itself. Several of these
hooks exit WITHOUT reading stdin, and that one is named for exactly that
behaviour: a non-loopback route returns without posting. So `write_all(...)
.unwrap()` races a legitimate early exit, and loses on a loaded runner.

The write is now allowed to fail. This cannot mask a real defect: if a script
that SHOULD consume stdin stops doing so, it never receives the payload, and the
stdout and exit-code assertions below — which are what every one of these tests
actually checks — fail as they should.

The line predates this branch (PR #141); this PR does not otherwise touch the
file. Fixed here rather than separately because it blocks this merge and the
cause was diagnosable from the failure.

Verification: `cargo test` 1525 lib passed, all integration suites, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@SoloJiang
SoloJiang merged commit 2f8db96 into main Jul 28, 2026
@SoloJiang
SoloJiang deleted the feat/omp-acp-runtime branch July 28, 2026 08:24

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 19221ec3a5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let thread_id = eng.lock().await.thread_id;
let session_id = eng.lock().await.session_id;
mark_queued_delivered(&app, &db, thread_id, session_id, &msg).await;
match client.prompt(&sid, &msg.text, &msg.images).await {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Serialize queued prompt admission with Stop

When Stop lands after the admissibility check releases the engine lock but before this RPC is enqueued, interrupt() sends session/cancel while no prompt is active and this queued prompt then starts anyway, allowing tools to run until completion or the delayed force reset. Fresh evidence after the earlier fix is that the new predicate is released before both mark_queued_delivered and client.prompt, so it remains a TOCTOU guard; serialize prompt arming with Stop or reissue cancellation after the request is registered.

AGENTS.md reference: AGENTS.md:L58-L58

Useful? React with 👍 / 👎.

Comment on lines +5461 to +5464
if let Err(err) = clear_native_id(&db, session_id, thread_id).await {
eprintln!(
"[weft] acp force reset: failed to clear native id for thread {thread_id}: {err}"
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep force-reset mirrors consistent on clear failure

When this database write fails, such as on a locked or full SQLite database, the force-reset path has already removed the in-memory native ID but merely logs the failure and makes the engine sendable again. The database therefore still names the abandoned session; a restart before a successful new-session persistence resumes that wedged session despite the user's forced reset. Keep teardown reserved until the clear succeeds, or preserve a retryable tombstone so the durable ID cannot remain stale.

AGENTS.md reference: AGENTS.md:L58-L58

Useful? React with 👍 / 👎.

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.

2 participants