fix(shell): reconcile task updates by origin - #432
Conversation
7f7e1b7 to
b5b6f41
Compare
b5b6f41 to
a192092
Compare
asto18089
left a comment
There was a problem hiding this comment.
Independent re-review
Audited against latest main (6195d704a, which is exactly this branch's merge-base — no rebase needed), including the stacked phase-1 delta (a445e02b6) and the CodeWhale candidate 5a5bf363e (fork PR #40). Verified: the root cause on main (assign_unclaimed_tasks binding purely by job.command == command; worse, a completed foreground job lingers in list_jobs for its retention window, so a later same-command tool replays the entire historical buffer as one delta onto the wrong card); the four-layer identity chain (engine plan.id → forwarder Event::ToolCallStarted.id → host MonitorState key → JS card toolId); all three engine dispatch stamps incl. approval retry and parallel batch; the legacy fallback predicate being byte-identical to main for origin-less jobs (walked every origin-less producer: verifier gates, stale remember_job, serde-restored payloads — all behave exactly as main); serde default/skip_serializing_if compatibility; TUI display unchanged; zero unrelated changes in either phase. Local gates green: fork-guard.sh --fast, architecture-guard.py, node --test tests/shell_task_projection.test.mjs (7/7), python -m unittest scripts.tests.test_ci_gate_policy (20/20); cargo clippy --lib --tests -D warnings clean; targeted engine tests pass. The fast-gate failure here is the declared tag-publication dependency, not a defect.
The fix is real, meaningful, and elegant. Requesting changes on one behavioral decision and one register-accuracy fix:
1. MAJOR — the phase-2 tail-suppression guard also hides running root jobs (needs author decision)
pinvou3-app/src/platform/tauri/bridge/terminal.js:159, mirrored at pinvou3-app/src/platform/web/bridge.js:4435:
if (!item && job.origin_tool_call_id && !job.owner_agent_id) return;Two problems:
(a) The comment's premise doesn't hold. "The job belongs elsewhere" is not possible: list_shell_tasks is queried per session (src-tauri/src/app/commands/runtime.rs:109-113) and applyShellSnapshots only ever receives this session's jobs. An unmatched job always belongs to this session; its origin card is missing because the card left the loaded transcript (compaction rewrote the persisted history, then reload), not because it lives elsewhere.
(b) Unlike the phase-1 guard at terminal.js:154, this one also suppresses running snapshots. Concrete loss: a root background job outlives a compaction that rewrote the persisted transcript; after a webview reload (Tauri keeps the Rust process, jobs and monitor, alive) or a page reload on the web bridge, re-hydration cannot restore the origin card, and the still-running job becomes invisible — the monitor emission handlers (chat:tool_delta → appendToolItemOutput, chat:shell_task_status → finishBackgroundToolItem) only ever update existing cards and never append. On main it got a live-updating synthetic card. A running job is never a "stale" card.
If suppressing running identified jobs is intentional position-purity, the comment should state that tradeoff; otherwise exempting running — if (!item && !running && job.origin_tool_call_id && !job.owner_agent_id) return; — restores main's visibility at zero stale-card cost. Note this flips the harness test identified root jobs without a loaded origin card are never appended at the tail, which currently pins running suppression, so this is a genuine design decision rather than a mechanical fix. Either resolution works; please pick one.
2. MAJOR (register accuracy) — guard inventory undercounts the r14 candidate
Both docs/fork-modifications.md (守护行, and the "r14 候选守护" bullet at line 162) and docs/fork-modifications.en.md say the candidate adds "one Shell-origin behavior test". Measured fn forkguard_ count: 63 → 65, because (1) background_shell_job_carries_subagent_owner is promoted/renamed to forkguard_background_shell_job_preserves_origin_identity, and (2) a second new test forkguard_tool_context_for_call_preserves_turn_and_sets_call_origin lands in crates/tui/src/core/engine/turn_loop.rs — that one is absent from both docs' claims and from the T2 guard enumeration. The register is the declared single source of truth; please correct the count and enumerate the turn_loop test in both languages.
Non-blocking notes
scripts/fork-guard.shlayer 0 now dual-accepts the r13 head and the candidate, but the fingerprint layer unconditionally requires candidate-only content, so an r13 checkout passes layer 0 and fails layer 1. The #370 precedent replaced the accepted head outright (single accept). Harmless post-merge, but the script advertises a state it can never fully validate.- The r14-candidate bullet is filed under the "### r12 厂商原生搜索…" heading in both register docs, and the drift row records no candidate numbers (measured: 7 files, +133/−7).
- Optional test gap: mixed precedence in
assign_unclaimed_tasks(one tool with no origin match + snapshots containing both a foreign-origin and a None-origin job of the same command) is unpinned; two small tests would catch a future "relax the fallback to any job" regression. - Merge-order constraint is correctly declared and enforced by the gate: CodeWhale #40 → immutable tag
pinjou-v0.9.5-r14→ this PR. Merging out of order would redden every open PR's fast-gate.
Signed-off-by: zhuowp <zwp-1989@qq.com>
The guard re-evaluated the latest shell card for every snapshot, so a running job's synthetic card created earlier in the same poll (the manager lists running jobs first) disarmed it for the completed jobs after it. Decide once from the pre-poll timeline instead, document the accepted limits until origin identity lands, and pin the guard, the web helper's scan predicate, and both documented limit scenarios in tests. Signed-off-by: asto <asto18089@126.com>
a192092 to
4f7bbb7
Compare
|
Addressed the review findings and updated the stack on top of the current #431 head (
Validation:
Please re-review the updated head. |
The stale shell card guard recognized wait observers by tool name only. Since engine v0.9.3 the exec_shell_wait/exec_wait names survive only in replayed legacy sessions and the canonical wait observer is the Bash tool with action="wait" (the engine's own timeout-recovery hint prefers it), so the guard never engaged on the primary path and a retained completed job was still appended below the newer wait result. Recognize the action on the card args (present both live and after history replay) in both bridges, cover it with regression and start-tool boundary tests, and lock the web parity clause. Signed-off-by: asto <asto18089@126.com>
Signed-off-by: zhuowp <zwp-1989@qq.com>
Signed-off-by: zhuowp <zwp-1989@qq.com>
- enumerate the r14 candidate commit in the English register's T2 list, matching the authoritative Chinese register - make the subagent-named projection test actually set owner_agent_id and pin the owned-job visibility branch beside the root-job suppression test - refresh the stale "until stable origin identity lands" limits comment in both bridges: origin identity lands in this same change Signed-off-by: asto18089 <asto18089@126.com>
4f7bbb7 to
2bcdfb7
Compare
Second-round re-review — prior findings resolved, branch re-stacked, small fixes pushedRe-audited the updated head against latest Prior MAJOR findings — both verified fixed
Re-verified independently: the four-layer origin chain, the legacy fallback predicate remaining filter-for-filter identical to Re-stack onto the current #431 headThis branch was still stacked on the old Fixes pushed in
|
Summary
Dependencies and stacking
pinvou-v0.9.5-r14This PR targets
mainas requested. Until #431 merges, its comparison also contains the phase-1 commit; after #431 merges, GitHub will automatically reduce this PR to the phase-2 delta.Compatibility
The new origin fields are optional for existing serialized jobs. Older runtimes continue through the command-match fallback, but an explicit different origin is never rebound by matching command text. No revision counter or ambiguous generic tool-result metadata is introduced.
Verification
node --test tests/shell_task_projection.test.mjs(10 passed)python -m unittest scripts.tests.test_ci_gate_policy(20 passed)python scripts/architecture-guard.py./scripts/fork-guard.sh --fastcargo check -p codewhale-tui --lib --lockedgit diff --check