Skip to content

chore: upstream/dev の 49 commit を取り込み、harness の発火検証を追加する - #328

Merged
terisuke merged 52 commits into
devfrom
chore/upstream-merge-20260904
Sep 4, 2026
Merged

terisuke merged 52 commits into
devfrom
chore/upstream-merge-20260904

Conversation

@terisuke

@terisuke terisuke commented Sep 4, 2026 •

Copy link
Copy Markdown

upstream anomalyco/opencode の 49 commit を取り込み、fork の harness と統合して発火検証まで行った。

upstream/dev...HEAD  behind=0 ahead=239
137 files changed, 8317 insertions(+), 826 deletions(-)

衝突ゼロ

packages/guardrails(fork 独自 95 ファイル)は upstream に存在しないので衝突しない。
今回 upstream からの変更も 0 件だった。

接触面は 2 つだけで、どちらも安全だった。

接触点 upstream の変更 判定
packages/opencode/src/util/background(harness が直接 import) 0 件 影響なし
@opencode-ai/plugin package.json の version bump のみ(1.18.25 → 1.18.27) API 変更なし

merge 前後の実測(ベースラインを先に取った)

前回の取り込み(2026-09-01-opencode-upstream-merge-checkpoint.md)が
「merge 前の lint ベースラインを取っていない」を未検証の限界として記録していたので、
今回は着手前に取得した。

baseline 5f6ce5e merge 直後 8dc1606 本 PR a42e6b5
bun install rc=0 rc=0(22 packages) —
lint 5119 warn / 1 error 5131 warn / 1 error 5131 warn / 1 error
typecheck 30/30 30/30(cache 無効・実走) 30/30
harness 30 pass 0 fail 30 pass 0 fail 36 pass 0 fail

error は 1 のまま増えていない。 残る 1 件は
packages/session-ui/src/v2/components/prompt-input/index.tsx の Tailwind
arbitrary value content-['\200B'] を oxlint が JS の非推奨 8 進エスケープと
誤読するもので、merge 由来ではない(前回の取り込みで同定済み)。

warnings は +12(upstream の新規 5 ファイル由来)。本 PR の追加テストは 警告を増やしていない。

動作検証で穴が 1 つ見つかった — 既存 harness は発火を検査していなかった

merge 後に harness テストは 30 pass だった。しかしその 30 本は 1 度も
gate の発火経路を通っていない。

  • model-whitelist.test.ts — 3 コピーの whitelist が揃っているか(宣言の一致)
  • team-permission.test.ts — 権限表が矛盾していないか(宣言の一致)

宣言が揃っていても gate() が素通りすればガードは無いのと同じである。

gate-fires.test.ts を追加した

軸 内容
F1 発火 preview(status 非 active / id に preview・alpha・experimental)と whitelist 非許可で gate() が理由文字列を返す
F2 統制 正当なモデルは undefined(全部拒否する壊れた gate ではない)/providerID 無しは早期 return
F3 変異 preview() / free() の結論に gate が追随していることを直接照合

client は Proxy stub。gate() が触ったら例外で落ちるので、
「触らない」前提を型ではなく挙動で担保している。

反証(この検査が結論を作っていることの実測)

guardrail-context.ts から denyPreview の判定を外す
  -> rc=1、preview に関する 3 アサーションだけが落ちる
  -> 統制: model-whitelist.test.ts は 25 pass のまま無傷(変異は局所的)
復元
  -> 6 pass 0 fail

統制を先に確認している。 変異版で全部落ちたなら「変異が効いた」ではなく
「変異版が動いていない」なので、そこを区別した。

正式チェック

$ bun run local:check-offline
  36 pass, 0 fail (63 expect calls, 3 files)
  ok   whitelist covers the catalog snapshot in all three copies
  rc=0

範囲外(明示)

  • upstream の新機能に対する guardrails の追随は行っていない。 発見しても直さず、
    必要なら別 issue にする(前回の契約 欄9 と同じ方針)
  • 既存の lint warning 5131 件と error 1 件は本 PR で減らしていない
  • packages/web / console / tui の upstream 変更は取り込むだけで、
    fork 固有の検証は行っていない(fork が使うのは主に opencode / core / guardrails)

CI — check-duplicates の失敗と、その是正

初回 CI で check-duplicates が TimeoutError(2m36s 消費)で落ちた。merge の正しさとは無関係である。

