Skip to content

Phase2: agent.fix_slice worker receives insufficient target context and falls back to parent-side file.edit #341

@Kewton

Description

@Kewton

Summary

Phase2 autonomy-v2-p2-fix-slice-v2 short smoke (qwen3.5:122b) では、前回の 0-turn completion は解消したが、agent.fix_slice を呼んでも worker-backed success path に入らず、A1/B1 がともに worker_observed=false のまま expectation_mismatch で終了した。

今回の failure は Issue339 の再発ではない。

  • Issue339 で直したのは「escalation だけで worker_observed=true になってしまう false positive」
  • 今回は逆で、worker_observed=false のままなので telemetry semantics 自体は正しい
  • それでも Phase2 が落ちるのは、FixSlice worker が success-side の file.rewrite まで到達できていない ため

artifact 上の確定結果:

  • A1: mutation_observed=true, worker_observed=false, changed_files=2, pack_validation_result=mismatch
  • B1: mutation_observed=true, worker_observed=false, changed_files=1, pack_validation_result=mismatch
  • A2: mutation_observed=false, worker_observed=false, fixslice_escalation_count=14, runner_nonzero_exit

つまり:

  1. runtime は worker escalation を出している
  2. agent.fix_slice 呼び出しも観測される
  3. しかし worker success (file.rewrite) は観測されない
  4. 代わりに親エージェントの通常 file.edit で mutation するか、何も変えずに落ちる
  5. そのため requires_worker_observation gate は正しく失敗する

Why this belongs to Anvil

これは localllm-test の pack / runner 不具合ではない。

autonomy-v2-p2-fix-slice-v2pack_expectation=requires_worker_observation を明示しており、runner は telemetry の worker_observed をそのまま評価しているだけ。

今回の mismatch は runner の誤分類ではなく、Anvil runtime が worker success を成立させられていない ことを正しく露出している。

Observed behavior

A1

  • agent.fix_slice 呼び出しあり
  • FixSlice subagent は ./src, ., ./auto-yes-resolver.ts などを読み始める
  • 3 iteration で Reached max iterations (3)
  • file.rewrite 成功や fix-slice applied は出ない
  • その後、親エージェントの file.editsrc/lib/auto-yes-manager.ts / tests/unit/lib/auto-yes-manager.test.ts を変更
  • 結果は mutation_observed=true だが worker_observed=false

B1

  • 同様に agent.fix_slice 呼び出しあり
  • ただし worker success には至らず
  • 親エージェントの file.editsrc/lib/auto-yes-manager.ts を 1 行変更
  • 結果は mutation_observed=true だが worker_observed=false

A2

  • agent.fix_slice は呼ばれる
  • fixslice_escalation_count=14
  • しかし worker 側は収束せず、loop detector / tool failure で終了
  • mutation_observed=false, worker_observed=false

Root cause

1. agent.fix_slice 実行時に、worker へ渡す user prompt が goal だけになっている

ToolInput::AgentFixSlice { target_path, goal, max_lines } を受けたとき、runtime は:

  • goal を subagent への prompt として渡し
  • target_path親ディレクトリ だけを scope に設定し
  • target_path / max_lines 自体は subagent の user prompt に埋め込んでいない

つまり worker は:

  • 「何を直したいか」は聞いている
  • しかし どのファイルを第一対象にすべきか を user prompt からは受け取っていない
  • scope はディレクトリ境界にすぎず、対象ファイルの明示ではない

これは FixSlice worker にとって致命的で、対象ファイルへ収束する前に探索が散る。

2. FixSlice subagent の system prompt は target-specific な proposal を要求するのに、実ランタイムは必要な target context を渡していない

FixSlice subagent の prompt contract は:

  • target_path
  • start_line
  • end_line
  • replacement_content

を含む FixSliceProposal を返すことを要求している。

さらに role prompt では:

  • target file を読め
  • minimal local fix を返せ
  • max_lines budget を守れ

と書いてある。

しかし実際には subagent への user message が goal しかないため、system prompt が期待する target-specific proposal と runtime input が一致していない。

3. この欠落が、A1 の実ログでそのまま再現している

