herdr substrate: session 同一性を workspace label アンカーへ移行(#556 の herdr 5 本赤の解消・完成済み実装の救出 land) - #587
Conversation
実レビュー + 現 main への追随(doeff 保守者側の席から)PR #569 の実レビュー(→ #569 の comment)の帰結として、本 PR を実レビューし、現 main へ追随させて検証しました。#569 は close 済みです。 追随(merge
|
| 対象 | 現 main | 本 PR + 現 main(c98492ac) |
|---|---|---|
packages/doeff-agents/tests/test_sessionhost_substrate_herdr.py(単独走行) |
5 failed / 6 passed | 13 passed(3 回とも) |
tests/test_enforcement_ledger.py + tests/semgrep/test_vm_failfast_semgrep_rules.py |
— | 18 passed |
packages/doeff-agents/tests 全量 -m 'not e2e' |
— | 7 failed / 1815 passed / 21 skipped |
全量の赤 7 本のうち 6 本は test_sessionhost_resume_cross_binding.py で、現 main のコードでも同じ 6 本が赤です(同一 worktree で main の packages/doeff-agents を checkout して実測 = 6 failed / 8 passed)。本 PR による退行ではありません。実 claude / codex の REPL ready gate が 120s で落ちるもので、実測時の機械の load average は 76〜130 でした。
⚠ 1 点、報告しておきたい実測(land を止める判断は依頼者にお任せします)
残り 1 本 = test_herdr_duplicate_session_rejected が、全量走行時に断続的に赤になります(観測 6 回中 3 回赤 / 3 回緑。単独走行は 3 回とも緑、単体指定の連続走行も load 85〜91 で 3 回とも緑)。
診断メッセージを一時的に入れて失敗点を特定したところ、段(2)「名札消失後の重複」で tmux-new-session が duplicate を raise しなかった(DIAG-3)ものでした。段(1)の通常の重複拒否は通っています。つまり create-then-verify の判定時に、先行 session の label 保持者が名簿から見えなかったことになります。
原因の候補を 2 つ実測で潰しましたが、まだ特定できていません:
- 実運用側による刈り取り: deftest と同じ API 列(
pane.report_agentで claude/working を名乗る +agent.rename)を打った workspace が外部に閉じられるか 90 秒観測 → 閉じられない(否定)。 workspace_idの順序鍵: 12 連続 create で shortlex 順 = 創出順が成立(否定)。
いずれにせよ main は名札消失後の重複を一切拒否できない(依存していた agent_name_taken が名札と一緒に消えるため)ので、本 PR は現状より後退はしません。ただし本 PR が導入する不変量そのものが実走行で 1 度すり抜けた実測なので、land 後の follow-up として残すか land 前に詰めるかは判断が要ると考えます。再現条件は「全量走行 + 高負荷(load 76〜130)」で、静穏機での再測が有効だと思います。
## 何を直すか #587 の重複 session 拒否(create-then-verify)は「workspace_id は創出順に 単調増加するので id 最小 = 先行 session」という前提に依存していた。この前提は 成立しない。 実測(2026-08-14・稼働 herdr 0.7.5 / protocol 17): - 断続的に赤だった全量走行の RPC 記録で、連続 create が w3NZ → w3N0 - 独立の連続 create 実測でも 200 回中 1 回 w3MZ → w3M0 いずれも後発のほうが shortlex で先に来る。前提が破れると gate は後発を勝者に 選び、**重複 session がそのまま素通り**する。deftest test-herdr-duplicate-session-rejected が全量走行でのみ断続的に赤だった (観測 6 回中 3 回)のはこれで、単独走行は反転を踏まないため緑だった。 初版の根拠(2026-08-09 の w1VS → w1VT → w1VV)は末尾桁の繰り上がりを跨いで おらず、一般には成立していなかった。 ## どう直すか 判定を 2 段に分け、既存 session の検出を id の順序から切り離す (新設の純関数 herdr-new-session-verdict が規則の単一定義点): - 段 1 = 事前照会。workspace を作る前に label 保持者を数え、1 つでも居れば 作らずに拒否する。順序に一切依存しない。無駄な生成と、close 失敗時の label 二重保持も同時に消える。 - 段 2 = 事後照合。段 1 と作成の間に割り込んだ同時作成だけが残る。競合者は 同じ名簿から同じ全順序で同じ勝者に合意するので、ちょうど 1 つが残る (合意に要るのは全順序であって創出順ではない)。 - 作成直後に自分が名簿に居ない場合は "vanished" として重複と別に名乗る。 旧実装は空 list に (get holders 0) を当てて IndexError になり、呼び手の RuntimeError 捕捉を素通りしていた。 shortlex 鍵(herdr-workspace-order-key)は残すが、用途を「同時作成の決定的 合意」だけに限定し、創出順の主張を docstring から撤回した。 ## 検証 - 新設 deftest test-herdr-new-session-verdict-ignores-id-order(実測の反転形 w3MZ / w3M0 を含む 6 例)— 実装前は red、実装後 green - packages/doeff-agents/tests/test_sessionhost_substrate_herdr.py = 14 passed - 実装経路の連続実射 200 回 = 重複拒否 200 / すり抜け 0 - 規則の対比(実測の反転形): 旧規則 = ok(素通り)/ 新規則 = duplicate - conformance/herdr-physics.md の該当節を訂正(旧「単調増加」の記述を反例つきで 差し替え) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
すり抜けの根本原因を特定しました — 前提が実測で偽でした(修正同梱)先の comment で「全量走行時に断続的にすり抜ける(原因未特定)」と報告した件の追報です。原因が判明したので、直して同梱しました( 原因:
|
| 出所 | 観測 |
|---|---|
| 断続的に赤だった全量走行の RPC 記録(実失敗時) | 連続 create が w3NZ → w3N0 |
| 独立の連続 create 実射(200 回) | 200 回中 1 回 w3MZ → w3M0 |
いずれも後発のほうが shortlex で先に来ます。前提が破れた瞬間、gate は後発を勝者に選び、重複 session がそのまま素通りします。
deftest test-herdr-duplicate-session-rejected が全量走行でのみ断続的に赤(観測 6 回中 3 回)で、単独走行では 3 回とも緑だったのはこれです — 単独走行は id の反転を踏みません。捕捉した失敗点も段(2)の「名札消失後の重複が拒否されなかった」で一致します。
初版の根拠(2026-08-09 の w1VS → w1VT → w1VV)は末尾桁の繰り上がりを跨いでいない 3 点で、一般には成立していませんでした。
否定した候補も記録しておきます(いずれも実測):外部プロセスによる workspace の刈り取り(90 秒 + 600 秒観測で不変)/ workspace label の自動改名(同)/ 兄弟 workspace の close 巻き添え(直接実射)/ workspace.list の一時的欠落・切り詰め(1,182 回 poll で 0 件)。
直し方: 既存 session の検出を id の順序から切り離す
新設の純関数 herdr-new-session-verdict を規則の単一定義点にして、判定を 2 段に分けました。
- 段 1 = 事前照会: workspace を作る前に label 保持者を数え、1 つでも居れば作らずに拒否する。順序に一切依存しません。副次的に、無駄な workspace 生成と、close 失敗時に label が二重保持される経路も消えます。
- 段 2 = 事後照合: 段 1 と作成の間に割り込んだ同時作成だけが残ります。競合者は同じ名簿から同じ全順序で同じ勝者に合意するので、ちょうど 1 つが残ります(合意に要るのは全順序であって創出順ではありません)。
- 作成直後に自分が名簿に居ない場合は
"vanished"として重複と別に名乗ります。旧実装は空 list に(get holders 0)を当ててIndexErrorになり、呼び手のRuntimeError捕捉を素通りしていました。
herdr-workspace-order-key は残しますが、用途を「同時作成の決定的合意」だけに限定し、創出順の主張を docstring から撤回しました。conformance/herdr-physics.md の該当節も反例つきで訂正しています。
検証
- 新設 deftest
test-herdr-new-session-verdict-ignores-id-order(実測の反転形w3MZ/w3M0を含む 6 例)— 実装前 red / 実装後 green - 規則の対比(実測の反転形): 旧規則 =
ok(素通り)/ 新規則 =duplicate - 実装経路の連続実射 200 回 = 重複拒否 200 / すり抜け 0
test_sessionhost_substrate_herdr.py単独 = 14 passedpackages/doeff-agents/tests全量-m 'not e2e'を 3 回連続 = いずれも 6 failed / 1817 passed / 21 skipped。赤 6 本はtest_sessionhost_resume_cross_binding.pyのみで、現 main のコードでも同じ 6 本が赤(実測済み)。herdr substrate は 3 回とも全緑で、以前の断続的な赤は再現しません。tests/test_enforcement_ledger.py+tests/semgrep/test_vm_failfast_semgrep_rules.py= 18 passedruff check packages/doeff-agents/の残り 2 件はtest_provider_failure_markers.pyの既存指摘(本枝を退避しても同数)- semgrep(変更 2 ファイル)= 指摘なし
計測時の load average は 39〜77 でした(赤を観測していた時間帯は 100〜220)。ただし段 1 は順序にも時間にも依存しない無条件の拒否なので、負荷条件によらず成立します。
別件の所見(本 PR の射程外・現 main にも在る)
同じ調査中に、herdr-pane-current-command-io が pane.process_info の foreground process に argv0 が無い応答で KeyError: 'argv0' になるのを実走行で 1 度観測しました(test_herdr_lifecycle_smoke が赤)。docstring は「foreground 不明は None」と契約しているので、契約どおり None を返すべき箇所です。現 main の同コードにも同じ形があり本 PR の変更点ではないため、ここでは直していません。別途の起票が要ると考えます。
herdr 0.7.5 / protocol 17 で agent.start が {name, kind, pane_id} 必須の
「既存 pane への管理対象 agent 起動 + 検出待ち」へ改形され(bundled schema
$defs.AgentStartParams、稼働 server 実測とも一致)、旧 payload はまず
missing field `kind` で拒否される(kind を足しても missing field pane_id —
実測)。shell pane の名前付き生成という TmuxNewSession の用途には
もう使えないため、herdr-new-session-io を protocol 17 の経路へ束縛替え:
workspace.create {label, cwd, env, focus:false}(cwd/env を直接受ける
ようになった — root pane がそのまま session pane、全幅 grid 維持)
→ pane.report_agent(外部 authority で agent エントリ生成)
→ agent.rename(名簿登録 — 重複名は agent_name_taken ネイティブ拒否
= tmux duplicate 拒否 parity 維持)
→ pane.clear_agent_authority(state authority を画面検出へ返却 —
名前は terminal に残り agent.get で解決可能、実測)
kill parity 不変(唯一 pane の pane.close で workspace 自動消滅 + 名簿解放)。
tmux backend(substrate.hy / host.hy)は無変更。実測記録は
conformance/herdr-physics.md の追補に記載。
test_sessionhost_substrate_herdr.py: 5 failed / 6 passed → 11 passed。
Refs #556
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…TDD red) issue substrate-herdr-session-identity-anchor-r2-607f0c(#556 の系譜、PR #569 の refile 先)。 実測前提(probe 2026-08-01 n=3 / 2026-08-09): - pane 内で実 agent を起動すると ~2 秒で herdr の実 agent 検出が agent 名札を 上書きし、agent.get {target: session 名} が agent_not_found になる - workspace label は実 agent 起動後も残存し、workspace.list で解決可能 - herdr は同一 label の workspace 重複をネイティブ拒否しない - workspace_id は base62 風カウンタで創出順に単調増加(shortlex 比較で順序復元) - agent 名には invalid_agent_name 制約(小文字開始・[a-z0-9_-]・32 文字以内)が あるが workspace label は無制約(60 文字・大文字・記号を実測受理) TDD red(現行実装での失敗を確認済み): - test-herdr-identity-survives-agent-name-loss: 名札消失模擬後の has-session/session-pane-ids/capture/send/kill 全経路 — 「has-session must survive agent name-plate loss」で fail - test-herdr-duplicate-session-rejected(改訂): doeff 側 duplicate 判定 + 名札消失後の重複素通り検出 + 敗者 workspace 掃除 — agent_name_taken 依存の 現行実装で fail - test-herdr-workspace-order-key-shortlex: tie-break 順序鍵 — ImportError で fail semgrep: doeff-agents-herdr-session-identity-not-agent-name("agent.get" 解決の 恒久禁止、sessionhost + conformance)。既存コード 3 箇所で発火を確認。 enforcement-ledger semgrep_rules 247→248。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
issue substrate-herdr-session-identity-anchor-r2-607f0c(#556 の系譜)。 PR #569 の workspace.create {label cwd env} 経路を出発点に、agent 名簿への 名前登録(pane.report_agent → agent.rename → pane.clear_agent_authority)を 撤去し、生死確認・帰属観測・kill の名前解決を workspace label に束縛替え: - herdr-workspace-order-key: workspace_id の創出順比較鍵(shortlex — base62 風カウンタの単調増加を実測 2026-08-09) - herdr-label-workspace-ids-io / herdr-workspace-id-io: label → workspace 解決(TmuxHasSession / TmuxSessionPaneIds / TmuxKillSession が共有) - herdr-new-session-io: create-then-verify の重複 gate(herdr は label 重複を ネイティブ拒否しない — 実測 2026-08-09。TOCTOU 窓を閉じる根拠はコード近傍) - herdr-kill-session-io: workspace.close(全 pane 破棄 = tmux kill-session parity、S19c 型 sibling も取り残さない) - conformance/harness.py: 帯域外経路(kill / liveness / S19c fault injection / adopt fixture)を同アンカーへ移行。create_session_out_of_band の protocol 14 形 agent.start 残骸も workspace.create 直行に修正 - herdr-physics.md:「clear_agent_authority 後も名前が残る」の破れ訂正 (実 agent 起動で ~2 秒後に検出が名札上書き — 実測 2026-08-01 n=3)+ アンカー移行の追補 テスト: test_sessionhost_substrate_herdr.py 13 passed(TDD red 3 本を含む)。 semgrep doeff-agents-herdr-session-identity-not-agent-name 0 findings。 tmux backend(substrate.hy / tmux.py)は無変更。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## 何を直すか #587 の重複 session 拒否(create-then-verify)は「workspace_id は創出順に 単調増加するので id 最小 = 先行 session」という前提に依存していた。この前提は 成立しない。 実測(2026-08-14・稼働 herdr 0.7.5 / protocol 17): - 断続的に赤だった全量走行の RPC 記録で、連続 create が w3NZ → w3N0 - 独立の連続 create 実測でも 200 回中 1 回 w3MZ → w3M0 いずれも後発のほうが shortlex で先に来る。前提が破れると gate は後発を勝者に 選び、**重複 session がそのまま素通り**する。deftest test-herdr-duplicate-session-rejected が全量走行でのみ断続的に赤だった (観測 6 回中 3 回)のはこれで、単独走行は反転を踏まないため緑だった。 初版の根拠(2026-08-09 の w1VS → w1VT → w1VV)は末尾桁の繰り上がりを跨いで おらず、一般には成立していなかった。 ## どう直すか 判定を 2 段に分け、既存 session の検出を id の順序から切り離す (新設の純関数 herdr-new-session-verdict が規則の単一定義点): - 段 1 = 事前照会。workspace を作る前に label 保持者を数え、1 つでも居れば 作らずに拒否する。順序に一切依存しない。無駄な生成と、close 失敗時の label 二重保持も同時に消える。 - 段 2 = 事後照合。段 1 と作成の間に割り込んだ同時作成だけが残る。競合者は 同じ名簿から同じ全順序で同じ勝者に合意するので、ちょうど 1 つが残る (合意に要るのは全順序であって創出順ではない)。 - 作成直後に自分が名簿に居ない場合は "vanished" として重複と別に名乗る。 旧実装は空 list に (get holders 0) を当てて IndexError になり、呼び手の RuntimeError 捕捉を素通りしていた。 shortlex 鍵(herdr-workspace-order-key)は残すが、用途を「同時作成の決定的 合意」だけに限定し、創出順の主張を docstring から撤回した。 ## 検証 - 新設 deftest test-herdr-new-session-verdict-ignores-id-order(実測の反転形 w3MZ / w3M0 を含む 6 例)— 実装前は red、実装後 green - packages/doeff-agents/tests/test_sessionhost_substrate_herdr.py = 14 passed - 実装経路の連続実射 200 回 = 重複拒否 200 / すり抜け 0 - 規則の対比(実測の反転形): 旧規則 = ok(素通り)/ 新規則 = duplicate - conformance/herdr-physics.md の該当節を訂正(旧「単調増加」の記述を反例つきで 差し替え) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
前段(27ba33c)は重複 gate から順序前提を外したが、**名前解決の側に同じ前提が 残っていた**: herdr-workspace-id-io が保持者一覧の先頭を「その名前の session」と して選び、生死・帰属観測・kill がその 1 つに向いていた。workspace_id は創出順に 単調でない(前段の実測 w3NZ → w3N0 / 追試 2026-08-14 の w3ZW → w3ZX → w3ZY → w3ZZ → …繰り上がり… → w303 → w304、および review 実測 w35Y → w35Z → w350 → w351)ので、この選択は任意の保持者を掴む。 **前段(27ba33c)に対する実測反例**(同 label の holder 2 つ — 同時作成の敗者が 閉じる前・敗者の close 失敗・doeff 外の同名 label・conformance の帯域外 create で 成立する状態): holders ['w42F','w42G'] → 帰属観測 ['w42F:p1'](第 2 holder の pane が不可視) → kill 後の holders ['w42G'](生きた workspace の漏れ。呼び手には「殺した」) 帰属観測の穴は policy.hy の R4 検証(pane が session に属さない → vanished 即時終端) を通って、**生きた席の誤終端**になる。 **修理**(順序を要求しない形にする — herdr に創出順が無いことが破れに繋がらない): - 生死(TmuxHasSession)= 保持者が非空か。 - 帰属観測(TmuxSessionPaneIds)= 全保持者の pane.list の和(tmux `list-panes -s -t NAME` = その名前に帰属する pane 全部、の parity)。 - kill(TmuxKillSession)= 全保持者を workspace.close(その名前の session が 消える、の parity)。消滅済み保持者の workspace_not_found のみ飲み込む (実測 2026-08-14: 消えた workspace への pane.list / workspace.close は この code を返す)。 - herdr-workspace-id-io(「その 1 つ」を選ぶ面)は撤去。純関数 herdr-label-holders を新設し、保持者は並べ替えずに集合として返す。 - 診断文言も保持者を全部並べる(1 つ選んで名乗ると「それが session」と読める)。 - conformance/harness.py も同形へ — 反証済みの創出順鍵 _herdr_workspace_order_key を撤去(review 指摘の「両方に適用せよ」の対)。kill = 全保持者掃き取り、 liveness = 非空判定は元から順序に依存しない。 - 前段の 2 段 gate(事前照会 + 同時作成の合意)と shortlex 鍵の用途限定は そのまま活かす — 合意に要るのは全順序であって創出順ではない、という前段の 整理は正しい。 **恒久ガード**: semgrep doeff-agents-herdr-label-holders-must-not-be-indexed (substrate_herdr.hy / conformance harness.py での保持者一覧の添字参照を禁止) + probe fixture + 単体テスト。enforcement 台帳 249 → 250。 **テスト**(実 herdr server、canonical 16 passed): - test-herdr-label-holders-are-a-set-in-listing-order(純関数・繰り上がり境界 w35Z/w350 の反例つき — 字面の順序鍵は先行/後発を入れ替える) - test-herdr-name-resolution-covers-every-label-holder(保持者 2 つで帰属観測の 被覆と kill の全掃き取り。前段の実装では red = 上記の実測反例) - test-herdr-duplicate-session-rejected を強化(残存する 1 つが先行の workspace であること・名前解決が先行 pane を指し続けること) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
f851c3e to
8373851
Compare
test(doeff-agents): cross-binding deftests の capture 台本へ {composer} marker を追随(#587 の更新漏れ)
|
本 PR は #594( 🤖 Generated with Claude Code |
何を直すか
herdr substrate の session 同一性の根拠を、実 agent 起動後に herdr 側の実 agent 検出が上書きしてしまう agent 名札から、上書きされない workspace label アンカーへ移行する。これにより未改変 main で赤の herdr substrate 正典テスト 5 本(#556 の症状 2)が green になる。
Refs #556(正典 hy-gate conformance が未改変 main で赤 — 本 PR は herdr substrate protocol 不一致側の 5 本を解消する)
doeff:issue:substrate-herdr-session-identity-anchor-r2-607f0c選定根拠(なぜ workspace label をアンカーにするか)
agent.startが改形され、名前付き shell pane 生成には使えなくなった。代替の名前登録はpane.report_agent→agent.rename経由だが、実 agent(claude 等)が pane 内で起動すると約 2 秒以内に herdr の実 agent 検出が名札を上書きし、agent.get {target: 旧名}はagent_not_foundになる(2026-08-01 probe n=3 決定的 —conformance/herdr-physics.md追補に実測記録を同梱)。tests/semgrep_rules/herdr_agent_name_identity_forbidden.hy)で機械的に禁止し、名札消失後も同一性が保たれることを deftest(test-herdr-identity-survives-agent-name-loss)で pin する。受入条件との突合(発注 4 項)
packages/doeff-agents/tests/test_sessionhost_substrate_herdr.py= 13 passed(main 0698c74 は 5 failed / 6 passed)。独立検証 3 回(2026-08-10 ×2・2026-08-11 正規形 ×1)。test-herdr-identity-survives-agent-name-loss+ semgrep guard 同梱。退行確認
packages/doeff-agents/tests全量 = 4 failed / 1768 passed / 21 skipped — 赤 4 本は main でも同一の環境依存 e2e(実 agentd / 実 MCP を要求)につき退行なし。出自(provenance)
本枝は ACP 実装席(4 世代目)が 2026-08-09 に完成させたもの。席は保存の 4 分後に稼働中の誤打ち切りで強制終了され「実装消滅」扱いになっていたが、成果は共有 checkout の ref 空間に残存していた。下調べ席の検証と operator 裁定(2026-08-11)を経て、本 PR はその完成品を無改変で救出 land するもの(コミットは実装席の著作のまま・tip 3c22539)。
改訂 2(2026-08-14): 名前解決からも創出順の推測を撤去
差し戻しの指摘(review)
初版の重複 gate は「workspace_id の shortlex 順 = 創出順」を前提にしており、herdr のカウンタが英字から数字へ繰り上がる境界(先行
w35Z/ 後発w350)で勝者判定が反転する。重複 create が自分の gate を通過して生きた session を孤児化し、続く kill が誤った workspace を閉じて生きた方を漏らした。review は (1) 両ファイル(substrate_herdr.hy と conformance/harness.py)への適用、(2) 繰り上がり境界の pin を要求した。fix_class: root-cause
根因 = herdr の workspace_id という他システムの不透明な識別子から創出順を自前で導き、その順序に session 同一性を託していたこと。id の字面に創出順は無い(下記の実測)。したがって「正しい順序鍵を作り直す」修理は同じ誤りの再演にしかならず、順序を必要としない形に変えるのが根治になる。
実測(稼働 herdr 0.7.5 / protocol 17):
w3NZ → w3N0/w3MZ → w3M0w3ZW → w3ZX → w3ZY → w3ZZ → …繰り上がり… → w303 → w304 → w305 → w306w35Y → w35Z → w350 → w351'0'(48)は英字(65-90)より小さいので、後から作った id が ASCII でも shortlex でも先行より小さくなる。2 段の改訂(この枝の 2 コミット)
27ba33c(前段) — 重複 gate を 2 段化(事前照会で既存 session を検出 + 事後照合は同時作成どうしの合意のみ)。shortlex 鍵は「合意のための全順序」に用途限定。
vanished判定で IndexError も解消。f851c3e(本コミット) — 名前解決の側に残っていた同じ前提を撤去。
herdr-workspace-id-ioが保持者一覧の先頭を「その名前の session」として選んでいたため、保持者が 2 つ並ぶ状態(同時作成の敗者が閉じる前・敗者の close 失敗・doeff 外の同名 label・conformance の帯域外 create)で被害が残っていた。前段(27ba33c)に対する実測反例:
帰属観測の穴は policy.hy の R4 検証(pane が session に属さない →
vanished即時終端)を通って生きた席の誤終端になる。修理後は:list-panes -s -t NAMEparity)workspace_not_foundのみ飲み込む)herdr-workspace-id-io(「その 1 つ」を選ぶ面)は撤去_herdr_workspace_order_keyを撤去 — review 指摘 (1) の対)検証(1:1 対応)
packages/doeff-agents/tests/sessionhost_substrate_herdr_deftests.hy::test-herdr-new-session-verdict-ignores-id-order(前段)+::test-herdr-label-holders-are-a-set-in-listing-orderw35Zvsw350)::test-herdr-label-holders-are-a-set-in-listing-order(shortlex も素の ASCII も先行/後発を入れ替えることを反例として assert)packages/doeff-agents/conformance/harness.py(_herdr_workspace_order_key撤去・集合型)+ 実 herdr 実射(holder 2 つ → liveness True → kill → holder 0 → liveness False)::test-herdr-name-resolution-covers-every-label-holder(実 herdr。前段の実装では red = 上記反例)::test-herdr-duplicate-session-rejected(強化: 残存する 1 つが先行の workspace であること・名前解決が先行 pane を指し続けること)packages/doeff-agents/tests/test_sessionhost_substrate_herdr.pydoeff-agents-herdr-label-holders-must-not-be-indexed+ fixture +tests/semgrep/test_vm_failfast_semgrep_rules.py::test_herdr_label_holders_indexed_rule_detects_arbitrary_holder_pick(単一ファイル走査・ディレクトリ走査の両モードで実射確認)+ 台帳 249 → 250(tests/test_enforcement_ledger.py)make lint-semgrep= 199 rules / 903 files / 0 findings、make lint-pyright= 0 errors検証の逸脱(declared)
DOEFF_SESSIONHOST_BACKEND=herdr)は本改訂では回していない。理由は実行機の負荷(load average 48)で、この suite は静穏機を前提とする既知の環境依存(Canonical hy-gate conformance is red on unmodified main in the local env (S11b client hang; herdr substrate protocol mismatch) #556)。代わりに harness の変更点を実 herdr へ直接実射して確認した(同 label の holder 2 つ →session_exists_out_of_bandTrue →kill_session_out_of_band→ holder 0 → False)。harness 側の消費は元から順序に依存しない(kill = 全保持者掃き取り / liveness = 非空判定)。make lint-ruffは main 由来の既存 red 2 件(packages/doeff-agents/tests/test_provider_failure_markers.pyの I001 / PT006。origin/mainの同ファイルでも同一に再現)。本 PR の差分外につき手を付けていない。make lint-doeffは doeff-linter 未インストールの環境警告(実行機側の欠落)。並行席との統合
同じ review 差し戻しに対して別席が 27ba33c を先に push していた。本コミットはそれを上書きせず土台として採用し(2 段 gate と用途限定した shortlex 鍵はそのまま活かす)、残っていた名前解決側の前提と harness を上に重ねた。