Skip to content

fix(fork): route the stopship scout through tool_search and grep_files - #61

Merged
asto18089 merged 4 commits into
Pinvou:pinvou3-cleanfrom
asto18089:followup/stopship-scout-tools
Sep 17, 2026
Merged

asto18089 merged 4 commits into
Pinvou:pinvou3-cleanfrom
asto18089:followup/stopship-scout-tools

Conversation

@asto18089

@asto18089 asto18089 commented Sep 14, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

workflows/stopship.workflow.js (the version-neutral release-acceptance fixture) briefs its read-only explore scout with a File call using action: "search_content". The follow-up review of #56 found this is not merely stylistic: File is a hidden replay-only alias (model_visible=false), so it never reaches a model-visible catalog, and the live child step loop dispatches through execute_from_surface, which fails any name outside the child's policy-filtered catalog outright. The explore gate's first step cannot succeed today — dispatch-by-alias only worked below the catalog gate (replay tests calling registry.resolve directly).

Fix

Route the scout through the live read-only surface:

  • Response 1 makes exactly one tool_search call (query: "grep_files") to activate the deferred content-search tool.
  • Response 2 makes exactly one grep_files call with the same evidence contract as before (path ., the exact five-file include list, the same high-signal alternation pattern, max_results 80, context_lines 2).
  • Response 3 returns the verdict with no tool calls, as before.

Step and token caps are unchanged (max_steps 6, 480s, 96k); the evidence turn grows one small activation response. grep_files is the canonical name for the retired search_content action (already mapped in canonical_action.rs), and the scout surface provably carries it: it passes the read-only evidence filter via is_read_only().

Regression guards

  • forkguard_scout_surface_keeps_tool_search_grep_files_activation_path pins the scout surface to a first-turn-active tool_search plus a deferred, searchable grep_files with no File. It builds the registry with the scope the fixture scout really runs under (the read-only lowering ["File"]), so the alias-family intersection that keeps grep_files discoverable under that legacy rule is guarded too — the exact reshape that silently broke the fixture is now a red test.
  • forkguard_workflow_briefs_name_catalog_visible_tools pins the fixture definition (not the maintainer comment) to catalog-visible tool names, denying hidden replay aliases (File, Bash, TodoWrite, work_update, read_file, write_file, edit_file) and the retired search_content as whole words — an unbackticked citation can no longer slip past. Mutation-checked: reverting the brief wording turns the test red.
  • forkguard_scout_activation_makes_grep_files_dispatchable is the behavioral counterpart: on the live scout surface, one tool_search call must make grep_files dispatchable through execute_from_surface, and a File call must keep failing the catalog gate. Composition can drift from behavior; this cannot.
  • stopship_acceptance_fixture_is_read_only_and_gate_complete (workflow crate) now pins the three-response activation contract instead of the retired wording.

Verification

  • cargo test -p codewhale-workflow --lib stopship_acceptance_fixture_is_read_only_and_gate_complete and cargo test -p codewhale-tui --lib (new guards, the envelope ceiling test, and stopship_acceptance_fixture_emits_role_gate_and_terminal_receipts, which compiles this fixture) all pass; cargo fmt --check and clippy (CI flags) clean on the touched files.

Base

One commit on top of the current r1 maintenance head ae7e3fb36. Shares crates/tui/src/tools/subagent/tests.rs with #56 (both PRs append at end of file), so whichever merges second needs a trivial keep-both rebase; no semantic coupling. Closes the leftover disclosed in #56 and Pinvou/pinvou-agent#490.

No-Issue: fork-side fixture repair; the disclosure in #56/Hmbown#490 tracks it.

The read-only scout brief commanded a `File` call with `action`
`search_content`, but `File` is a hidden replay-only alias
(`model_visible=false`): it never reaches a model-visible catalog, and
the child step loop fails any call outside the child's policy-filtered
catalog outright. The release-acceptance explore gate's first step
therefore could not succeed; dispatch-by-alias only worked below the
catalog gate (replay tests calling `registry.resolve` directly).

Route the scout through the live surface instead: response 1 activates
the deferred `grep_files` with one `tool_search` call, response 2
runs the same alternation search through `grep_files`
(path/include/pattern/max_results/context_lines), and response 3
returns the verdict. The step and token caps are unchanged; the
evidence turn grows one small activation response.

