ADR-0037 — render-safety verification の二層化 (static floor + deterministic browser ceiling)

Status: accepted · Date: 2026-06-04 · folio v0.5.0-draft · render-safety verification を二層化する — static floor (bash の既知パターン lint、 全 consumer 配布) + deterministic browser ceiling (headless chromium で実 render し flowchart の幾何 overlap を検出する playwright gate、 CI-only folio dogfood、 決定的ゆえ blocking)。 mermaid 図の render 後 overlap (文字がブロック/枠に重なる) は pure-bash の folio validate が見れない floor の死角ゆえ、 実ブラウザ render で補う。 ADR-0028 の ceiling は元から LLM ceiling 限定で、 その CI 不適格根拠 (LLM の非決定性) は決定的 render ceiling を排除しない点を明示化 (CI 採用基準=決定性)、 ADR-0020 validate scope を render-safety ceiling に拡張 (frozen 本文不変、 reverse-link + prose で trace)。 REQ-VER-021 の二層 prose を機構化 (専用 REQ-VER-022 は defer)。 retrospective (Slice A〜C SHIP 後の user 承認起票)

§1. Context

criterion-c (1.0.0 「実用に耐える」判断) で user が公開 architecture の folio-self-spec 図5 の subgraph 多行タイトル (「7 並列固定」等) が枠/子ノードに overlap しているのを発見した。 この種の視覚欠陥は過去の mermaid 問題 (#121 / #123) も全て人間の目視で発見されており、 folio validate の機械 gate を素通りしていた。

根本原因は構造的である: folio validatepure-bash で動き、 mermaid が render した後の DOM 幾何 (getBoundingClientRect 座標) を観測できない。 link-integrity / jsonld / EARS 等の構造は検査できるが、 「render しないと存在しない」テキスト-ブロック overlap は static 解析の原理的死角である。 floor (REQ-VER-021 render-safety gate、 Slice B) は既知の overlap-prone パターン (<pre class="mermaid"> 内の subgraph 多行タイトル) を static pattern-lint するに留まり、 「任意の」幾何 overlap は捕捉できない。

一方 ADR-0028 は「ceiling (Phase F の LLM review agent) は非決定的ゆえ CI gate に不適 = authoring advisory」と定義していた。 render geometry の検査は LLM でなく 決定的 (vendored mermaid + pinned browser を固定すれば座標は再現する) であり、 ADR-0028 の「ceiling」語が想定した非決定 LLM ceiling とは別カテゴリである。 ゆえに render-safety の上層 (ceiling) を CI に搭載するには、 「ceiling = CI 不可」 framing の射程を明確化する必要がある。

§2. Decision

§2.1 render-safety を二層化する (static floor + browser ceiling)

render-safety verification を二層で構成する:

floor は「既知パターンを全 consumer に安価に配る」/ ceiling は「任意の幾何 overlap を folio 自身で網羅的に検査する」の役割分担で、 floor が原理的に拾えない欠陥を ceiling が埋める (REQ-VER-021 の二層 prose を機構として実現する)。

§2.2 「ceiling」語を二分する — ADR-0028 の CI 不適格根拠 (LLM 非決定性) は決定的 ceiling を排除しない

ADR-0028 は ceiling を一貫して「LLM ceiling」 (Phase F の spec-review-* agent) と限定して定義済で、 それが「CI gate に不適」とした根拠は LLM agent の非決定性 (出力が再現しない) であった。 本 ADR は「ceiling」を 2 種に明示分割し、 この CI 不適格根拠が決定的 ceiling に及ばない点を clarify する:

すなわち ADR-0028 の CI 不適格根拠は (a) LLM ceiling の非決定性に紐づき、 (b) 決定的 visual ceiling を排除しない。 CI 採用の唯一の基準は「決定的か」であり、 LLM/render の別ではない (frozen な ADR-0028 本文は不変、 本 §2.2 はその適用解釈を clarify (明示化) する — ADR-0028 が広く ceiling=CI 不可と述べていたのを狭めるのではなく、 元から LLM 限定だった射程の外に決定的 ceiling が在ることを示す)。

§2.3 決定性は三重 pin で担保し、 ゆえに blocking とする

visual ceiling を blocking merge gate にする正当化は決定性である。 三重 pin で render を固定する:

detector の主軸 (多行 cluster-label の height) は font-size 駆動で行数に比例するため font 差に頑健。 面積比 detector は text 幅依存だが mermaid dagre layout が node box を label 幅に self-normalize するため conservative threshold を跨ぎにくい (font 摂動の実測で交差比は不変)。 三重 pin により render は再現的で、 blocking が正当化される。

§2.4 fail-closed + constitution の advisory carve-out

§2.5 検出範囲 = flowchart 3-class、 完全網羅は normative 化しない

ceiling が検出するのは flowchart/graph の 3 class: (1) 多行 cluster-label (subgraph タイトルの縦 overflow)、 (2) label-over-foreign-node (text が自分以外の node 矩形と交差)、 (3) node-over-node (layout collision)。 非 flowchart 図 (sequence/state) は .node/.cluster を出さず vacuous、 横溢れ / viewport clip は未対応 (follow-up folio-w5z)。 「任意 overlap の完全網羅」は normative 化しない (best-effort tier): render 環境差 + 図 type 多様性ゆえ no-gaps 保証は過大で、 floor/ceiling の二層分業 (REQ-VER-021) を精緻化するに留める (完全網羅を約束する supersede ではない)。

ceiling の WHAT は REQ-VER-021 の二層 prose (「任意 overlap の網羅は browser-render を要する ceiling の領分」) を anchor とし、 専用 REQ-VER-022 は defer する: folio の既存 ceiling (Phase F spec-review-fidelity) も独立 REQ を持たず floor REQ の prose 内に位置づけられる先例に倣う (HOW = tests/render-gate/P-11 隔離・executable、 P-13 WHAT↔HOW 分離)。

§3. Consequences

Positive

Negative

Neutral / TODO

§4. Alternatives Considered

採否
二層 (static floor 全配布 + deterministic browser ceiling CI-only blocking、 採用)採用 — floor が原理的に見れない render 幾何を ceiling が実ブラウザで埋める。 決定的ゆえ blocking、 browser 依存ゆえ consumer 非配布の folio dogfood。 完全網羅は best-effort (flowchart 3-class) で REQ-VER-021 を精緻化
ceiling も全 consumer に配布 (browser gate を consumer validate に同梱)不採用 — browser + chromium download は consumer の bash/yq/jq stack に重く、 folio の「pure-bash floor を全配布」の軽量性を壊す。 ceiling は folio dogfood に留め、 consumer には floor (static lint) のみ配る
ceiling を advisory (report-only、 non-blocking) にする不採用 — render geometry は決定的ゆえ ADR-0028 の CI gate 基準を満たし blocking 可能。 advisory に格下げすると視覚欠陥を merge block できず検出力を活かせない。 fail-closed + 三重 pin + constitution carve-out で blocking を安全化 (§2.3/§2.4)
floor (static lint) だけで完結させる (ceiling を作らない)不採用 — pure-bash は render 後 DOM 幾何を原理的に見れず、 既知パターン以外の任意 overlap (図5 以外) を捕捉できない。 criterion-c の視覚欠陥クラスは static 解析の死角ゆえ browser ceiling が要る
任意 overlap の完全網羅を normative 化 (全図 type・横溢れ含め no-gaps 強制)不採用 — render 環境差 + 図 type 多様性ゆえ no-gaps 保証は過大で、 REQ-VER-021 の floor/ceiling 二層分業を逸脱。 flowchart 3-class の best-effort に留め generality は folio-w5z へ漸進 (§2.5)
ADR-0028 を改訂して「ceiling」定義を直接書き換える不採用 — ADR-0028 は frozen (CLAUDE.md §2、 P-10 隣接の historical record)。 本文を触らず、 本 ADR が「ceiling 語を二分し CI 基準=決定性を render ceiling に適用」の適用解釈を授権する (reverse-link + prose で trace)

§5. Trace