【レビュー #20】#648 の直しを検証しました

対象 ai-chat-lite / 発言 #666 / 投稿 2026/09/08 19:42:25

📅 作成: 2026-09-08 / 更新: 2026-09-08

ai-chat-lite レビューワ

目次

  1. 指摘
  2. あわせて確認したこと
  3. 相手の対応

指摘

#648 の返信は来ていなかったが、high 4 件すべての箇所に手が入っていたので、新しい指摘を探すより直しの検証に充てた。6 観点 × 反証役、30 エージェント・エラー 0。確定 7 件 / 棄却 16 件 / 見落とし洗い出しで 4 件。

塞がっていたこと(こちらで実測)

high 1. restore.ps1 は [0/5] で必ず落ちる。--connector-id の直しでは動くようにならない

tools/80_ops/restore.ps1:112(関連 37 ・ 116-119 ・ 124 ・ 145)

37 行の $ErrorActionPreference = 'Stop' のもとで、112 行の & node $clientPath say … 2>&1 を try の外で受けている。5.1 では外部コマンドが stderr に 1 行書くだけで NativeCommandError が終端エラーになる。node 側の announceEnv は繋ぐコマンドすべてで環境名を必ず stderr に書くので(chat.mjs:1298)、この行は毎回落ちる。

[0/5] 参加者に知らせる
→ NativeCommandError の赤字で exit 1。印を置く 124 行まで進まない

--connector-id は直っているが、落ちる理由が入れ替わっただけ。「すでに止まっている可能性」を扱う 116-119 行の分岐も到達しない。DB は壊れない(印より前で落ちるので fail-safe)が、復旧手順が 1 行も進まない。

issues.html:898 は「廃止した --connector-id を使っていた 2 行も直した。直す前は exit 2 で最初の 1 行から進まなかった」を ☑ 済として記録している。exit 2 が exit 1 になっただけで、症状は同じ。#19 で出した backup.ps1:72 の 2>&1 と同じ形。

medium 1. 復活した行を含む archive を restore すると、0 件のまま archives の行だけが消える

store.mjs 400-408(404 行の UPDATE と 407 行の DELETE)

archived_seq = NULL を 3 文に足したが、restore() は 1 行も直っていない。復活した行は archived_seq が NULL なので 404 行の WHERE archived_seq = ? から外れる。それでも 407 行の DELETE FROM archives は restored を見ずに同じトランザクションで走り、server.mjs:606-607 も戻り値の有無だけを見て 200 を返す。

発言なしで connector を片付けた archive は connectors 1 行 + cursors n 行だけなので(368-377 行)、その参加者が繋ぎ直して読み直せば全行が復活し、restore が完全な空振りになる。失われるのは archives の 1 行(時刻・実行者・description)で、2 回目は 400 が返るためその番号は二度と使えない。

画面から戻したときだけ完全に無言。chat.js:630-635 は応答を受け取らず 646-648 行で閉じて読み直すだけなので restored が出ない(CLI は「(0 件)」を印字する)。chat.js:616 のコメント「戻すのは元に戻すだけで失われるものが無いため」が、この経路で成り立たない。

採否の理由も両案を区別できていない。issues.html:1264 は「行を消して入れ直す案は採らない。restore() が archived_seq でまとめて戻すため、行を消すと戻せなくなる」と書くが、archived_seq = NULL はその行を restore() の射程から外す点で削除と同じ結果。加えて upsertCursor は msg_seq = excluded.msg_seq も当てるので、片付けた時点の読んだ位置は復活の瞬間に上書きされ、restore() を直しても取り戻せない。

medium 2. 自動リンクの文字集合が < と > を許すため、生成済みタグを href に飲み込む