Guard both halves so neither side silently drifts again: one forkguard
test pins the scout surface to an active `tool_search` plus a
deferred, searchable `grep_files` with no `File`, and another pins
the fixture definition to catalog-visible tool names only.

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

Copy link
Copy Markdown

Thanks @asto18089 for taking the time to contribute.

This repository is observing a maintainer-managed PR intake gate in dry-run mode, so this pull request is staying open. This note helps maintainers prepare the allowlist before any enforcement is considered.

Please read CONTRIBUTING.md for the expected contribution shape. A maintainer can grant recurring PR access by commenting /lgtm on a pull request.

The review of Hmbown#61 caught one real break and three guard-fidelity gaps:

- js_authoring.rs still pinned the retired `File`/`search_content`
  scout wording, so the workspace suite and CI went red on this
  branch. Pin the new three-response contract instead.
- The scout surface guard built its registry with no explicit scope,
  while the fixture scout really runs under the read-only lowering
  `["File"]`. Build it with that scope so the alias-family
  intersection that keeps grep_files discoverable is guarded too.
- The brief guard only denied backticked `File` and bare
  `search_content`; deny the hidden replay aliases as whole words so
  an unbackticked citation cannot slip past.
- Add the behavioral counterpart: one tool_search call must make
  grep_files dispatchable through execute_from_surface, and a
  File call must keep failing the catalog gate.

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

Copy link
Copy Markdown
Collaborator Author

Full four-track review of the original head (4a669ce) + fix-up commit bf15241ce:

Root cause and fix: confirmed end to end. File is model_visible=false (file_tool.rs), filtered by to_api_tools (registry.rs:256), and the child step loop's execute_from_surface rejects it with "not in this child's policy-filtered catalog" (subagent/mod.rs); no alias fallback exists on that path (the main-loop one at turn_loop.rs never runs for sub-agents). The new three-response path is live-viable: explore lowers to FleetRole::Scout, tool_search is first-turn active, grep_files is deferred+searchable, and activation writes the same SubAgentToolSurface the dispatch gate reads — no second-catalog gap. Evidence contract of the brief is byte-identical apart from the tool mechanics.

Fix-ups in bf15241ce (all review findings):

  1. MAJOR — CI break, fixed. js_authoring.rs::stopship_acceptance_fixture_is_read_only_and_gate_complete still pinned the retired wording, so the workspace suite went red (reproduced locally and on CI; the only failure — fmt/clippy steps were green). The guard now pins the three-response contract.
  2. The surface guard now builds the registry with the scope the fixture scout really runs under (["File"] lowering), so the alias-family intersection keeping grep_files discoverable is guarded, not just the scope-free surface.
  3. The brief guard now denies hidden replay aliases (File, Bash, TodoWrite, work_update, read_file, write_file, edit_file, search_content) as whole words — mutation-tested that an unbackticked File call wording turns it red.
  4. Added forkguard_scout_activation_makes_grep_files_dispatchable: live-surface behavioral check that one tool_search call admits grep_files through execute_from_surface while File keeps failing the catalog gate.

Also corrected the PR description: this PR shares crates/tui/src/tools/subagent/tests.rs with #56 (both append at end of file), so the second merger needs a trivial keep-both rebase; no semantic coupling.

Verification: stopship_acceptance_fixture_is_read_only_and_gate_complete, the three forkguard_* tests, stopship_acceptance_fixture_emits_role_gate_and_terminal_receipts all pass at bf15241ce; cargo fmt --check clean; clippy (CI flags) clean on both touched crates.

@JensenChen28 JensenChen28 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.

已审查当前 head 对目标分支的实际差异,未发现需要阻塞合入的问题;提交前重新确认 required checks 通过。核对了 stopship scout 的 tool_search → grep_files 激活路径及相关回归。本轮以静态审查和远端门禁为依据,未在本机运行完整 Rust workspace 或所有平台测试。

Resolve the tests.rs anchor collision: keep the stopship scout surface,
brief, and activation-path guards from this branch and the skill-catalog
and handle_read hint guards that landed with Hmbown#56.

Signed-off-by: asto18089 <asto18089@126.com>
Sync the resolved scout-branch state with the Hmbown#58/Hmbown#59/Hmbown#60 batch.

Signed-off-by: asto18089 <asto18089@126.com>
@asto18089
asto18089 merged commit 92427bd into Pinvou:pinvou3-clean Sep 17, 2026
5 checks passed
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