pnpm buildは通った。agentも「実装完了」と返した。ところがtool-result panelをクリックすると、browser consoleにruntime exceptionが出る。
AI agentへUI修正を任せていると、この手のずれが厄介です。buildが見ているものと、ユーザー操作後のbrowserが見ているものは同じではありません。
Vite 8には、browser consoleのlogやerrorをdev serverのterminalへ転送するserver.forwardConsoleが入りました。coding agentを検出した場合は自動で有効になる仕組みもあります。
これはかなり便利です。ただし、consoleがterminalに戻っただけでPRを完了にしてはいけない。増えたのは観測経路であって、合否判定ではないからです。
自分なら完了条件を次の4層に分けます。
browser runtime evidence
-> lint / test / build
-> GitHub security checks
-> human scope review
-> complete / reject
まずforwardConsoleを明示する
検証用projectでは、agent検出だけに任せず設定を残します。
// vite.config.ts
import { defineConfig } from "vite"
export default defineConfig({
server: {
forwardConsole: true,
},
})
明示する理由は、特別な挙動を期待していることをrepository上で読めるようにするためです。agentを起動したshellやIDEによって検出条件が変わる可能性もあるので、fixtureの前提を暗黙にしません。
ここで期待してよいのは、browser consoleの出力をdev server terminalでも観測できることです。少なくとも、この設定だけで次のことまで保証されたとは扱いません。
- runtime errorが出たらdev server processも非zeroで終了する
- すべてのbrowser errorを漏れなく捕捉する
- UIの表示や操作が正しいと判定する
- E2E testやsecurity scanを代替する
terminalに赤いerrorが見えるようになっても、それを誰が、どの条件でfailにするかは別途決める必要があります。
1 route、2 actionでfailure pathを固定する
いきなり実際の画面全体をagentに巡回させると、どの操作を確認したのか曖昧になります。最初は検証用routeを一つに絞ったほうが扱いやすいです。
route: /agent-runtime-gate
component: tool-result-panel
action A:
open panel
-> console.error("[fixture] tool result failed")
action B:
submit tool
-> throw new Error("[fixture] invalid tool payload")
Reactなら、fixture自体はこの程度で足ります。
import { useState } from "react"
export function ToolResultPanelGate() {
const [open, setOpen] = useState(false)
const openBrokenPanel = () => {
console.error("[fixture] tool result failed")
setOpen(true)
}
const submitInvalidTool = () => {
throw new Error("[fixture] invalid tool payload")
}
return (
<section>
<button type="button" onClick={openBrokenPanel}>
Open panel
</button>
<button type="button" onClick={submitInvalidTool}>
Submit tool
</button>
{open && <div role="status">Tool result</div>}
</section>
)
}
これは成功例ではなく、観測経路を確かめるための故障fixtureです。実在するcredentialを入れたり、わざと脆弱なdependencyを追加したりする必要はありません。固定文字列なら、どのactionから出たerrorかも追いやすい。
生成UIのfixture候補を探すときは、SDK、OSS、実装記事をまとめた生成AI UIデザインのリソース集を補助にできます。ただし、選ぶのはtool result、approval panel、streaming listのどれか一つで十分です。凝ったdemoを作るより、failure actionを再実行できる小ささを優先します。
agent-done.ymlは公式schemaではなく、チームの完了契約にする
console forwardingの確認結果をagentの最終メッセージだけに残すと、次のPRではまた判定基準が変わります。そこで、repository側に小さな完了契約を置きます。
# agent-done.yml
# ViteやGitHubの公式schemaではない。チーム側の検証template。
task: genui-tool-result
runtime:
route: /agent-runtime-gate
required_actions:
- open-panel
- submit-tool
result: not-observed
commands:
- command: pnpm lint
result: not-run
- command: pnpm test
result: not-run
- command: pnpm build
result: not-run
security:
- category: code-scanning
result: unconfigured
- category: secret-scanning
result: unconfigured
- category: dependency-review
result: unconfigured
review:
scope_matches_task: pending
owner: pending
最初からpassで埋めないのがポイントです。これは設定例なので、未確認のruntimeはnot-observed、未実行のcommandはnot-runにしています。
securityの名前も期待する検査カテゴリにすぎません。実際のrepositoryで有効なcheck名、対象branch、適用条件をGitHub上で確認し、その結果へ置き換えます。「GitHubに機能がある」と「このPRでcheckが走った」は別です。
4層のevidenceを混ぜない
結果は一つのbooleanへ急いで丸めず、層ごとに残します。
| layer | evidence | result |
|---|---|---|
| runtime | route、実行action、forwarded log | pass / fail / not-observed |
| commands | command、exit status | pass / fail / not-run |
| security | check URL、status | pass / fail / unconfigured |
| review | changed files、owner | accept / reject / pending |
たとえば、browser errorが0件でもpnpm testを走らせていなければcommandsはnot-runです。buildが成功しても、対象PRにdependency checkが設定されていなければsecurityはunconfiguredのままです。
空欄や未設定を成功扱いすると、この表を作る意味がなくなります。
commandのexit statusも、agentの要約ではなくshellの結果から記録します。簡単に確認するだけなら、次のような形で十分です。
pnpm lint
printf 'lint=%s\n' "$?"
pnpm test
printf 'test=%s\n' "$?"
pnpm build
printf 'build=%s\n' "$?"
CIへ組み込むなら、各commandの終了コードをjob resultへ反映させます。ここで保存したいのは「実行したはず」という会話ではなく、commandとexit statusの組です。
GitHub側はruntimeと別のgateにする
GitHubのthird-party coding agents向けドキュメントでは、agentが変更したコードに対するCodeQL、secret scanning、依存関係の検査が説明されています。一方で、利用できる機能や適用範囲はrepository設定やplan、agentによって確認が必要です。
そのため、forwardConsoleでerrorが見えなかったことをsecurityの合格理由にはしません。逆方向も同じです。CodeQLが通っても、click handlerがbrowserで落ちる可能性は残ります。
agent sessionがGitHub Actions minutesやAI creditsを使う点も、runtime gateとは分けて扱います。失敗時に無制限retryするのではなく、最初のfailure evidenceを保存してから、人が続行か差し戻しかを決めるほうが運用しやすいです。
最後のscope reviewは人間が持つ
agentic coding toolの初期採用を調べた研究では、2025年5月から7月に観測された2,361 repositories、25,264件のagentic PRのうち、78.9%でreviewとcommitを同じ一人が担当していました。複数人が関与した割合は11.3%です。
これは現在の全projectへ当てはめられる数字ではありません。ただ、小規模なrepositoryでは判断が一人へ集まりやすい、という運用上の注意にはなります。
最後のreview packetには、少なくとも次をまとめます。
changed files
requested task scope
runtime result
command exit statuses
security check URLs / statuses
unresolved failures
review owner
人間が見るのはコードの雰囲気ではなく、task外の変更がないか、未解決failureが残っていないか、各gateの証拠が揃っているかです。agent自身の「完了しました」は、このpacketの代わりになりません。
再現手順
手元のVite 8 projectへ適用するなら、自分はこの順で進めます。
- 現在のVite versionを記録する。
-
server.forwardConsole: trueを明示する。 -
/agent-runtime-gateへ2つの固定failure actionを置く。 - dev serverを起動し、routeを開いて両actionを実行する。
- terminalへ戻ったlogにroute、action、errorの有無を記録する。
- fixtureを正常系へ戻し、同じactionをもう一度実行する。
-
pnpm lint、pnpm test、pnpm buildのexit statusを保存する。 - GitHub上で実際に有効なsecurity checkを確認し、人間がdiff scopeをreviewする。
4層すべてに証拠があるときだけcompleteにします。not-observed、not-run、unconfigured、pendingが残っているなら、そのPRはまだ完了していません。
Vite 8のconsole forwardingで、agentはbrowser側の失敗を拾いやすくなりました。そこで止めず、見えたfailureをcommand、security、scope reviewへ渡す。AI agentの速度を活かすには、この地味な完了条件のほうが効きます。