pr-management.yml の当該 job は、非 team member の PR に対して opencode を
インストールし LLM に重複 PR を判定させる upstream 専用の triage 自動化で、
外部サービス呼び出しがタイムアウトした。fork の作者は .github/TEAM_MEMBERS に
居ないので毎 PR で全経路が走る。

80dcff8a87「chore(ci): forkのCI/CD実行を最適化」が duplicate-issues.yml には
repository guard を入れていたが、pr-management.yml は漏れていた。
同じ理由なので同じ guard(if: github.repository == 'anomalyco/opencode')を入れた。
workflow 本体は残す。add-contributor-label は外部呼び出しが無く 3 秒で終わるのでそのまま。

pull_request_target: [opened] は push で再実行されないため、本 PR の当該 check は
赤のまま残る。次の PR から効く。
緑に見せるために再実行はしていない。

範囲外(追加)

  • .github/TEAM_MEMBERS に fork の作者を足す案は採らなかった。upstream 由来の
    ファイルであり、次の取り込みで衝突する。repository guard のほうが fork の作法に合う

H5-guard: yes
H5-NEGATIVE: packages/guardrails/profile/plugins/guardrail-context.ts から denyPreview の判定行を削った変異版で bun test profile/plugins/gate-fires.test.ts が rc=1、preview に関する 3 アサーションだけが落ちる。統制として model-whitelist.test.ts は 25 pass のまま無傷(変異が局所的であることの確認 = 「変異が効いた」と「変異版が動いていない」の区別)。復元で 6 pass 0 fail。変異は assert s.count(old) == 1 で対象の実在を先に検査してから適用している。
H5-LEDGER: 本 PR の CI 変更は upstream 専用 job の no-op 化であり、発火を減らす側なので新たな台帳配線は無い。guardrail 本体の発火は guardrail.ts の chat.params から ctx.mark({last_block: "chat.params", ...}) を経て .opencode/guardrails/events.jsonl と state.json に記録される既存経路のままで、本 PR は変更していない。gate-fires.test.ts はこの経路に触れず gate() の戻り値だけを検査する(テストが実台帳を汚さない)。
H5-RETIRE: check-duplicates の guard は、fork が .github/TEAM_MEMBERS 相当の自前名簿を持つか、upstream が本 job を team 限定に変えた時点で不要になるので、その時点で撤去を再審する。gate-fires.test.ts は、gate() の拒否理由が 2 四半期にわたり 1 度も実運用で発火せず、かつ whitelist/preview 判定が別機構へ移った場合に退役 issue を起票する。
H5-SUBTRACTION: N/A
H5-PAIR: packages/guardrails/profile/opencode.json の plugin 宣言(./plugins/guardrail.ts)↔ guardrail-context.ts の gate() 実装; mutation=denyPreview の判定行を削ると同じ入力が undefined を返すようになり、gate-fires.test.ts の preview 3 アサーションが red(統制 25 pass は無傷、復元で green)。
H5-E2E: bun run local:check-offline
H5-E2E-OUT: 36 pass, 0 fail (63 expect calls, 3 files) / ok whitelist covers the catalog snapshot in all three copies / rc=0。加えて CI で e2e (linux) unit (linux-smoke) typecheck nix-eval storybook build が pass。

Evidence: bun install --frozen-lockfile / bun run lint / bun turbo typecheck / bun run local:check-offline / 変異による反証 | baseline 5119warn+1err → merge 5131warn+1err → 本PR 5131warn+1err、typecheck 30/30 実走、harness 30→36 pass 0 fail、変異で rc=1(統制 25 pass 無傷)・復元で 6 pass、upstream/dev...HEAD behind=0 ahead=239 | 2026-09-04 JST

thdxr and others added 30 commits August 31, 2026 21:34
Co-authored-by: rekram1-node <[email protected]>
Co-authored-by: Andreas Holt <[email protected]>
opencode-agent Bot and others added 21 commits September 2, 2026 16:06
upstream 取り込み (49 commit / 137 files) の動作検証で、既存の harness テストが
**発火経路を 1 度も通っていない**ことが分かった。

model-whitelist.test.ts と team-permission.test.ts が見ているのは宣言の一致
(3 コピーの whitelist が揃っているか、権限表が矛盾していないか)であって、
gate() が実際に止めるかではない。宣言が揃っていても gate が素通りすれば
ガードは無いのと同じである。

