Skip to content

build: re-fork Pinvou on v0.9.12 - #44

Merged
h3c-hexin merged 0 commit into
pinvou3-cleanfrom
codex/pinvou-v0.9.12-r1
Sep 9, 2026
Merged

h3c-hexin merged 0 commit into
pinvou3-cleanfrom
codex/pinvou-v0.9.12-r1

Conversation

@h3c-hexin

@h3c-hexin h3c-hexin commented Sep 8, 2026 •

Copy link
Copy Markdown

Summary

Re-fork Pinvou's maintained CodeWhale foundation directly from upstream v0.9.12 and preserve only the four host lifecycle topics that remain necessary.

The merged head reduces the old v0.9.5 r13 fork from 110 changed files and +10895/-1195 to 72 files and +4848/-637, while retaining result-oriented regression coverage.

Changes

  • Rebuild the narrow host facade, route limits, session ownership, reliable steering, and child cancellation boundaries.
  • Preserve host tools, MCP secret resolution, per-turn and final-dispatch security, non-bypassable approvals, redaction, and the 64 KiB File write limit.
  • Restore host-owned static prompt composition, explicit Skill-root isolation, and bounded instruction/working-set behavior.
  • Preserve Automation conversation ownership, v3 writer/v4 legacy-reader compatibility, no-backfill/no-overlap scheduling, thread creation, and terminal cleanup.
  • Fold current Rust release-lint compatibility into the final topic commit without changing the public prompt-composer signature or adding a new runtime behavior topic.
  • Restore the reachable keyless Bing tail for configured API search chains and remove the empty benchmark-observability feature before merge.

Testing

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features --locked with the documented CI allow list — warning-free
  • RUST_MIN_STACK=16777216 cargo test --workspace --all-features --locked -- --test-threads=1 — all crate, integration, PTY/Cucumber, and doc tests passed
  • cargo check --workspace --all-targets --locked
  • cargo test -p codewhale-protocol --test parity_protocol --locked — 17 passed
  • cargo test -p codewhale-state --test parity_state --locked — 6 passed
  • ./scripts/release/check-ohos-deps.sh
  • 36 Pinvou forkguard_* behavior tests passed: 30 default plus 6 feature-gated benchmark controls
  • Incremental Gitleaks scan across v0.9.12..HEAD found no leaks

Impact and notes

  • The merged head is anchored to the exact upstream v0.9.12 commit dcd4c200f72f0c1ffd60d8e7f6850313db879fc5 and contains eight DCO-signed Pinvou commits above it.
  • This PR established the reviewed clean re-fork at ff299f94b0795180c76d0336152385dbd02dfa05. Exact-head release-gate fixes and final protected-ref/tag publication are tracked in follow-up PR fix(ci): close exact-head release gates #46.
  • Linux ARM64 and OHOS dependency contracts were verified locally. Native Windows/macOS coverage remains CI-bound.
  • No live L1/vLLM endpoint or credential was available for the opt-in real-model suite.

Checklist

  • This PR adds a new layer/module/abstraction — it names or deletes the layer it replaces
  • Updated docs or comments as needed
  • Added or updated tests where relevant
  • Verified TUI behavior manually if UI changes — no interactive TUI UI behavior is changed by the Pinvou fork topics
  • Harvested/co-authored credit uses a GitHub numeric noreply address — not applicable; no harvested/co-authored commits

if drift:
print(f"limit drift detected ({len(drift)}):")
for path in drift:
print(f" - {path}")
if drift:
print(f"limit drift detected ({len(drift)}):")
for path in drift:
print(f" - {path}")

@github-advanced-security github-advanced-security AI 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.

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@h3c-hexin

Copy link
Copy Markdown
Author

Multi-agent review: foundation-side findings

Reviewed v0.9.12..b4c02616 (5 commits, 62 files, +3127/−614) against the old r13 fork (pinvou-v0.9.5-r13, 110 files, +10895/−1195) and both upstream baselines. Parent-side findings are cross-posted on Pinvou/pinvou-agent#453.

Overall: this is a genuine rewrite on v0.9.12 semantics, not a mechanical transplant — steering is rebuilt on the upstream mailbox with epoch isolation, prompt ownership is woven into the new StaticPromptCtx/fragment architecture, Automation gains session-scoped conversation keys. Deviations from r13 are consistently stricter (Full Access re-blocks non-bypassable registered tools, ambient prompt channels closed, ambient fleet roster removed). No exploitable approval bypass, permission escape, secret leak, or cancellation/cleanup bug was found: exact-dispatch is enforced at every dispatch seam with forged-backend fail-closed tests, redaction is a whitelist projection, the steering register re-checks after the permit await (TOCTOU closed), and ExactToolDispatchPolicy is non-serializable so the wire can't mint authority.