A1 では agent.fix_slice(target_path=\"src/lib/auto-yes-manager.ts\", ...) の直後、worker は:

  • file.read \"./src\"
  • file.read \".\"
  • file.read \"./auto-yes-resolver.ts\"

のように、target file ではなく周辺探索へ流れている。

これは「model が怠けた」よりも、「runtime が worker に target context を渡していない」説明のほうが整合的。

4. worker_observed が立たないのは仕様どおり

Issue339 後の Anvil では worker_observed は request-side ではなく success-side。

handle_fixslice_result() では、record_worker_success() は次の場合にしか呼ばれない:

  • worker proposal が存在する
  • validate_fix_proposal() を通る
  • file.rewriteCompleted
  • rollback なし
  • summary が空でも (no changes) でもない

今回の A1/B1/A2 はここまで達していないため、worker_observed=false は正しい。

Concrete problem areas

Primary hotspot:

  • src/app/agentic.rs
    • ToolInput::AgentFixSlicegoal -> prompt に潰している部分

Supporting hotspots:

  • src/agent/subagent.rs
    • FixSlice subagent の session 初期化は prompt をそのまま user message に積むだけ
    • つまり parent が target context を埋めなければ worker は受け取れない
  • src/app/agentic.rs
    • handle_fixslice_result()
    • ここはむしろ正しく、file.rewrite success がない限り worker_observed を立てない

What is not the root cause

Not localllm-test

  • pack は requires_worker_observation を正しく宣言している
  • runner は worker_observed をそのまま見ているだけ
  • 今回の mismatch は妥当

Not Issue339 regression

  • 今回は escalation だけで worker_observed=true にはなっていない
  • false positive ではなく worker success 未達 が本質

Not telemetry bug

  • telemetry は正直
  • 問題は worker 実行経路が success boundary に到達していないこと

Fix direction

1. agent.fix_slice subagent の user prompt に target_pathmax_lines を明示的に埋め込む

最低限、worker に渡す prompt を次の情報を含む固定フォーマットへ変更するべき。

  • exact target_path
  • exact max_lines
  • goal
  • 「まず target file を read せよ」
  • 「proposal の target_path は必ずこの path と一致させよ」

単に goal を渡すのではなく、FixSlice 専用の structured instruction block にする必要がある。

2. scope=parent_dir は維持してよいが、target anchor の代替にはしない

親ディレクトリ scope は sandbox 制約としては有効。
ただし scope だけでは target file selection を強制できないので、target anchor は prompt に別途必要。

3. 可能なら FixSlice subagent 起動時に target file path を system/user の両方で冗長に渡す

この path は correctness-critical なので、モデル任せにしない方がよい。

例えば:

  • system prompt: target_path must equal X
  • user prompt: fix X according to goal Y

の二重化が安全。

4. 追加回帰テスト

必要な回帰:

  1. agent.fix_slice(target_path=A, goal=...) で subagent 初回 prompt に A が含まれる
  2. FixSlice worker が target file 以外へ散りにくいことを prompt construction test で固定
  3. positive test:
    • escalation
    • agent.fix_slice
    • successful file.rewrite
    • worker_observed=true
  4. negative test:
    • escalation
    • parent-side file.edit salvage only
    • worker_observed=false

5. Secondary improvement: FixSlice worker budgetの見直し

今回の primary root cause は prompt/context 欠落だが、FixSlice worker が MAX_ITERATIONS=3 / TIMEOUT=60s でかなり早く切られる点も成功率に影響しうる。

ただしこれは secondary。
まず直すべきは target context を渡していないこと であり、budget tuning はその後。

Candidate acceptance criteria

  • ToolInput::AgentFixSlice から subagent へ渡す prompt に target_pathmax_lines が含まれる
  • FixSlice worker の初回探索が target file に anchor される
  • A1/B1 型の run で、worker path 成功時は file.rewrite が観測される
  • parent-side file.edit のみで終わった run は引き続き worker_observed=false
  • Phase2 retest で少なくとも 1 run は worker_observed=true かつ mutation_observed=true を達成する

Desk-check: if this issue is cleared, does it achieve the original goal?

Original goal

Phase2 の当初目的は:

  • parent autonomy を保ちつつ
  • local bounded mutation だけを worker に逃がし
  • benchmark 上でも requires_worker_observation gate を満たす

こと。

Desk-check conclusion

かなり高い確率で、今回の blocker は解消される。

理由:

  1. 現在の failure は「worker が起動しない」ではなく「起動しても target file に収束できない」
  2. A1/B1 の mutation 自体は既に親エージェントが到達できているので、task difficulty が不可能なわけではない
  3. つまり不足しているのは能力そのものより、worker に対する target grounding
  4. その grounding を追加すれば、worker proposal -> file.rewrite success path に乗る見込みは高い

But not mathematically guaranteed

この issue だけで 100% Phase2 pass が保証されるわけではない。

残るリスク:

  • model が still poor proposal を返す
  • max_iterations=3 がまだ厳しい
  • line-range / JSON proposal quality で別 failure が出る

しかしそれらは 次の層の failure であり、今の front blocker は明確にこの issue。

したがって机上検証の結論は:

  • Yes: この issue をクリアすれば、当初目的に対する主要 blocker は除去できる
  • Likely yes: Phase2 retest で worker-backed mutation が観測される公算は高い
  • Not absolute: もし retest でまだ失敗するなら、その次は worker budget / proposal quality の問題として切り分けられる

Relation to existing issues

  • #339: telemetry semantics の false positive 修正
    • 本 issue はその後に表面化した true negative / success-unreachable 側の問題
  • #332: escalation policy が narrow すぎる問題
    • 本 issue は「escalation 後に worker が正しい target を受け取れていない」問題

要するに:

  • #332when to call fix_slice
  • #339when to mark worker_observed
  • this issuewhat fix_slice actually knows when it starts

であり、役割が分かれている。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions