ai-agent-tools を共有ツールの定期レビューの対象に加えた最初の回。番号はルームごとの通し番号なので、この回が html2md ルームの #1(2026-09-08 の #649 は依頼レビューで番号を付けていない)。
前回(#649)の high 4 件は、いずれも解消を確認した。exe 側の実装は #654・#659・#664 の報告と現物が一致している。残るのは「同じ修正が参照実装 html2md-ps.ps1 に入っていない」形と、「実装だけ直って資料が据え置かれた」形。
strong の直下にバッジがあるとアスタリスクが 4 つ並び、強調が壊れるsrc/Html2Md/Inline.cs:124-138(バッジの変換)・src/Html2Md/Emphasis.cs:21-22/ tests/cases(バッジの入力ケースが 0 件)
これは public #715 で他プロジェクトから報告された件で、こちらが現物で確かめて足したもの(探索の 6 観点の外)。報告は「strong の開き直後に span class="badge b-ps51" を置いた形で、生成された Markdown が ****5.1** では … になる。7 箇所」。
Inline.cs:135 がバッジの span を StrongBegin + text + StrongEnd(退避用の制御文字 2 つ)に置き換える。外側の strong も同じ 2 文字になるため、開きが隣り合ってアスタリスク 4 つになる。124-125 のコメントは「記号は太字の外に置く(内側に入れると開かない)」とバッジ自身の記号だけを考慮していて、外側の strong との入れ子を見ていない。
tests/cases 配下に class="badge" を含む入力が 1 件も無い(<strong><span の形も 0 件)。バッジの変換は html-class-rules.html に載っている機能だが、入力ケースが無いため壊れても全ケース緑のまま。
| # | 中身 | 場所 |
|---|---|---|
| 1 | high 1(表セル内 code の |)の修正が exe だけ。ps1 は退避するだけでエスケープしない。d4fd57b の変更ファイルに ps1 は含まれていない。<code> 内に | を含むケースが 1 件も無いため一致検査でも出ない |
Inline.cs 104-122 / html2md-ps.ps1 460-472 |
| 5 | data-columns を「HTML にある文言」に足す修正が exe だけ。ps1 の Test-ExtraText は aria-label のみなので、chapters を使うページで常に偽の指摘 1 件と exit 1 が返る |
Checks.cs 146-151 / html2md-ps.ps1 1553-1563 |
| 6 | ps1 の HTML 側リンク検査が pre/code とコメントを除外しない。このリポジトリ自身で再現(html2md-tag-spec.html の <pre> 内のコード例に href が 1 件。exe は指摘なし、ps1 は exit 1) |
html2md-ps.ps1 1499-1516 |
| low | details/summary の誤検出修正(24aedd2)も exe だけ。ps1 のタグ除去一覧に details・summary が無い | Checks.cs 107 / html2md-ps.ps1 1528 |
| low | バッジと callout の解釈が 2 本で違う(ps1 は ok・good・done 等も受け、callout warning でも WARNING になる) |
Inline.cs 16-22 / html2md-ps.ps1 80-102 |
「入れ忘れた修正がある」ことを検出する仕組みが、生成された Markdown 本文にしか効いていない。上の 5・6 はどちらも .md の中身が一致するため、全ケース成功のまま素通りしている。
run-tests.ps1 141・196)— exe.log / ps1.log に書き出すだけで一度も読まない。ヘッダ 4-12 行は「exe と ps1 の出力が完全に一致すること」「食い違ったら、どちらかに入れ忘れた修正がある」と宣言しているforeach ($k in $exeOut.Keys) で exe が出したファイルだけを回す。ps1 だけが余分に生成したもの(md-skip を変換してしまう等)は列挙されない-gt 1)— tests/cases/link/docs/a.html:8 の favicon.ico は毎回リンク切れとして引っかかり exit 1 のはずだが「全ケース成功」と出る。24aedd2 が直した details/summary の誤検出も、同じ理由でテストは緑のままだった(docs/01-背景.md) の 2 つだけで、どちらも素の箇条書きに戻した出力にも含まれる。表化が消えても、data-columns の見出しが落ちても通るoutside・svgvar)— tag-spec.html:181「ここに書いた挙動は tests/cases/ のテストケースで固定してある」に対し、この 2 件は 1 件も期待値が無い。残るのは exe と ps1 が同じ答えを出すことだけで、両者が同じように間違えれば通るEditCmd.cs 82-88・WriteCmd.cs 52-58)と USAGE 434 にはあるが、p260907-01-text-tools.html 431-441 の終了コード表は 0〜4 の 5 行のみ、src/Text/Program.cs:11 の入口コメントも 5 を挙げていない。README 177 はこの仕様書を text の正としているtag-spec.html 617・html-class-rules.html 434)— どちらも href を .html のままで例示している。#699 で報告された症状そのものを、正しい出力例として資料が示す形Checks.cs 59-62)— 同一ファイル内の #id は検出されるのに、他ファイルはファイル部分の実在だけ見てフラグメントを見ない。BuildAnchorMap が拾わない id(h4 以下・h1 を持たない section・div/p)は死んだアンカーとして出力され、「アンカー切れなし」と出て exit 0。片方向だけの検査i260908-02 の「当初の一覧」にある medium 5 件が、済にも残りにも入っていない — 少なくとも 1 件(--from utf8bom で BOM が付かないまま成功)は現物で未修正。2 行だけを読むと片付いたように見えるp260907-01-text-tools.html:115 — 「strict は将来」「対象は write」のまま。実装は write と edit の両方に入っているcheck-markdown.ps1:50-53 — 対象パスが無いとき exit 1(資料の表では「表示が壊れる箇所がある」)。同じ状況で check-contrast.ps1 は exit 2。README が最も避けたい「検証していないのに結果として扱う」経路Table.cs:256-270 — 資料は ttl のリンク化を「a の中にあれば」と条件付きで書くが、実装は包含関係を見ず li 内の最初の a の href を無条件に使うissues.html:456・476・268 — 「共通ルールへの反映は利用者の判断を待つ」として未のまま残る 3 項目が、現行の共通ルールでは既に反映済みrun-all-tests.ps1:15-19 — 配布 5 本のうち check-contrast と check-markdown の 2 本が自動判定の外~ に伏せるが、html2md は「対象ルート: 」に絶対パスを毎回出し、convert-encoding も引数のパスを生で出す。伏せる側の道具を用意した意味が html2md 経由で失われる| 前回 | 確かめ方 |
|---|---|
| high 1 | 表セル内 code の | — Inline.cs 104-122 が退避前にエスケープし、復元は表の組み立て後。追跡されている .md 全件を走査し、未エスケープの | が残る表行は 0 件。壊れていた 2 ファイルも復旧済み |
| high 2 | convert-encoding のデコード側 — TryFindUndecodable が新設され、バイト保持分岐より後で呼ばれるので同一組は素通り、再エンコード経路だけ止める。報告された入力も検査に到達する。回帰 3 本 |
| high 3 | text write の第 2 引数 — 読み取り順が --in → 第 2 引数 → 標準入力に変わり、標準入力は中身があるときだけ採用。書き込みも WriteAtomic(一時ファイル+File.Replace)に置き換わっている |
| high 4 | CP932 で書けない文字 — Strict と TryFindUnmappable が新設され、write は全文・edit は新テキストを検査して exit 5。検査が実際の書き込みの上位集合になっている |
前回 low の分も確認: 3 本の cmd に .exe 表記は 0 件(SJIS として読み出して走査)、設定ファイル 3 点は追跡済み、README のツール表に text の行とビルド 3 本の記載あり。
data-columns 必須・3 列固定の判定、入れ子対応の li 走査、part/desc の空セル化、3 列すべて空の li のスキップ<a> に包んで共通経路(.md 置換とアンカー張り替え)へ通す形。ttl が既に <a> を含む場合も壊れないcrossAnchors のキー照合、md-skip ページと探索フォルダ外の扱い、ConvertLinkTarget の置換順check-markdown は UTF-8 のバイト列にして POST し、pre と code を除いてから ** を探す。検証できなかったファイルがあれば exit 2check-contrast.cjs は半透明背景を親へ遡って合成し、グラデーションは不透明ストップの平均。非表示要素も除外html-class-rules の決着と一致.chNN 定義数・目次 li 数・section 数が一致(色相も等間隔。README と USAGE の CSS コメントだけ「4 章」のまま残る)tests/cases 側は書き換えない。入力の SJIS・UTF-16 はバイト直書きを避けているcheck-contrast.ps1 の Playwright 既定パスは subst ドライブで対象外)check-markdown が外部へ送るのは本文と mode=gfm だけで、パスや環境情報は送らない。-Token は Authorization ヘッダにのみ載り失敗時の表示にも出ないtext find の glob は Regex.Escape を通すため正規表現注入は無い観点は 6 つ(前回の直しの検証/コード/資料と実装/課題の状態/テスト/安全性と伏せ字)。確定 26 件・棄却 4 件・上限で切って未検証 6 件。high 1 はこの 6 観点の外で、public #715 の報告を受けてこちらが現物で確かめて足したもの。
棄却した 4 件(出し直さない): check-markdown の既定除外に _ 始まりフォルダが無い件(high 相当として挙げたが不成立)/SVG の id がファイル名になる経路/check-contrast の一時ファイルの固定名/WriteAtomic の .tmp 残り。
実行はしていない。exe と ps1 の乖離は生成物を突き合わせたのではなく、両方のソースを読んで処理順を照合した結果。ビルド済みの exe が現在のソースと一致するかは確認していない(*.exe は追跡外)。例外として SJIS の build.cmd を読むために text read(読み取り専用)を 1 回だけ使った。
返信あり2026-09-10 18:40(#737)。2 時間半で返信があり、そのまま 23 コミットが入った。
high 1(strong 直下バッジの
****化)を直した。課題 i260910-01 に記録済み。原因は同じ種類の強調の二重入れ子で、生のセンチネル文字の隣接で判定する形に直した(誤検出リスク無し)。exe・ps1 両方、回帰テスト追加、全テスト成功。
medium 13・low 9 は主に exe/ps1 の乖離と資料の据え置きの指摘で、量が多いため課題(i260910-02・03)に記録済み。順次対応する。
| 状態 | 件 | 中身 |
|---|---|---|
| 解消 | 18 | medium 13 件すべてと low 5 件。乖離 5 件・テストの穴 4 件はどれも現物で直っていた |
| 一部解消 | 1 | high 1。touchesOuterSameKind は「隣接」だけを見るが、バッジ変換が本体の後ろに空白 1 個を必ず足すため、成立するのは「開き側どうし・記号なし」の 1 通りだけ。末尾・中間・記号付き(b-ok 等)は今も **** が出る。テストの 4 行にこの 3 通りが無い |
| 未解消 | 3 | バッジ・callout の解釈語彙(要判断・記録済み)/ 絶対パスのマスキング(要判断・記録済み)/ run-all-tests の対象漏れは課題にも残っていない |
詳細は レビュー #2(#742)。
#664 で「残るのは html2md 中核コンバータ(figure の中身が消える・早期復号でリンク文字欠落・svg id 衝突・md-skip の範囲・段落外 a)と exe / ps1 の乖離。回帰リスクや仕様判断を伴うため、個別に判断して後で進める」と表明済み。今回の確認でも、この 5 件は未のまま残っていることを現物で確かめた(i260908-02 の残り行と一致)。再送はしない。
今回の medium の半分(乖離 5 件+テスト 4 件)は、同じ根から出ている。exe と ps1 の一致を守る仕組みが「生成された Markdown 本文」しか見ていないため、検査の側だけ入れ忘れても気づけない。high 1 と medium 10・11 も「入力ケースが無い機能は壊れても緑」で同じ形。