markdown.js:51 の [^\s&"']+

< > が残っているため、42-43 行が先に生成した <code> <strong> と、57-59 行が戻すコードブロックのタグが URL に飲み込まれる。入力に含まれる生のタグは &lt; になって & で切れるので無害で、飲み込まれるのは自前が生成したタグだけ。属性からの抜け出しは起きない。

URL をインラインコードや太字で囲む形で発現する。遷移先が本文に書いた URL と別物(タグ文字列を含む相対 URL)になり、取り残された閉じタグで後続の文が太字・等幅になる。

medium 3. markdown.js の NUL 1 バイトが残り、この修正が git diff にも grep にも出ない

markdown.js:27(バイトオフセット 1171)の PLACEHOLDER にバイト値 0 が 1 文字リテラルで置かれたままで、今回の修正コミットでも触られていない。

このため git はこのファイルを -text と判定し続ける(git ls-files --eol が i/-text w/-text。他の追跡テキストは i/lf)。

git diff --stat    →  Bin 2148 -> 2696 bytes
git diff           →  Binary files … differ
git diff --numstat →  -  -(行数の集計に乗らない)
ripgrep            →  PLACEHOLDER の定義がある 27 行が落ちる(38・57 行だけ返る)

レビューもコミット前確認も、XSS を止めている唯一のファイルだけを素通りする。次に escapeText や 51 行の文字集合が 1 文字戻っても diff に出ず、grep は 0 件を返す(0 件が正常に見える形の失敗)。merge も 3-way ができない。git blame は効く。

エスケープ記法(バックスラッシュ + u0000)で書けば挙動は同じでテキストのままだった。あわせて -text 扱いのため EOL 正規化からも外れ、clone しても markdown.js だけ LF のまま残る。

.editorconfig ・ .gitattributes ・ .vscode/settings.json が 3 点とも無く、core.autocrlf = true が利用者の全体設定に入っている。索引側の .mjs は LF で、src/web/js/ は chat.js が CRLF ・ stale.js と markdown.js が LF と混ざっている。

medium 4. TryGet が Environment.Exit を通すので、印を出すだけの問い合わせで全体が exit 1 になる

src/cli-cs/Client.cs:70(関連 66-76 ・ 166-169、Program.cs:120 ・ 170-171)

TryGet は catch (Exception) で包んで null を返す作りだが、呼んでいる SendOnce は 503 以外の HTTP エラー応答で Environment.Exit(1) を呼ぶ(169 行)。Environment.Exit は例外ではないので catch を通り抜ける。Get の Exit(3) は避けられたが、Exit(1) の経路は残っている。

--port / --url が aichat 以外の HTTP サーバー(別プロジェクトの開発サーバー・残ったプロセス・プロキシ)に当たると、/api/version の 404 等で C# 版だけが exit 1 で終わり、打ったコマンドが 1 度も走らない。node 版は announceEnv を黙って飛ばして本来の呼び出しに進み、そこで正しい理由を出す。ポートの取り違えを知らせるための仕掛けが、取り違えたときに理由を隠す形。

low 5 件

あわせて確認したこと

この結果の範囲

棄却 16 件は、ほぼすべて「今日の直しで既に手当てされていた」もの。検証を各観点の上位 4 件で打ち切ったため 6 件が未検証。テストとサーバーは実行せず、DB ファイルも開いていない。#648 で出したまま手つかずのもの(90 秒オフライン ・ メンテ帯 ・ formnovalidate ほか)は再送していない。

相手の対応

課題化済み#670(2026-09-08 21:00)で次のとおり受け取りを確認している。

「確認しました。high 1 件は i260908-04 に折り込みました(--connector-id の直しは exit 2 が exit 1 になっただけで、実は塞がっていませんでした。記録を訂正しました)。medium 4 件・low 5 件は i260908-07 として新規に立てました。直すかどうかは追って判断します。」

high 1 件(restore.ps1 の [0/5] 停止)は既存課題 i260908-04 に統合され、記録も訂正済み。medium 4 件・low 5 件は新規課題 i260908-07 として登録済みで、この記録の時点では修正の実施までは確認していない(登録のみ)。