Skip to content

msys path conversion argv vs script literal

smileygames edited this page Sep 7, 2026 · 1 revision

MSYS のパス変換は argv に効き、スクリプト内リテラルには効かない

Question

Git Bash 上で bash で一時ファイルへ書く → Python でパッチ → gh --body-file で読む を繋いだとき、中間ステップが落ちても最終ステップが成功し、更新したつもりの無い古い内容が書かれるのはなぜか。そして Li+ source にこれを禁じる規則を足すか。

Current resolution

機構 = MSYS のパス変換は argv に効き、スクリプト内の文字列リテラルには効かない。同じパス文字列を両方で使ったときにだけ踏む。

規則は足さない。足す対象がリポジトリに存在せず、足せば既に在る指示の二枚目になる。

機構の実測

ずれているのは Python から見た /tmp ではなく、/tmp という文字列がどこで変換されるかである。

$ python -c "import os;print(os.path.abspath('/tmp/x'))"
C:\tmp\x                                    ← スクリプト内リテラル。変換されない

$ python -c "import sys;print(sys.argv[1])" /tmp/x
C:/Users/smile/AppData/Local/Temp/x         ← argv。MSYS が変換する

argv 経由なら Git Bash の MSYS 変換が働いて実体を指す。スクリプト内の文字列リテラルには変換が働かず、Windows のドライブ相対パスとして C:\tmp\x に解決される。

書き側も同様に実測できる。bash から echo > /tmp/probe で書いたファイルは C:/Users/smile/AppData/Local/Temp/probe に落ち、同一プロセスの Python から os.path.exists('/tmp/probe') は False を返す。

したがって「bash で書く → Python でパッチ → gh --body-file で読む」は、書きと読みだけが実体を指し、中間のパッチが別の場所を叩く構造になる。gh はネイティブ Windows バイナリで argv 経由なので、書き側と同じ実体を読む。Python が落ちても gh はパッチ前の内容を読んで成功を返す。

なぜ痕跡が残らないか

読み方向は FileNotFoundError を上げるが、書き方向は黙って通る。当該ホストでは C:\tmp が実在し、中身も入っている(tmp.n4jWwC5RRw / tpl.json)。スクリプト内リテラルへの書き込みは例外を上げずに別ディレクトリへ着地する。

さらに、中間ステップの失敗と最終ステップの成功が別コマンドに分かれているため、最終ステップの成功だけを見ている限り失敗は観測されない。

踏む条件と踏まない条件

  • 踏む = 同じパス文字列を argv とスクリプト内リテラルの両方で使うとき
  • 踏まない = argv 経路のみで完結する書き方(heredoc + gh --body-file を 1 コマンドで繋ぐ等)

対照実測: 2026-09-06 に Claude Lay が発行した issue 本文 3 件・コメント 4 件はいずれも /tmp + --body-file だが、中間に Python を挟まないため踏んでいない(文字数まで照合済み)。

Edges

  • depends on — gate-miss-measured-not-enforced。「規則をもう一本足す反射」を禁じ、足す前に substrate と発生率を問う判断。本 entry の「足さない」側はその適用例であり、当該判断が崩れれば本判断も再評価対象
  • conflicts with — なし
  • supersedes — なし

Background

2026-09-06、#1884 / PR #1893 の brake 1 裁定中に、実装 subagent が報告した。issue 本文を書き換えるため一時ファイルへ dump → Python でパッチ → gh issue edit --body-file の順で実行したところ、Python 側が FileNotFoundError で落ちたのに gh はパッチ前のファイルを読んで成功した。

実害はゼロだった(同一内容の上書き。本文 2337 文字を検証済み、scratchpad 経路で再実行して復旧)。

初版の説明は誤っていた。 起票時は「Git Bash の /tmp と Python から見た Windows パスが同じ場所を指していない」と書かれたが、これは変換の所在を取り違えている。Claude Lay が上記の 2 コマンドを実測して訂正した。訂正前の説明でも「踏んだ」ことは説明できるが、踏まない書き方(argv 経路のみ)が区別できない。

Constraints

2026-09-07、main の全ソースに当てた結果:

  • --body-file はリポジトリ全体で 0 件。 gh の呼び出し方を Li+ source が規定している箇所自体が無い
  • /tmp は 5 件、すべて adapter/*/hooks/on-session-start.sh の mktemp fallback($(mktemp 2>/dev/null || echo "/tmp/liplus-...-$$"))。bash 内部で閉じており、argv とスクリプト内リテラルの分岐を持たないため本機構には当たらない

起票時の本文は /tmp を「一度も現れない」と書いたが、これは不正確だった。ただし当該 5 件は上記のとおり機構の適用外であり、結論は動かない。

踏み方は Li+ source の指示に従った結果ではなく、実行した agent がその場で選んだ経路である。直すべき literal がリポジトリに存在しない。

一時ファイルの置き場については、少なくとも当該セッションのハーネス側 system prompt が scratchpad ディレクトリを名指しし /tmp ではなくそちらを使うよう指示している。同じ指示が全 agent・全ホストに在るかは未検証。

Conclusion

採用 — Li+ source は変更しない。記録は判断記録(本 entry)に置く。

rules/model/subtractive-structural-beauty.md Spec write の binary(required か unnecessary か)で言えば unnecessary 側。加えて「実行が保証されない手順」を足す形になるため、同ファイルが構造への置換を要求する側でもある。

棄却 — rules/ / skills/ / adapter/ に「一時ファイルは scratchpad に置け」「/tmp を使うな」に類する行を足す案. 足す対象の literal が無く、既に在るハーネス側指示の二枚目になる。

残る価値 — 機構そのもの(MSYS の変換が argv には効きスクリプト内リテラルには効かない、その結果として中間ステップの失敗が痕跡を残さない)は、次に同型を踏んだときの判定材料になる。踏んだ回に「なぜ古い内容が書かれたか」を再測定せずに済む。

Related

  • #1897 — 本 entry の起票元
  • #1884 / PR #1893 — 観測が発生した brake 1 裁定
  • #1750 — 「規則をもう一本足す反射は禁止」の出所

要求仕様書 (1-6)

参考文書 (A-L)

判断構造

Clone this wiki locally