Repository navigation
Conversation
The restore lane already prefers the provenance-stamped carrier (#76, #88), but the rest of the recognition family still ran the loose predicate `is_compaction_checkpoint_message`, which matches any user turn whose text merely starts with the summary header. In a history that carries the stamped carrier that misread has four effects (Pinvou/pinvou-agent#594): recompaction anchors the last round earlier and `bound_last_round` filters the pasted turn out of the replacement — verbatim loss the coverage floor cannot catch, because it classifies user prompts through the same loose predicate; the survival contract counts the retained turn as a duplicated summary, so fixing the drop alone would have made every such replacement fail closed; `/edit` classifies the turn engine-owned and truncates at an older message; and retention never carries the turn verbatim. The restore-time topology repair located a sidecar's summary with the same loose scan, anchoring the sidecar to a pasted turn instead of its summary. Recognition is now era-scoped through `history_recognition::CheckpointEra`: a history that contains a stamped carrier postdates stamping, so only the stamped form is its checkpoint and a pasted bare-header turn is user content in recompaction slicing, replacement retention and bounding, coverage and receipt validation, `/edit` targeting, anchor placement, and restore repair. Histories without a stamped carrier predate the stamp — there the pasted header is indistinguishable from the saved bare carrier, so the loose form keeps applying; the legacy behavior is now pinned explicitly in the Rust tests and the fixture matrix. The receipt count reads the replacement in its own era, so a modern replacement that retains a pasted turn still validates with exactly one receipt. The dead `compaction::is_compaction_summary_text` (zero callers repo-wide) is deleted along with the stale "out of scope here" doc sentences. Verified: cargo fmt --check; clippy -p codewhale-tui --lib clean (the floating stable 1.99 chunks_exact/eprintln/redundant-closure lints fire on the pristine base test targets too); cargo test -p codewhale-tui --lib 12025 passed / 0 failed under RUST_MIN_STACK=16777216 (one unrelated remote_control test flaked once under parallel load and passes in isolation); mutation check — collapsing is_checkpoint to the loose form turns exactly the five new pins red (pasted-turn recompaction survival, retention, /edit targeting, sidecar anchor, fixture matrix) and leaves the pre-existing suite green. Pinvou/pinvou-agent#594 Signed-off-by: asto <asto18089@126.com>
The era-scoping round left two classifications outside CheckpointEra and documented the signal too strongly: - `replace_agent_topology_checkpoint` detected a trailing compaction summary with the stamped predicate only, so a pre-provenance history ending in its bare carrier appended the topology sidecar straight after the summary — the `user → user` shape the placement fix exists to prevent. The check now reads the history's era, and the anchor lookup shares one derivation (`compaction_anchor_in_era`). - The survival receipt floor read the replacement in its own era, so a hand-built stampless replacement of a post-provenance history passed vacuously: the retained pasted turn counted loosely as the missing receipt. A post-provenance original now demands the stamped carrier — the engine stamps every replacement it builds, and the stamp is the only form request rewriting relocates. - Docs: the era signal is the history's content, not the session's age — one-way and destructible (`/edit` or a host rewind can truncate away the only stamped carrier), foreign imports stay legacy, and typed input cannot forge the two-block form. SURVIVAL_CONTRACT.md states the raised receipt bar and stops attributing recompaction/retention//edit survival to the receipt count alone; `edit_last_turn_target` discloses the truncate-away-the-carrier boundary. Pins: the raised receipt floor, era-aware round slicing and kept-count, the legacy bare-carrier sidecar anchor, and the era-aware turn indices; the /edit pin joins the forkguard_ convention. Mutation check — collapsing `is_checkpoint` to the loose form turns exactly the era-dependent pins red and leaves the legacy boundary pin green. Pinvou/pinvou-agent#594 Signed-off-by: asto <asto18089@126.com>
…cript validate_survival_contract.mjs read the same fixture matrix with a substring-only checkpoint check, so the modern pasted-header cases the previous commit added broke it: the stamped carrier's text was demanded as surviving user content and the kept case failed closed. The script now mirrors the Rust shapes structurally — loose is a header-prefixed first text block with an empty or provenance-marker rest, stamped is exactly [text, marker] — derives the era from the history, scopes plain-user coverage and receipts through it, and raises the receipt bar for post-provenance originals like `validate_survival_contract`. All 11 fixtures pass again under `node validate_survival_contract.mjs`. Pinvou/pinvou-agent#594 Signed-off-by: asto <asto18089@126.com>
The era model made the engine's history classifications era-aware but kept the machinery `pub(crate)`, so the host app's turn numbering and rewind cut points — which hold the full history in scope — could not follow and stayed on the legacy per-message predicate. Export `user_turn_prompt_indices`, the history-aware form of `is_user_turn_prompt`: one era derivation per history, indices of every editable user turn. Rewind numbering, turn counts, and `/edit` targeting can then read one list and stay consistent in post-provenance histories. `is_user_turn_prompt` stays for single-message callers with no history in sight. Pinvou/pinvou-agent#594 Signed-off-by: asto <asto18089@126.com>
c57b6a0 to
fc9dd2d
Compare
|
Fix round pushed after a fresh from-scratch review (8 subagent lanes over the whole recognition family + hand verification; zero P0/P1 found). Three should-fixes, the doc drift, and one host-side enabler, in three commits on a rebased branch (
Verification: |
R2 fresh review found the mirror validator's coverage loop witnesses replacement texts through a bare userTextOf, while the Rust floor witnesses through the era-scoped user_prompt_text_of on both sides (compaction/last_round.rs). The required-set side was era-scoped in 8c8e09d; the witness side was not, so a post-provenance rewrite could drop a last-round turn whose text is the bare marker sentence and still pass the mirror: the replacement carrier's joined text starts with exactly that marker. The matrix never exercised this, so the split was invisible. Witness through the same era-scoped plain-text predicate, and pin the rule with a new matrix case modern-dropped-turn-not-witnessed-by- a-carrier-text (expect fail): Rust already rejects it, the era-blind witness accepted it, and the scoped witness rejects it again. The header now also discloses that the mirror's plain-text classification stays scoped to the fixture matrix (it skips the rest of Rust's classify_user_turn_prompt_in_era conjuncts, which the fixtures never contain). Verified: node validate_survival_contract.mjs -> ok 12; a probe build with the era-blind witness restored fails exactly on the new case; cargo test -p codewhale-tui --lib fixture_matrix green. Signed-off-by: asto <asto18089@126.com>
R2 fresh review follow-ups, comment-only: - is_generated_compaction_checkpoint claimed a hand-forged stamp "flips recognition for that one message"; of_history keys off any stamped carrier, so the flip is history-wide: every other bare-header turn reclassifies as content, while the forged message itself reads as the checkpoint (deleted by restore, skipped by retention and bounding, unprotected by the coverage floor). Say that. - CheckpointEra covered foreign imports but not the spliced-mix case: a history holding both a genuine pre-stamp bare carrier and a stamped carrier reads post-provenance, so the old bare carrier reclassifies as user content (retained, editable, round-anchoring). - user_turn_prompt_indices promised consistency with what /edit targets without disclosing the Unsupported-latest-turn divergence: the list ends at the newest editable turn while /edit refuses at Unsupported. Point rewind cut points at edit_last_turn_target for that case. Signed-off-by: asto <asto18089@126.com>
JensenChen28
left a comment
There was a problem hiding this comment.
复核 exact head 26e94fd8。我沿 checkpoint 识别族逐项核对了 era 推导在 recompaction slicing、retention、coverage/receipt 校验、/edit target、topology anchor 与 restore repair 中的使用;现代 history 只识别 stamped carrier,legacy history 保持 loose 兼容,replacement receipt floor 也不会再被粘贴的裸 header 冒充。JS mirror 与 Rust 规则一致,未发现漏接的分类路径或新的数据丢失边界。
本地 node crates/tui/src/compaction/validate_survival_contract.mjs 返回 ok 12 survival-contract fixtures,git diff --check 通过;exact-head check、CodeQL、DCO、Gitleaks 均成功。批准。
JensenChen28
left a comment
There was a problem hiding this comment.
Reviewed the history-era changes against 3118c36 across round slicing, retention, survival validation, edit targeting, and topology-anchor placement. I found no blocking defect in those paths. The stamped-history rule preserves pasted headers while the documented legacy ambiguity remains explicit.
Independent local validation: all 12 survival-contract fixtures passed. A small Rust executable using the exact recognition module with minimal message types also passed legacy/stamped, pasted/quoted, mixed-content, role, and truncated-history cases. These focused checks are not a full Rust TUI integration run. All required checks pass for this head.
Parent integration note: Pinvou currently still uses the single-message is_user_turn_prompt predicate in features/code_checkpoints/turns.rs and features/sessions/rewind.rs. When adopting this foundation head, migrate history-wide numbering/counting to user_turn_prompt_indices and retain edit_last_turn_target for safe edit selection; exporting the new helper alone does not update those host callers.
Fixes the recognition-family residuals tracked in Pinvou/pinvou-agent#594 (follow-ups disclosed as out of scope in #74/#76 and again in #88). The parent issue closes through the pinvou-agent gitlink PR that pins the merge commit of this PR — the fix reaches pinvou-agent
mainonly then.Problem
is_compaction_checkpoint_messagematches any user turn whose text merely starts with the summary header — the boundary the restore lane accepted because pre-provenance saves committed the bare summary. Restore now prefers the provenance-stamped carrier, but the rest of the recognition family still ran the loose predicate, so in a history that carries a stamped carrier a user turn that pastes the full header is misread as a checkpoint:is_plain_user_textexcludes it, so the last-round slice anchors earlier andbound_last_roundfilters the turn out of the replacement;validate_last_round_coveragecannot catch it because it classifies user prompts through the same loose predicate.validate_survival_contractcounted receipts with the same loose predicate, so fixing (1) alone would have rejected every such replacement as "prior summaries were duplicated"./editskips the turn:is_runtime_owned_user_messageclassified it engine-owned, so the cut point landed on an older message.retained_user_messagesskipped it, so the turn survived only as summary prose.The restore-time topology repair (
restored_topology_anchor) located a sidecar's summary with the same loose scan — a pasted turn before the stamped summary anchored the sidecar a round too early.Fix
Recognition is era-scoped through a new
history_recognition::CheckpointEra, following the structural (not session-age) framing #88 introduced: a history that contains a provenance-stamped carrier postdates stamping, so only the stamped form is its checkpoint and a pasted bare-header turn is user content everywhere the family classifies — recompaction slicing (last_round_start/last_round_range), replacement retention (retained_user_messages) and bounding (bound_last_round), coverage validation, receipt counting,/edittargeting (edit_last_turn_target), anchor placement (compaction_anchor), and restore repair. Histories without a stamped carrier predate the stamp, where a pasted header is indistinguishable from the saved bare carrier, so the loose form keeps applying — legacy recognition is unchanged and now pinned explicitly.The survival receipt count reads the replacement in its own era: a modern replacement that retains a pasted turn still validates with exactly one stamped receipt, while hand-built/legacy replacements keep the loose count the fixture matrix pins. The dead
compaction::is_compaction_summary_text(zero callers repo-wide) is deleted along with the stale "explicitly out of scope here" doc sentences, andSURVIVAL_CONTRACT.mdstates the era rule.Deliberately unchanged: request-time wire relocation (
client/chat.rs) already reads only the stamped form;restore_compaction_checkpoint's prefer-stamped-then-loose carrier swap (its own documented boundary); the loose existence checks (inspect_compaction_keep,last_round_kept_count), where a stamped carrier also matches the loose form so both eras answer identically.Verification
cargo fmt --check;clippy -p codewhale-tui --libclean. (Note: the floating stable 1.99 toolchain now fireschunks_exact_to_as_chunks/print_stderr/redundant_closureon the pristine base test targets (shell_dispatcher.rs:762,core/engine/tests.rs:20141,:25056) — pre-existing, untouched here.)cargo test -p codewhale-tui --lib: 12025 passed / 0 failed (underRUST_MIN_STACK=16777216; one unrelatedremote_controltest flaked once under parallel load and passes in isolation).CheckpointEra::is_checkpointto the loose form turns exactly the five new pins red and leaves the pre-existing suite green:forkguard_pasted_header_turn_survives_recompaction_post_provenance(residual 1+2)legacy_history_still_replaces_a_bare_header_turn(legacy boundary stays put under the fix; red only if someone tightens legacy)forkguard_retention_carries_a_pasted_header_turn_only_post_provenance(residual 4)edit_target_reads_a_pasted_header_turn_post_provenance(residual 3)forkguard_restore_anchors_a_trailing_sidecar_to_the_stamped_summary_not_a_pasted_turn(restore repair) + the two new language-invariant fixture casesmodern-pasted-header-turn-kept/modern-pasted-header-turn-dropped.Pinvou/pinvou-agent#594
No-Issue: fixes Pinvou/pinvou-agent#594, which lives in the parent repo; the gitlink PR there carries the closing reference.
Fix round (review follow-ups, commits b589114 / 8c8e09d / fc9dd2d)
Fresh review found no P0/P1. Three should-fixes and the doc drift are fixed on top, rebased onto
pinvou3-clean@ 3118c36:replace_agent_topology_checkpointdetected a trailing summary stamped-only, so a pre-provenance history ending in its bare carrier appended the topology sidecaruser → userright behind it. The check now reads the era (compaction_anchor_in_erashares one derivation); a post-provenance history keeps appending after a pasted-header turn, which is the latest real user input.validate_survival_contract.mjscarried a substring-only checkpoint check, so the new modern matrix cases broke it (base: 9 ok → head: exit 1). It now mirrors the loose/stamped block shapes and the era, including the raised receipt bar:ok 11 survival-contract fixtures.user_turn_prompt_indices(history-aware form ofis_user_turn_prompt) is re-exported so host turn numbering / rewind cut points can share the era-aware predicate instead of the legacy single-message one./editor a host rewind can truncate away the only stamped carrier), foreign imports stay legacy, typed input cannot forge the two-block form.SURVIVAL_CONTRACT.mdstates the raised receipt bar and correct attribution;edit_last_turn_targetdiscloses the truncate-away-the-carrier boundary.Verification:
cargo fmt --checkclean;clippy -p codewhale-tui --lib0 warnings;cargo test -p codewhale-tui --lib12100 passed / 0 failed (RUST_MIN_STACK=16777216); mutation re-check — collapsingis_checkpointto the loose form turns exactly the era-dependent pins red (now 9, including the new slicing/sidecar/indices pins and the raised receipt floor via the duplicated-summary path) and leaveslegacy_history_still_replaces_a_bare_header_turngreen.R2 fresh-review round (commits a6dbb6f / 26e94fd)
A second from-scratch review (11 subagents across completeness, era semantics, receipt-floor writers, mirror fidelity, claim-vs-issue fidelity, per-module correctness, smuggling, performance, test adequacy, and an adversarial red-team pass) confirmed the fix round: zero P0/P1. Two commits land its findings:
validate_survival_contract.mjswitnessed replacement texts through a bareuserTextOf, while the Rust floor witnesses through the era-scopeduser_prompt_text_ofon both sides — the required-set side was era-scoped, the witness side was not, so a post-provenance rewrite could drop a last-round turn whose text is the bare marker sentence and still pass the mirror (the carrier's joined text starts with exactly that marker). The witness now reads through the same era-scoped plain-text predicate, and a new matrix casemodern-dropped-turn-not-witnessed-by-a-carrier-text(expect fail) pins the rule: Rust rejects it, the era-blind witness accepted it. The validator header also discloses that its plain-text classification stays scoped to the fixture matrix.node validate_survival_contract.mjs→ok 12 survival-contract fixtures.is_generated_compaction_checkpointsaid a hand-forged stamp "flips recognition for that one message" — the flip is history-wide (of_historykeys off any stamped carrier);CheckpointEranow also covers the spliced-mix case (genuine bare carrier + stamped carrier reads post-provenance, the bare carrier reclassifies as content);user_turn_prompt_indicesnow discloses the Unsupported-latest-turn divergence fromedit_last_turn_target(the list ends at the newest editable turn while/editrefuses atUnsupported).Verification:
cargo fmt --checkclean;clippy -p codewhale-tui --lib0 warnings;cargo test -p codewhale-tui --lib12100 passed / 0 failed (RUST_MIN_STACK=16777216); mutation re-check from the R2 review — collapsingCheckpointEra::is_checkpointto the loose form turns exactly the 9 era-dependent pins red and leaveslegacy_history_still_replaces_a_bare_header_turngreen.