fix(hooks): axis-tag format spec and locale-pinned ordering across three session-start ports - #1652
Conversation
…format #1650 の brake 1 で分離した、3 ポート共通の残余 4 件をまとめて解消する。いずれも「片方だけ直すと新しいポート間の食い違いを作る」構造なので、`adapter/claude/hooks/on-session-start.sh` / `adapter/codex/hooks/on-session-start.sh` / `adapter/codex/hooks/on-session-start.ps1` を同時に変更した。 項目 4(書式の spec 化)を先に置き、そこから項目 2 の正規形を導いている。順序依存は issue 本文の制約どおり。 - 項目 4: `skills/evolution-self-eval/SKILL.md` に Axis tag line format 節を新設。2 つのレイアウト、フィールド定義、軸名の normal form、1 行形の終端規則を literal として規定した。検出器はこの節の実装であり、齟齬が出たら skill が正本である旨を 3 ポートのコメントに明記した。これまで `**Axis tags**:` という書式はどの spec にも存在せず、hook のハードコードだけが根拠だった(#1632 F3 と同じ構造リスク) - 項目 2: 軸名を正規形へ畳んでから集計する。`*` 除去 → 括弧修飾の切り落とし → `-`/`_` を空白へ → 空白畳み込み → 小文字化 → 10 軸のいずれか 1 つだけの語境界前方一致なら展開。alias table は持たず、10 軸リスト自体を正規語彙として使う。実ログ(live self-evaluation_log.md)で検証したところ、`Character drift` / `Character` / `Character(pronoun)` / `character-drift` に分裂していた 4 件が 1 軸に集約され、閾値超えの軸が 2 件から 4 件になった - 項目 3: 1 行形の pair 列を括弧外の `。` または `Root cause:` / `Domain:` で終端する。分割も括弧外の ` / ` のみで行う。これまで最後のセグメントが行末まで伸び、後続の自由記述が `miss` 走査の対象になっていた(実ログでは混入テキストに `miss` が無かったため未発火だったが経路は生きていた)。括弧内の ` / ` による phantom 軸も同時に消える - 項目 1: 出力順を決めるすべての並び替えをロケール/カルチャ非依存に固定した。bash 側は `LC_ALL=C sort`、PowerShell 側は `Sort-Object` を排して `[System.StringComparer]::Ordinal` に置換。rules injection の並びは相対パス(`/` 区切り)を対象にしたので、ロケール軸とセパレータ軸の両方でポートが一致する。`SURFACE_CAP` の切り詰めは並び順が「どの項目が残るか」を決めるため、表示順だけの問題ではない - テスト: `AxisTagFormatTest` を新設し、4 件それぞれを本番形の 3 ポートで検査する。fixture には従来無かった形(表記ゆれ、日本語の行末自由記述、verdict 括弧内の ` / `、非 ASCII の entry title、ケース非依存の並び替え差分)を入れた。項目 1 は culture-aware locale が無いホストでは挙動差が観測できないため、ソース側の pin 検査を回帰ガードの本体に置き、挙動側は 3 ポート一致の裏取りとして併走させている - docs: `docs/6.-Adapter.md` の検出器 1 の記述を新 spec への参照に差し替え、並び順の決定性を追記した Closes #1651
…er note brake 1 (N=3, sonnet 床, 単巡) の指摘のうち、裁定して採用した 5 件を反映する。 いずれも仕様文とコメントの修正で、検出器のアルゴリズムには触れていない。 - 括弧の範囲を仕様に明記 (3/3)。仕様は "outside parentheses" とだけ書いて いたが、実装は ASCII の `(` `)` しか追跡しない。全角 `(` `)` が追跡外で あること、および閉じられない `(` 以降が全て括弧内扱いになりペアが数えられ なくなることを、読み手が実装を読まずに分かる形で書いた。軸名正規形の step 2 も同じ ASCII 限定を持つため併記。 - `$axisCount` の Ordinal 比較子コメントを訂正 (3/3)。重複検出器の理由を 類推で借りていたが、Get-AxisNormalForm は全 return 経路で小文字化するため この表では大小文字差がキーに到達せず、比較子は現状の入力では効かない。 awk 側の意味論とのパリティが目的である旨へ書き換えた。 - miss 判定が大小文字を区別しないことを明記 (2/3)。実装は 3 ポートとも 小文字化して照合しているが、仕様文が言及していなかった。 - `SKILL.md` の "Putting the trailer on its own line ... is preferred" を削除 (固定軸 2/3 -> refine)。挙動を規定せず、削除しても規則の意味が変わらない。 記録のみとした所見と分離した所見は PR のセルフレビューに残す。 Refs #1651 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
liplus-lin-lay
left a comment
There was a problem hiding this comment.
セルフレビュー (semi_auto / patch)
リリース種別 = patch
rules/operations/release-version-rule.md を適用時点で実読して判定した。cold-start が surface する軸集合が変わるため system observable ではあるが、minor の条件は「observable であること」ではなく「large な refactor / 構造変更であり、かつ observable」。本 PR は検出器 1 本の欠陥修正と仕様節の追加であり large の修飾を満たさない。よって patch。semi_auto の patch につき human check なしで AI 直接マージ。
brake 1
N=3 / model = sonnet 明示 / 単巡 / 6 軸(固定軸 impression-literal + 3 ポート挙動パリティ + 仕様実装一致 + 正規化の過剰併合/併合漏れ + inline 終端子網羅性 + 完了条件に対するテスト十分性)。
step 2 の operational copy は適用せず。差分に rules/** を含まず、skills/evolution-self-eval/SKILL.md の description: も無変更のため、草案は評価者の注入文脈に載らない(skill body は invoke 時の遅延ロード)。judge 型評価者の観測面が変わらないため、Procedure step 2 の分岐どおり SHA 直読へ倒した。step 5 の復元も不要。
逸脱記録: 初回 spawn の 3 体が全て基盤停止で verdict ゼロ。3 日連続の同型(#1644 / #1650 / 本 PR)。単巡 cap の限定列挙は評価者クラッシュ等の評価側の欠陥を指しており、基盤が実行を拒んで verdict ゼロならラウンドが成立していない、と読んで再走した(2 周目ではなく 1 周目)。Master の go-sign 済。なお停止の実体は月次課金上限ではなく 5 時間ローリングの使用量上限であり、ホストのエラー文字列 You've hit your monthly spend limit は実態と食い違う。#1647 の本文と過去コメントを訂正済。
採用 5 件(いずれも仕様文・コメントのみ、アルゴリズム不触)
| # | 内容 | 一致 |
|---|---|---|
| 1 | 括弧の範囲が ASCII 限定であることを仕様に明記(全角 () 追跡外/非対称 ( 以降が括弧内扱い)。正規形 step 2 も併記 |
3/3 |
| 2 | $axisCount の Ordinal 比較子コメントを訂正 |
3/3 |
| 3 | miss 判定が大小文字非区別であることを明記 | 2/3 |
| 4 | SKILL.md の "Putting the trailer on its own line ... is preferred" を削除 |
固定軸 2/3 → refine |
| 5 | (1 に含む)非対称 ( の挙動を仕様に明記 |
2/3 |
2 の裁定内容: 元コメントは重複検出器 $pairs の理由を類推で借りていたが、Get-AxisNormalForm / normalize_axis は正規リスト照合の前に小文字化するため、この表にはキーが大小文字差を持ったまま到達しない。比較子は現状の入力で一度も効かない。3 体が別々の軸から同じ結論に達し、親も axis = tolower(axis) の位置を実読して確認した。Ordinal 指定自体は残す — パリティの相手は現在の正規化器ではなく awk の意味論であり、正規化が小文字化をやめた時に黙って割れるのを防ぐ側の選択だから。その理由へ書き換えた。
記録のみ(採用せず)
- 終端子集合の腐り (3/3):
。/Root cause:/Domain:は現行の慣習に合わせた閉じた列挙で、新しい慣習が出れば同じ形で黙って死ぬ。#1632 が「最大の残余」と名指しした構造リスクと同一で、仕様節の追加はこれを緩和するが解消しない - 総称語の過剰併合 (1/3): 素の
Review/Loopが正規軸へ自動展開されうる。機構はコード追跡で確定、live ログでの発生は未確認。他 2 体は「正規軸同士の衝突」を見ており別の問いに答えていたため、割れではなく非重複 hits == 2分岐が到達不能 (3/3): 現行 10 軸の第 1 語が全て相異なるため。語彙についての事実であって欠陥ではない- bullet 形式の同一行 trailer 混入 (1/3): #1651 item 3 は inline に明示スコープされており完了条件外
- BMP 前提が強制されていない (2/3):
.ps1のコメントが述べる UTF-8 バイト順と UTF-16 序数順の一致は BMP 内で真だが、補助面の文字が来ないことは検査されていない。低severity
分離(新規起票)
- #1653 — 全角括弧が追跡外 (3/3) / 非対称
(以降のペアが落ちる (2/3)。後者は本 PR が持ち込んだ退行(旧コードの無条件 split なら拾えていた)。ただし live ログの軸タグ行 10 本を親が実測し、全角括弧 0 件・非対称括弧 0 件で現時点は未発火。挙動変更は指摘が要求する範囲を超えるため実装側へ戻す - #1654 — bash の
tolower()にロケール固定が無い (2/3、いずれも low confidence)。#1651 item 1 の完了条件はsortであり、その条件は満たされている(評価者 2 名が repo 全体 sweep で未固定サイトゼロを確認)。隣接残余 - #1655 — item 1 の挙動テストが CI で本当に culture-aware 分岐を踏んだか、ログから判別できない (3/3 が同一の未検証点を申告)
item 1 のテスト分割について
実装者が「挙動側だけだとどのホストでも通るテストになり計測器として成立しない」としてソースレベル表明を主体に据えた判断は、誠実と判定する。評価者 3 体とも独立に同じ結論に達しており、うち 2 体はソースレベル表明の regex sweep を自分で再現して非自明であることを確認している。ただし判別不能性そのものは残るため #1655 へ分離した。
目的
#1650 の brake 1 で検出され、port-verbatim 制約の外として分離した残余 4 件を解消する。4 件はいずれも「3 ポートが揃って同じ天井を持っている」形で、片方だけ直すと新しいポート間の食い違いを作るため、同時に変更した。
Closes #1651
変更点
issue の順序依存に従い、項目 4(書式の spec 化)を先に決めてから項目 2(軸名の正規化)を実装している。正規形の出典が無い状態で正規化規則を書かないため。
項目 4 —
**Axis tags**:書式の spec 化skills/evolution-self-eval/SKILL.md(layer: L2-evolution)に Axis tag line format 節を新設した。規定内容は 2 つのレイアウト、フィールド定義、軸名の normal form、1 行形の終端規則。3 ポートのコメントに「この節の実装であり、齟齬が出たら skill が正本」と明記した。これまで
**Axis tags**:はどの spec にも存在せず、hook のハードコードだけが根拠だった。#1632 F3(検出器が誰も書かない書式を読んでいて黙って死んでいた)と同一の構造リスク。項目 2 — 軸名の正規化
集計前に軸名を正規形へ畳む:
*除去 → 括弧修飾の切り落とし →-/_を空白へ → 空白畳み込み → 小文字化 → 10 軸のいずれか 1 つだけの語境界前方一致なら展開。alias table は作らず、10 軸リスト自体を正規語彙として使う設計にした(
characterはcharacter driftの一意な前方一致なので展開される)。曖昧な短縮形は展開せずそのまま残す。10 軸に無い自由記述の軸名(Instrument validity等)は正規化されたうえで独立に集計される。実ログ(live
self-evaluation_log.md)での効果:THRESHOLD_N=2)characterx2 /gist vs literalx2character driftx4 /expansion limitx2 /gist vs literalx2 /source checkx2Character drift/Character/Character(pronoun)/character-driftの 4 分裂が 1 軸に集約され、issue が指摘した「実際には繰り返している弱点が閾値未満に留まる」状態が解消されている。項目 3 — 1 行形の末尾セグメント
pair 列を括弧外の
。またはRoot cause:/Domain:ラベルで終端し、分割も括弧外の/のみで行う。これまで最後のセグメントが行末まで伸び、
Root cause: ...以降の自由記述がmissの部分文字列走査の対象になっていた。実ログでは混入テキストにmissが無かったため誤検出は未発火だったが、経路としては生きていた。あわせて、verdict の括弧内/((character application-moment / release gist)等)が分割されて phantom 軸を作る経路も閉じた。項目 1 — 並び順のロケール固定
出力順を決める並び替えをすべてロケール/カルチャ非依存に固定した。
| sortをすべて| LC_ALL=C sortへSort-Object(現在カルチャ依存)を排し[System.StringComparer]::Ordinalへrules injection の並びは
FullName(ネイティブ区切り)ではなく相対パス(/区切り)を対象にしたので、ロケール軸とセパレータ軸の両方でポートが一致する。SURFACE_CAPで切り詰める一覧では並び順が「どの項目が残るか」を決めるため、表示順だけの問題ではない。加えて検出器出力は diff-only emission の sha256 対象なので、順序がぶれると毎セッション差分が出る。adapter/codex/hooks/on-session-start.ps1のコメントにあった「No bash port pins LC_ALL=C」という記述も現状に合わせて更新した。テスト
AxisTagFormatTestを新設し、4 件それぞれを本番形の 3 ポートで検査する(既存 fixture が触れていなかった形を入れた: 表記ゆれ、日本語の行末自由記述、verdict 括弧内の/、非 ASCII の entry title、ケース非依存の並び替え差分)。項目 1 だけは instrument の性質が違う。culture-aware locale が入っていないホストでは挙動差そのものが観測できないため、ソース側の pin 検査(全ポートの並び替え site を列挙して未固定が無いことを assert)を回帰ガードの本体に置き、挙動側(culture-aware locale を渡して 3 ポートの順序一致を確認)は裏取りとして併走させている。挙動側だけだと「どのホストでも通る」テストになり計測器として成立しない。
docs
docs/6.-Adapter.mdの検出器 1 の記述を新 spec への参照に差し替え、並び順の決定性を追記した。確認
python -m unittest discover -s tests= 49 tests OK(ローカル、bash / pwsh 両方あり)release type
patch を提案する。cold-start の orientation surface に出る軸が増える点は system observable だが、変更規模は検出器 1 系統の不具合修正と spec 追加であり
rules/operations/release-version-rule.mdの minor 条件(large refactor / large structural change)に当たる規模ではない。補足
layer: L1-modelのファイルには触れていない(skills/evolution-self-eval/SKILL.mdはlayer: L2-evolution、adapter hooks は frontmatter 無し)。brake 2 の対象外。