## 追加した検査

F1 発火  preview / whitelist の各経路で gate() が理由文字列を返す
F2 統制  正当なモデルは通る(全部拒否する壊れた gate ではない)
         providerID 無しは早期 return する
F3 変異  preview() / free() の結論に gate が追随していることを直接照合

client は Proxy stub にした。gate() が触ったら例外で落ちるので、
「触らない」前提を型ではなく挙動で担保している。

## 反証

guardrail-context.ts から denyPreview の判定を外すと rc=1 になり、
preview に関する 3 アサーションだけが落ちる。統制として
model-whitelist.test.ts は 25 pass のまま無傷(変異は局所的)。
復元で 6 pass に戻る。

## 実測

local:check-offline rc=0 / 36 pass 0 fail / typecheck 30/30
lint 5131 warnings 1 error(merge 直後と同一。本テストは警告を増やさない)

Agent-Lane: claude-code
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Et1J3J3Zi3V6zEzAod8QPD
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

80dcff8「chore(ci): forkのCI/CD実行を最適化」が duplicate-issues.yml には
repository guard を入れたが、pr-management.yml は漏れていた。

この job は非 team member の PR に対して opencode をインストールし、
LLM に重複 PR を判定させる upstream 専用の triage 自動化である。
fork の作者は .github/TEAM_MEMBERS に居ないので毎 PR で全経路が走る。

2026-09-04 に PR #328 で実測してタイムアウトした(TimeoutError / 2m36s 消費)。
merge の正しさとは無関係な失敗であり、fork では走らせる意味がない。

同じ理由なので同じ guard を入れる。workflow 本体は残す。
add-contributor-label は外部呼び出しが無く 3 秒で終わるのでそのまま。

なお pull_request_target: [opened] は push で再実行されないため、
PR #328 の当該 check は赤のまま残る。次の PR から効く。

Agent-Lane: claude-code
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Et1J3J3Zi3V6zEzAod8QPD
@terisuke
terisuke merged commit 8d9c54b into dev Sep 4, 2026
15 of 17 checks passed
@terisuke
terisuke deleted the chore/upstream-merge-20260904 branch September 4, 2026 00:29
@terisuke

terisuke commented Sep 4, 2026

Copy link
Copy Markdown
Author

訂正(2026-09-05)— 本 PR の「発火・動作検証」は過大だった

本 PR 本文と gate-fires.test.ts の追加は、guardrail が本番で発火することを検証したように読める。実 opencode run で反証された。 Codex による実環境貫通と監督の独立再現で、merge 時点の dev は:

failed to load plugin .../guardrail.ts  error="The \"paths[0]\" property must be of type string, got object"
failed to load plugin .../team.ts       error="Plugin export is not a function"
state.json: last_block='' llm_call_count=0

gate() は本番で一度も呼ばれていなかった。gate-fires.test.ts は loader を迂回して createContext を直接 import するため、この状態でも 6 pass だった。「@opencode-ai/plugin は version bump のみ・API 変更なし」も package.json の版数差分だけを見た断定で、取り消す。

是正(#329、merged 5d65dadd72)

  • default export を v1 形 { id, server } へ(legacy 経路で named export が server(input) として呼ばれていた)
  • loader の判定を複製しソースと pair で照合する plugin-loads.test.ts
  • gate-fires.test.ts ヘッダを「loader を迂回した単体検査」と明記

#329 merge 後の実環境(2026-09-04T17:01Z、aidd-governance worktree から実行)

$ opencode run --print-logs --model deepseek/deepseek-v4-flash-vision-exp "reply with the single word ok"
failed to load plugin: 0 件
ERROR ... error="Guardrail policy blocked this action: deepseek/deepseek-v4-flash-vision-exp is preview-only"
        at guardrail.ts:613
state.json: last_block='chat.params' last_reason='... is preview-only' llm_call_count=1

chat.params の gate が本番で block した最初の観測。

残る穴(次で直す)

block は state.json に ctx.mark() で書くだけで、events.jsonl に行を追記しない。台帳が last-writer-wins の state 欄しか無いので「全期間で何回 block したか」は数えられない。H6 の「発火時に台帳へ 1 行追記」を満たしていない。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.