🔴 Blocker — #32 eval-control semantics are gone without a clean disposal (cross-repo decision needed)

  • benchmark-observability / benchmark-eval-controls remain only as empty feature declarations (crates/tui/Cargo.toml); cfg(feature = ...) consumers number zero. with_final_only_after_tool_budget, with_missing_read_action_repair, repair_benchmark_read_call, fold_benchmark_alias_group, normalize_benchmark_u64 do not exist in b4c02616.
  • Upstream's ToolCallBudget is not an equivalent: it rejects over-budget calls with a typed error; it does not remove the tool face from subsequent provider requests (final-only), and read-action repair has no upstream counterpart.
  • The parent's benchmark-hooks feature still calls the deleted APIs, so that build is broken (details on OPENCODE: Auto-import MCP/subagent/hook configs from Claude Code, Cursor, Codex Hmbown/Codewhale#453), and the parent register's claim that eval isolation "remains in the parent benchmark-hooks" overstates what actually remains.
  • The 6 r13 forkguard_benchmark_* behavior tests were deleted with no replacement.

Please either re-implement the control semantics here (with tests + fingerprints restored), or record a deliberate drop in the register and fix the parent's dangling calls accordingly.

🟠 Major — safety claims with zero test coverage

  • 64 KiB File write limit: enforced at both write paths (tools/file.rs, stricter than r13's single point) but there is no test anywhere in the tree (r13 had forkguard_file_content_caps_reject_before_writing). The parent's --fast guard only greps the constant — it would not catch a refactor that bypasses the call sites.
  • Bulk cancel (cancel_all_running_for_session): the session-scoped semantics are an improvement over r13, but zero tests (r13 had an idempotency test asserting cancel_all_running() == 2 then == 0).
  • Terminal cleanup (delete_terminal_task "Refusing to delete active task", delete_terminal_run identity re-check): no direct or indirect tests.
  • The r13 restricted-turn idle-deferral tests and the 5 MCP-invisibility tests were deleted; the implementations appear present but unguarded.

Suggest restoring behavior tests for at least the write limit and bulk cancel before this becomes the protected baseline.

🟡 Minor

  • Commit 2 ("preserve v0.9.12 tool safety") adds the two dead feature flags — unrelated to its title. Delete them or wire them; don't leave shells.
  • SetDisallowedTools no longer hot-disconnects newly denied MCP servers (the pool API was removed upstream; call-level denial via tool_matches_any_rule still holds). Register this behavior change in fork-modifications.
  • fix(search): API 后端失败兜底从 DuckDuckGo 换成 Bing #35 fallback shape changed silently: the upstream chain tail is DuckDuckGo again (with an internal allow_bing_fallback → Bing), whereas r13's keyless tail was Bing directly. The default path is unaffected (the app injects Bing prefs), but the register should name this explicitly instead of the generic "旧产品搜索覆盖 → 不再移植" bucket — API-backend users on mainland networks now traverse a dead DDG hop first.
  • The upstream stuck-guard deletion (b39cf5650) is reasonable (no result digest, live-polling false kills) but isn't listed in the disposal table; in-flight work building env knobs on top of the old guard needs coordination.
  • The write tool description lost the "Recommended at most 32KB; hard limit 64KB per call" hint that r13 carried — models now hit the wall blind.
  • Commit 5 mixes lint/clippy churn into a safety-titled commit and all five commit bodies are empty; as the future protected-baseline history this raises bisect/audit cost.
  • The CodeQL "Clear-text logging of sensitive data" alert on scripts/catalog_models_dev.py is upstream-inherited (the fork doesn't touch the file); document the attribution rather than fixing it here.

✅ Verified preserved (sampled with evidence)

Narrow facade + route limits + wire/embedding aliases; reliable steering (opaque ids, committed/dropped, withdraw outcome, SteerDropGuard); MCP secret resolver (env reads only via host_env_var, OAuth headers covered, error messages carry names only); per-turn security (exact-dispatch final re-check, read-only dispatch, restricted-turn control-plane latch — a superset of r13 including CancelSubAgents); 64 KiB write limit (implementation); static prompt composition + ambient sealing + explicit Skills root isolation; Automation ownership (no-backfill/no-overlap, conversation keys, terminal pruning); task schema v2/v3/v4 read compatibility with v5+ fail-closed (old user data remains readable after upgrade); native search from r12 #33 — fully upstreamed via Pinvou's own upstream merges (Hmbown#5682–Hmbown#5687), no loss.

@asto18089

Copy link
Copy Markdown
Collaborator

Independent review addendum: findings not in the earlier list

I independently reviewed v0.9.12..b4c02616 against the upstream tag commit and the r13 fork. The earlier foundation-side list holds up — the eval-control disposal, the zero-test safety surface, and all minors verified. This comment covers only what that list does not already cover.

Major

Upstream tests flipped/replaced without the fork-policy §3.4 annotation. The policy requires stating why an upstream test no longer holds when product semantics change it. At least three cases are silent:

  • crates/tui/src/core/engine/tests.rs: upstream full_access_auto_approves_non_bypassable_registered_tools (dcd4c200f, :11961) is renamed to full_access_blocks_non_bypassable_registered_tools_without_prompting (b4c02616, :12212) with the assertion inverted. The behavior itself is registered (T2); the test flip is not.
  • crates/tui/src/skills/tests.rs: upstream discover_for_workspace_and_dir_merges_workspace_and_configured_sources is gone; its coverage of the merge path is replaced by forkguard_explicit_skills_dir_excludes_ambient_workspace_sources (:1105), which asserts the opposite disposition.
  • crates/tui/src/prompts.rs: upstream system_prompt_merges_workspace_and_configured_skills_dir no longer exists in any form.

Minors

  1. crates/tui/src/lib.rs widens the facade instead of narrowing it. The diff flips 36 mod declarations to pub mod (artifacts, compaction, hooks, models, network_policy, session_manager, …). That is a wall, not the "necessary and narrow facade" the register claims, and it enlarges every future sync surface. Prefer an explicit re-export list.

  2. Steer lifecycle tests dropped a test class. The two new forkguard_steer_* tests (core/engine/tests.rs:6313, :6339) are solid pure-SteerControlState state-machine unit tests, but nothing ties the state machine to tx_steer / the engine loop; r13 verified steering at engine level through the loop. Acceptable as a stopgap, but it is coverage degradation worth registering.

  3. Commit 5 is a mega-commit, not just lint churn. It carries 50 files, +2491 of the +3127 total (~80%), including nearly all substantive T1–T4 behavior, so bisect value across the five commits is near zero. Also, the ExtraTools compile-compat shims (exec_agent.rs, runtime_threads.rs, tui/ui/frame.rs) belong with commit 1, which introduces the field. Since the branch may be rewritten before the protected-baseline transition, this is the moment to split the lint folding out and add commit bodies.

  4. The Permissions fragment exception should be on the §9 upstreaming list. model_context/fragment.rs:166-182 lifts the upstream 10K-token cap for FragmentId::Permissions to the 100 KiB product contract. The deviation is registered (§6), but nothing marks it as upstream-candidate debt.

  5. Cross-ref for MIN-c (search chain tail): the parent repo's pinvou3-app/src-tauri/src/platform/prefs/search.rs:14-16 still claims the foundation-side API-backend fallback is Bing — posted on pinvou-agent#453 as well. Whichever side moves, the two must not disagree.

@asto18089 asto18089 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review: request changes

Consolidated multi-agent deep review of v0.9.12..b4c02616. Ground truth verified independently: anchor = dcd4c200 (= v0.9.12), 5 linear commits above it, 62 files +3127/−614, only crates/ touched, no merge commits, no lockfile/CI/submodule changes, no secrets in the delta. The rewrite is genuine (not a transplant), the four retained topics are real, and no smuggled scope was found in any of the 62 files. I am requesting changes for the disposal gaps, the coverage collapse, and the transition issues below. This confirms/extends the two earlier comments where noted and adds new findings; the first two items now carry compile-level evidence.

🔴 Blockers

B1 — #32 eval-control disposal is incomplete, and the register is false about it.

  • benchmark-observability / benchmark-eval-controls remain empty feature shells with zero cfg consumers; all six forkguard_benchmark_* tests are gone.
  • The parent's benchmark-hooks build does not compile against this head: bridge.rs:2363-2366 calls with_final_only_after_tool_budget() / with_missing_read_action_repair(), and TurnToolSecurityPolicy here has only new / with_read_only_dispatch / with_trusted_hooks (core/ops.rs:73-102). Verified by an actual cargo check --features benchmark-hooks of the Hmbown#453 tree with the submodule pinned to b4c02616b: 2× E0599. PR Hmbown#453 does not touch those calls.
  • The register row “评测隔离留在父仓 benchmark-hooks” is factually wrong — nothing named eval isolation survives on either side (and Hmbown#453 simultaneously deletes the only CI lanes that compiled that feature).
  • Either re-implement the control semantics here (tests + fingerprints), or record a deliberate drop in the register and remove the dangling parent calls in Hmbown#453 in the same pairing.

B2 — #35 keyless chain tail: the loss is worse than registered.

  • The v0.9.12 tail is DuckDuckGo (web/backend.rs:119-122); Bing exists only as an internal fallback that fires after a successful HTTP response (bot challenge / zero parseable results, web_search.rs:1424-1490). A mainland network-level DDG failure (DNS poisoning, SNI reset) errors at .send() and never reaches Bing.
  • Net effect: Metaso/Bocha/Baidu API-backend users lose keyless fallback entirely on mainland networks. forkguard_api_provider_chain_tail_is_bing was deleted with no replacement; the register buries this in the generic “旧产品搜索覆盖” bucket; the parent's prefs/search.rs:16-21 comments are now doubly stale (the foundation default is also now Firecrawl).
  • Restore a Bing tail (or fall back to Bing on network-level errors), or register the semantics change honestly and fix the parent comments in Hmbown#453.

🟠 Must-fix before this becomes the protected baseline

M1 — Coverage collapse 63 → 18. The 18 kept tests are genuinely result-oriented, but the following surfaces are now implementation-untested (each had an r13 guard): 64 KiB write cap (both call sites), bulk cancel idempotency/session scope, terminal cleanup (delete_terminal_task refusal + delete_terminal_run identity re-check — r13 even asserted get_task fails afterwards), ExtraTools all-mode registration, restricted hooks opt-in (allow_hooks, zero refs), session trusted-roots override (zero refs), worker-boundary conversation-key transfer. fork-guard.sh --fast is grep-only and would not catch a refactor that bypasses the call sites. Restore at least: write cap, bulk cancel, terminal cleanup, ExtraTools registration, plus one end-to-end composer-sealing test (see M6).

M2 — Upstream test flips still lack the fork-policy §3.4 annotation. Confirmed for full_access_* (engine/tests.rs:12212) and the skills merge-path replacement (skills/tests.rs:1105). One factual correction to my earlier comment: system_prompt_merges_workspace_and_configured_skills_dir was renamed + inverted in place as system_prompt_uses_only_explicit_configured_skills_dir (prompts.rs:2019), not deleted — the annotation gap stands, the wording does not. Two benign renames into forkguard supersets (automation enqueue :2823, task schema :3556) deserve a line each for traceability.

M3 — [NEW] settle_steers_on_interrupt can silently drop accepted steer input (engine.rs:3778-3792). Whenever take_stopped returns any id, the channel is drained indiscriminately (while try_recv().is_ok() {}). Safe for StopDropInbox (all in-flight ids are in dropped), but under a prior unsettled StopDropInbox + an InterruptKeepInbox interrupt, current-generation registered steers sitting in the channel are discarded while their entries stay unsettled — the input is lost with no SteerCommitted/SteerDropped event, violating the PR's own no-silent-loss contract, and the unsettled entry leaks until session retire. Precedents are narrow but real (a cancel landing in the terminal tail after the loop's last iteration). Fix is mechanical: discard only ids present in dropped; re-queue the rest into pending_steers.

M4 — this PR alone does not close pinvou-agent#254. The shared bare CancellationToken slot remains (engine.rs:936, reset_cancel_token, cancel_with_mode with zero identity check); of the three autonomous self-start paths only goal continuation got a boundary re-check (engine.rs:2937-2944); idle-completion and shell-wake remain exposed. CodeWhale#38 (TurnCancelSlot + cancel_turn + publish_stop_disposition) therefore needs full re-authoring on this base — its merge-base with this branch is v0.9.5, with 7 touchpoints in engine.rs plus a handle.rs rewrite. Please state that explicitly in the register/transition plan so downstream (#38/Hmbown#408) doesn't plan a rebase.

M5 — facade: widened where it should be enumerated. 18 mod → pub mod (the earlier "36" counted diff lines). Every flipped module is consumed by the parent today, but pub mod core / pub mod mcp expose entire recursive APIs versus the ~100 symbols the host actually uses. The tui privatization + root AppMode/ApprovalMode re-exports is the right pattern — apply it to the other 17, or switch to an explicit re-export list. Note the privatization breaks the current parent tree (21 uses / 16 files of deepseek_tui::tui::…); Hmbown#453 migrates them, but the register only says “narrow AppMode/ApprovalMode API published to hosts” — name the import migration requirement explicitly.

M6 — ambient sealing has no drift guard. The six if static_composer_installed() guard clauses are scattered through the ~300-line compose function, so any upstream addition of a new ambient block defaults to unsealed with no compile-time signal, and no test asserts the sealed composition end-to-end (the renamed skills test sets the flag without a composer installed). One forkguard test — composer installed ⇒ prompt contains none of AGENTS.md / constitution.json / .cursorrules / pack / harness content — would pin the doctrine. Related: .codewhale/handoff.md relay (prompts.rs:1276-1278, fn :285-305) stays open under composer ownership; seal it or document the exemption.

🟡 Minor (new, non-blocking)

  • “Five signed Pinvou commits”: no cryptographic signatures exist (%G? = N ×5); only DCO trailers. Reword or sign.
  • Commit 5 measured: 50 files, +2491 ≈ 80% of all additions, pure lint ≈ 6–8%; all five bodies are subject + trailer only. The body's “release-lint compatibility folded into the final topic commit” materially understates it and per-commit bisect value is ~0. The protected-ref transition is the last cheap window to re-cut per-topic commits with bodies.
  • 64 KiB cap scope: File action=patch create_if_missing writes unbounded new files (apply_patch.rs:405, 621-642; parity with r13, edit uncapped by design) — state the cap's actual scope (write-tool content only) in the register.
  • authority.rs:449-462: the resolver's Allow branch for non-bypassable + auto-approve is now dead in production (the fork blocks earlier in turn_loop), but the doc still teaches the old behavior — a future caller would silently reintroduce auto-approve. Update or delete.
  • Fleet profiles: prompt profiles installed via Op::SetFleetRoster are silently ignored at spawn (subagent/mod.rs:9108, 12880 re-derive from session config — two sources of truth), and the profile prompt overlay is uncapped (:12950-12970) versus the 100 KiB discipline elsewhere.
  • Manual/agent-triggered automation runs bypass the no-overlap guard (run_now_with, automation_manager.rs:1719-1756; r13-inherited) — one register sentence.
  • EmbeddingHost reuses MessageId::StatusContextSourceConfigured (status.rs:378) — the status UI cannot distinguish a host-imposed limit from a user-configured one.
  • The r9/r13 turn-scoped foreground-shell kill on cancel has no foundation-level successor (only session-scoped kill_for_session); product behavior now rests solely on the parent's SessionTurnShellTasks. Register the disposal.
  • The rewritten fork-guard anchors no native-search fingerprint (old documented_server_side_web_search_for_route / is_exact_url_route anchors dropped with the upstreaming) — one line in the register.
  • r13 topic #28 cannot be classified from any ledger — needs an author note (kept/renamed/dropped).

✅ Verified good (sampled with evidence)

  • Scope hygiene: every one of the 62 files classifies into T1–T4 / mechanical v0.9.12 API adaptation / lint (including the 15 suspicious "new-only" files, checked hunk-by-hunk). Nothing to split out.
  • T2 security: no exploitable bypass found. Exact-dispatch enforced at a single chokepoint before lock acquisition; subagent, MCP-direct, interpreter, retry/fallback, ACP/HTTP seams all unreachable or gated while latched; policy non-serializable (wire cannot mint authority); wire projection is a compile-time-exhaustive whitelist under the wildcard-ban lint; redaction is whitelist projection with no leak path found; MCP secret resolver covers all five env sites incl. OAuth headers.
  • T4: conversation keys, no-backfill/no-overlap scheduling, and the v2/v3/v4 + v5-fail-closed matrix verified race-free including the rollback direction (new-written v3 readable by both r13 and upstream).
  • Native search (#33) upstreaming verified at symbol level, including the K3 180 s / 8-call budgets and exact four-way endpoint matching.
  • Steering TOCTOU re-check ordering is real (reserve_steer: register re-validates active_target after the permit await); child settlement is deterministic (TurnMailboxBarrier, pre-cancel park flag); shell process-group kill paths byte-equivalent to upstream.

Related PRs (scheduling notes, not part of this verdict)

  • Close as obsolete: #40 (fully upstreamed incl. its own test, renamed tool_context_for_call_preserves_turn_and_sets_call_origin), #34 (v0.9.12's encoding_rs decode loop emits the valid prefix + U+FFFD; legacy-decoder leg moot), #39 (applying it to this base would make the model-facing docs false — re-file only if B2 is resolved by restoring the Bing tail).
  • Rebase: #20 → #42 → #43 (low conflict; #43 is under active review).
  • Rewrite: #31 (notify fix already upstream; keep finance gating + verify/verifier wording), #37 (matcher ports, engine wiring collides semantically with upstream's exec_shell_ask_rule_decision_for_policy), #41 (fresh accuracy pass last).
  • Re-author #38 after #43/#37 settle (see M4).
  • Parent Hmbown#453 must fix before pairing: benchmark-hooks compile (B1 + forwarder.rs:1240-1260 TurnUsage → first_token_ms/request_ms), pinvou-cli/Cargo.lock regeneration, restore a benchmark-hooks compile lane (the upgrade PR deletes the only ones), search.rs comments, register additions (tui privatization migration, execute_with_options_env_for_owner → _and_session visibility, MCP hot-deny disposal, manual-trigger overlap caveat).

Happy to re-review on the follow-up push; the disposal-ledger items (B1/B2, the register corrections, and #28) are the critical path to the protected-baseline transition.

@asto18089 asto18089 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Comment from the parent-side review of Pinvou/pinvou-agent#453

Context: Hmbown#453 pins the CodeWhale gitlink at this PR's head b4c02616 and was reviewed in depth on the parent side. The fork work itself is high quality — 5 well-scoped commits, 18 forkguard_* behavior tests mapped 1:1 to features, and the security core is strengthened (non-serializable ExactToolDispatchPolicy prevents transcript-forged authority, single final-dispatch enforcement point, forged-backend-name tests).

Three things must be resolved in this PR before the parent can proceed:

  1. CodeQL check is failing with 1 high alert introduced by this PR ("2 configurations not found" warning + "New alerts: 1 high"). The parent PR's verification section does not currently disclose this. Please resolve or disprove the alert before merge.
  2. No human review yet — only advanced-security bot comments. Since this replaces the entire fork baseline, at least one approving human review should land here first.
  3. Publish pinvou-v0.9.12-r1 immediately after merge and complete the pinvou3-clean protected-branch transition (which needs an explicit maintainer decision — normal merge is impossible since the histories are disjoint). Until the tag exists, the parent's gitlink pins a SHA reachable only from a force-pushable branch, and the parent's verify-public-submodule gate is designed to require branch = tag = gitlink.

Non-blocking notes for the fork register (update here or in a fast-follow):

  • Plan mode also receives host extra-tools injection (append_host_extra_tools is called in the Plan branch too) — acceptable under the host contract, but record it explicitly in fork-modifications.
  • WriteFileTool gained a fork-specific 64 KiB single-write cap (WRITE_FILE_MAX_CONTENT_BYTES) — a behavior difference vs upstream worth documenting.
  • benchmark-eval-controls in crates/tui/Cargo.toml is now an empty feature shell (0 gated code sites, 0 tests) behind a stale comment — remove it with the benchmark topic cleanup.
  • Automation due-sweep changed list_runs(&id, Some(25)) → list_runs(&id, None) — O(all runs) file IO per sweep; fine under retention pruning, but worth keeping an eye on.
  • Note for downstream: the issue-Hmbown#254 turn-bound cancel chain (CW#38, candidate ef424c1d7 on the r13 line) has no disposition in the upgrade report or register; the r1 engine still swaps an identity-less shared cancel token (engine.rs:1543). Either argue closure or plan the re-port — this needs an explicit decision on the parent side.

@h3c-hexin

Copy link
Copy Markdown
Author

已按原评审及补充评审逐项复核,结论如下。

确认存在并已修复

  • benchmark-eval-controls 的 feature/API 兼容、默认关闭语义、工具预算截断、final-only 熔断、无歧义只读调用修复:均已恢复,并补齐 6 条 feature-gated forkguard 回归。
  • 两条写入路径的 64 KiB 上限、session-scoped/idempotent subagent cancel、Task/Automation run 终态删除、受限轮 idle 唤醒推迟、被禁 MCP 不进入 catalog/执行且与未知工具不可区分:均已补行为测试。
  • 上游被替换的 Prompt/Skills 与 Full Access 对照测试:已恢复上游测试;Pinvou 的 explicit-root/权限反转测试改为独立测试并明确标注反转理由。
  • steer 不仅验证 helper:新增真实 channel + turn-loop 提交/撤回回归。
  • MCP hot-disconnect、搜索响应形状、stuck guard 移除、write 描述等文档偏差已校正。
  • Permissions 100 KiB 片段仍是上游结构债:已在 CodeWhale 缓存说明和父仓登记中明确,避免被误认为 Pinvou 独有改动。

以上最终落在 fe0cd7551175f0f3df2c785b15c6c16e282218f7(基于官方 v0.9.12 的 7 个 DCO 提交)。

确认是债务,但本轮不做破坏性热修

  • facade 范围偏宽是真实问题:当前是 18 个 mod -> pub mod,父仓有 272 处引用、分布于 48 个 Rust 文件。直接收窄会同时破坏宿主编译,不适合作为评审期间的无迁移改动;已把准确规模和后续“显式 re-export + 分批收窄”验收要求写入 fork 文档。
  • 历史首个大提交的可审阅性问题真实。因为分支已公开供评审,本轮不 force-push 重写历史;已在登记中显式披露。

不属于本 fork 修复范围

  • 评审指出的 scripts/catalog_models_dev.py:432 CodeQL 注解:该文件 blob 与官方 v0.9.12 完全一致,当前 fork 对该文件无差异。因此不在 clean re-fork 中制造私有补丁;这里只对该条被引用的注解作此判断,不扩展为对其它 CodeQL 注解的结论。

验证

  • CodeWhale 默认完整测试:11,702 passed / 0 failed / 12 ignored。
  • CodeWhale all-features TUI 完整测试:11,723 passed / 0 failed / 12 ignored。
  • forkguard:默认 29 条 + benchmark-eval-controls 6 条,全部通过。
  • 父仓完整 ./scripts/fork-guard.sh 已在该 gitlink 上通过。

仍有一个发布流程门禁:公开 pinvou3-clean 还在旧 r13,且不可变 pinvou-v0.9.12-r1 标签尚未发布。没有在本次评审修复中绕过保护、移动标签或改默认分支。

@h3c-hexin

Copy link
Copy Markdown
Author

补充对当前 CodeQL 汇总红灯的完整核验:该 check 报告 101 条 annotations(汇总为 151 alerts)。我按 annotation path 和精确行号逐一与官方 v0.9.12 比较:

  • 未被 Pinvou fork 修改的告警文件,其 blob/内容来自官方 v0.9.12;
  • 对同时存在 fork diff 的 7 个文件,所有被标注行的 git blame origin 均是 v0.9.12 的祖先提交,不是本分支 7 个 Pinvou 提交。

因此现有 CodeQL 汇总失败是 PR base 仍为旧 r13、clean re-fork 造成超大比较面后暴露的上游告警;当前 annotations 中没有一条能归因到本 fork 新增行。后续仍应由上游安全治理处置这些告警,但不应在 Pinvou clean re-fork 中复制 101 条私有补丁。公开 baseline 更新后,也应让 CodeQL 以正确基线重新计算增量。

@h3c-hexin

Copy link
Copy Markdown
Author

Re-review of dbd1b7cb3 + fe0cd7551 (fix round) — verified outcomes

Follow-up to the earlier multi-agent review. The restore commits were compared hunk-by-hunk against r13 (pinvou-v0.9.5-r13) and the v0.9.12 baseline.

✅ Confirmed fixed (and genuine)

  1. feat: 增加隔离的评测控制 #32 eval-control semantics restored, not watered down: with_final_only_after_tool_budget / with_missing_read_action_repair are verbatim from r13; the final-only chain (budget-exhausted admission failure → strip ToolUse from the assistant message → clear tool surface for subsequent provider requests → drop tool-only responses with a second-violation Failed) matches r13 segment-by-segment; the fused-plan_tool_calls refactor is carried through with equivalent semantics. The new repair_benchmark_file_primitive is a correct v0.9.12 schema adaptation (read: offset/limit; read_file: start_line/max_lines — each folded per its own canonical fields), not a weakening; benchmark_final_only as a prefix-stability reason is a necessary adaptation to the new mechanism. All 6 forkguard_benchmark_* tests are restored with loop-level assertions equivalent to r13's.
  2. Lifecycle/safety test gaps closed with real assertions: 64 KiB write limit now has boundary-value tests on both write paths (exactly 64 KiB succeeds with correct bytes; 64 KiB+1 rejected with the target file never created); cancel_all_running_for_session has session-isolation + idempotency tests; delete_terminal_task/delete_terminal_run cover refuse-while-Running, delete-after-Completed (json+artifact), and idempotent false; restricted-turn idle-deferral has an end-to-end test (no TurnStarted within the latch window, delayed completion carried on the next explicit message). All helpers used already exist in b4c02616 — no self-made scaffolding.
  3. Write-tool description regained the 32KB-recommended / 64KB-hard-limit hint.

No new blockers or majors introduced: no unwrap/expect paths in new non-test code, desktop default builds remain behavior-identical (benchmark gates are behind compile-time feature + runtime host policy).

🟡 Still open before this is a clean protected-baseline

  1. SetDisallowedTools pool hot-disconnect: denied-server error invisibility is restored (with tests), but the r13 behavior of disconnecting already-established pool connections is neither restored nor registered in fork-modifications — the register currently promises more than the code does. Either restore the hot-disconnect or register the deliberate drop with rationale.
  2. benchmark-observability is still an empty shell (single Cargo.toml declaration, zero consumers) while benchmark-eval-controls is now wired — please wire or delete it, and update the parent register's "two empty features" wording which is now half-stale.
  3. Behavior-reversals doc (fe0cd7551) covers Full Access blocking, ambient prompt/skills closure, and the permissions-fragment exception, but misses the SetDisallowedTools difference and the scheduling dedup full-scan change.

⚪ Search fallback tail (#35) — adjudicated, still unfixed

The earlier review had conflicting readings; a line-level trace settles it: on a mainland network, when an API search backend (Tavily/Bocha/…) fails, the chain falls to DuckDuckGo, and a DDG connection failure returns early at the ? in web_search.rs (run_scrape_search_with_endpoints, the client send) — execution never reaches the allow_bing_fallback branch, which only triggers when DDG returns a successful HTTP response that parses to zero results or a challenge page. The two conditions are mutually exclusive with "DDG unreachable". Net effect: API-backend users on mainland networks get Err(NotAvailable) for the whole chain with no Bing results; the dead DDG hop also burns fair-share budget when it black-holes.

Recommendation: re-apply the r13 keyless-tail-as-Bing patch (backend.rs chain-tail enumeration + comment + error copy + the forkguard_api_provider_chain_tail_is_bing test; r13's 9c5f4f19b is a 3-file +59/−7 change whose surrounding function structure is unchanged between v0.9.5 and v0.9.12, so conflict risk is low), and register it in fork-modifications. If this is deferred, please record it as a tracked issue rather than leaving it in the generic "旧产品搜索覆盖" bucket.

⚪ Minor

  • The restored benchmark code dropped r13's rationale comments (the Web fields/max_chars/cross_action_parameters notes and "Never sanitize write-shaped or unknown actions") — worth restoring for the next reader.
  • The stuck-guard fork candidate noted in the parent register is invalidated by the v0.9.12 baseline (upstream removed the mechanism); the register's "candidate pending PR" wording should be updated to reflect the disposal.

Foundation CI (gate, Analyze) green on the new head; the CodeQL alert remains the upstream-inherited catalog_models_dev.py finding (file untouched by the fork) — attribution documented is fine.

Overall: the two prior blockers on this side are genuinely closed. With the two 🟡 registration/disposal items and a decision on the search tail, this is ready for the protected-ref transition.

@h3c-hexin

Copy link
Copy Markdown
Author

已核验本轮复审结论,并在 ff299f94b0795180c76d0336152385dbd02dfa05 完成需要落地的处理。

确认存在并已修复

  • API provider 的搜索兜底不可达:复审判断成立。原链路在 DuckDuckGo 连接失败时会在 ? 处提前返回,无法进入仅覆盖“成功响应但为空/挑战页”的内部 Bing fallback。现已恢复 API provider 失败后直接以 Bing 为 keyless tail,并补充覆盖 8 个 API provider 的 forkguard_api_provider_chain_tail_is_bing 回归。
  • benchmark-observability 空 feature:确认没有消费者,已删除;benchmark-eval-controls 保留并继续承载已恢复的 6 条评测控制行为。
  • 评测修复逻辑的理由注释:已恢复 Web/read 参数归一化及“不修复 write-shaped/unknown actions”的设计说明,避免后续维护者误扩展行为。

确认是有意设计,已登记但不改实现

  • SetDisallowedTools 不热断开共享 MCP pool:拒绝规则是 session/turn-scoped,而 pool 是共享资源。热断开会影响同时使用同一 server 的其他已授权 session;当前在 catalog 投影及最终 dispatch 两处 fail-closed,连接由正常生命周期回收。该差异和理由已写入 fork register。
  • Automation 调度扫描完整保留历史:这是为防止仍处于 active/同一调度槽的旧 run 被最新一页挤出后漏判;run retention 已限制存量,所以保留全量扫描并登记理由。
  • stuck guard:v0.9.12 上游已移除对应机制,当前登记已明确为上游处置,不再写成待移植候选。

验证结果:CodeWhale 默认完整测试 11,703 passed / 0 failed / 12 ignored;forkguard 默认 30/30、benchmark-eval-controls 6/6;父仓完整 ./scripts/fork-guard.sh 通过。此次复审没有遗留需要继续修改的 foundation-side blocker。

@h3c-hexin

Copy link
Copy Markdown
Author

Re-review of ff299f94b (fix round 2) — #35 verified closed

The search tail fix was re-reviewed scenario-by-scenario against the adjudication from the previous round. Verdict: correct, and stronger than r13.

Verified

  • Implementation re-applies the r13 approach: API-provider chains (anything other than Bing/DuckDuckGo) get a keyless Bing tail — no probing, no geo guessing. The guard condition keeps explicit keyless providers unduplicated.
  • Scenario walkthrough (with line-level evidence): app default (injected Bing) unaffected with a single-backend assertion; Tavily-failure → [Tavily, Bing], reachable from mainland networks (the core bug — a dead DDG hop can no longer swallow the chain); explicit DuckDuckGo selection preserved with its internal allow_bing_fallback untouched (zero diff in run_scrape_search_with_endpoints); server-side native search at the chain head still falls through open to the configured provider and then the Bing tail, with the comment updated to match.
  • Tests: forkguard_api_provider_chain_tail_is_bing restored and stronger than r13 — asserts the full chain [provider, Bing] across all 10 providers (8 API incl. Firecrawl + 2 keyless), where r13 only asserted the tail. The explicit-keyless no-duplicate case is asserted too.
  • Docs: config.example.toml + CONFIGURATION.md (en/zh) all say "directly to the keyless Bing tail" and note DDG remains explicitly selectable — consistent with the code. The example default correction (duckduckgo → firecrawl) fixes a pre-existing doc/implementation mismatch.
  • The benchmark-observability = [] deletion is complete on this side, and the parent has already dropped its feature reference (no dangling reference, verified at the parent head).

🟡 Non-blocking notes

  • The all-backends-failed error message keeps upstream's plain "web search backends unavailable: …"; r13's actionable "configure an API-backed [search] provider …" hint was not restored (though r13's provider list was stale anyway — Firecrawl missing — so restoring it verbatim wouldn't be right either). Optional.
  • The commit folds the dead-feature deletion and 12 lines of pure comments into fix(search); both are disclosed in the message, registered, and behavior-neutral — acceptable packaging, though a split would have been cleaner. The 12 turn_loop lines are confirmed comments-only inside the benchmark repair fn, i.e. the requested rationale notes, zero behavior.

With this, the foundation side has no open code findings from this review. Remaining path is the protected-ref transition + tag as described in the PR.

@h3c-hexin
h3c-hexin merged commit ff299f9 into pinvou3-clean Sep 9, 2026
5 of 6 checks passed
@asto18089

Copy link
Copy Markdown
Collaborator

Post-merge review follow-up (from the Pinvou/pinvou-agent#453 audit): this branch silently dropped overdue FREQ=ONCE automations, lost the actionable search-failure hint from the old fork line, and had a fail-unsafe deletion order in delete_terminal_task. Fixes are in #47. Also note this PR's description is stale: it claims 5 commits / 62 files / +3127/-614, but the merged head was 8 commits / 72 files / +4848/-637 — worth correcting for the record.

asto18089 added a commit that referenced this pull request Sep 17, 2026
…est clippy (#60)

* chore(baseline): resync the tui changelog slice, surface facts, and test clippy

Split out of CodeWhale#56 per CONTRIBUTING.md: changelog entries are
written on main at merge time and PRs carrying changelog hunks are asked
to strip them, so the DDG-to-Bing degradation entry (behavior already
merged in #44 via ff299f9; the root CHANGELOG already carried it, the
packaged tui slice did not) moves here together with its generated
web twin. The surface-facts trio moves with it because no gate on the
pinvou3-clean lane checks them; note v0.9.13 published on 2026-09-14,
so the next receipts run should refresh latestPublishedRelease again.
The redundant-closure cleanup in engine/tests.rs rides here because it
blocks the master/main and release lint lanes, not the fork lane.

Signed-off-by: asto18089 <asto18089@126.com>

* fix(web): evolve telemetry trust prose with the release bump

The surface-facts contract interpolated latestPublishedRelease.version
into a required 'published <version> release asked first' phrase. Once
the pin moves to v0.9.12 that demanded phrase becomes false: 0.9.12
counts by default and never asked. Name the historically fixed asking
release instead, matching the contract's upstream evolution: prose says
'earlier 0.9.11 release asked first' (en + zh), the test asserts it as
a literal, and the roadmap/faq/telemetryLead copies follow. Catalogs
regenerated with npm run i18n:gt -- export; npm test 407/407 green.

Signed-off-by: asto <asto18089@126.com>

* test(web): anchor the asking-release phrase across site faces

Review follow-up: the telemetry trust prose this PR evolves is mirrored
across the en/zh dictionaries and the faq/roadmap pages with no test
anchor, so a future edit could update one face and silently leave the
others behind. Assert the fixed 'earlier 0.9.11 release asked first'
phrasing (and its zh mirror) in every face and reject the interpolated
'published 0.9.11' wording; make providerCountDefinition version-free
('the providers of any published release') so the same stale-pin drift
cannot recur there; refresh the stale comment above the telemetry
assertions.

Signed-off-by: asto18089 <asto18089@126.com>

---------

Signed-off-by: asto18089 <asto18089@126.com>
Signed-off-by: asto <asto18089@126.com>
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.

3 participants