仕様 (Spec) folio — sandbox verification framework
v1.2.0 · status: active · sandbox verification framework spec (機能単位 use case verification の SSoT)
この仕様が約束すること (1 文サマリ)
folio plugin を sandbox (隔離環境) で実機検証する枠組みの仕様です。 検証の 3 つの状態・何を検証し何を範囲外とするか・規範要件 (EARS 記法の REQ-VER / REQ-NAV)・段階的な実装の進め方を章立てで定め、 上位の原則 (constitution) や決定記録 (ADR) へ前方照会します。 本ページは元の verification.html を機械 SSoT から dual-audience で再生成したもので、 機械層の fold — 章ごとの『機械層』fold (地の文) と各要件の『normative (machine)』fold (EARS 規範文) — を開くと精密な記述を確認できます。
まず読み方から。 着手前は §1 (検証契約の定義) と §3 (EARS 規範要件) を必読とし、 そのあと §2 (検証範囲) → §4 (実装段階) と読み進めます。 §5 の未解決事項と §6 の参照は補足です。
読み順: §1 概念 → §2 scope → §3 EARS 規範 → §4 実装。 着手前は §1 + §3 必読。
「verification (検証)」という言葉の正式な定義をここに置きます。 検証には 3 つの状態があり、 本枠組みが対象とするのはそのうち sandbox で実機検証された状態だけです。 他の文書はこの章を参照します。
「verification」 の canonical 定義章 (他 spec はここへ参照、 P-5)。
検証の 3 state を canonical 定義する (下表)。
| concept | 定義 | 担当 |
|---|---|---|
| sandbox-verified | plugin の機能 (use case) が tests/scenarios/ 配下の YAML scenario で実機検証され、 期待挙動 (exit code + stderr + baseline) と一致した状態 | folio framework (本 spec) |
| experiment-verified | spec 内の各 claim (HOW level) が個別実機検証された状態 (twill plugin が採用する 4-state lifecycle: proposed / accepted / experiment-verified / archived) | 本 framework scope 外、 将来別 plugin folio-claim-verifier (仮称) で対応 (ADR-0015) |
| unverified | 上記いずれも未実施。 spec 上の規範 (EARS REQ) は存在するが実機 verify がない初期状態 | — |
本 framework は sandbox-verified のみを対象とする MUST。
何を検証し、 何を範囲の外とするかの境界を定めます。 対象は plugin の挙動・spec どうしの整合性・マージ前の自動チェック (CI gate) の 3 つで、 個別実装の内部 test や AI 出力品質の評価は範囲外です。
検証対象と非対象の境界章 (in-scope 3 カテゴリ / out-of-scope 3 種)。
検証対象は plugin behavior・spec 整合性・CI gate (REQ-CI-010〜016) の 3 カテゴリ。
scope 外 3 種と、 in-scope な hook unit test の用語整理 (用語衝突)。
本 framework の検証対象は以下 3 カテゴリ:
.claude-plugin/scripts/ 配下で個別実装)。 ⚠ §4.1 Step 1 の 「hook script unit test」 は script の stdin / exit code / stdout の契約を実機検証する black-box 行動検証であり、 本項 (white-box 内部 test) とは別概念で in-scope (用語衝突の整理、 ADR-0026)検証すべき規範要件を EARS という記法で集めた、 本仕様の中心となる章です。 冒頭の図が検証の全体像 (scenario を runner が読み、 hook 模擬や CLI golden で実行し結果を判定する流れ) を示します。
規範要件を EARS で集約する章。 図 1 が verification model の全体像。
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#2a4d6e', 'primaryTextColor': '#ffffff'}}}%%
flowchart TB
accTitle: folio verification model
accDescr: scenario YAML を runner が読み、 hook mock payload (kind:hook) または CLI golden (kind:cli-golden) で実行し、 exit_code / file-state / golden-diff のいずれかで assert する流れ。
SC["scenario YAMLtests/scenarios/"]
RUN["runner.sh"]
HK["hook mockkind: hook"]
CL["CLI goldenkind: cli-golden"]
EX["assertexit_code"]
FS["assertfile-state (e2e)"]
GD["assertgolden diff"]
SC --> RUN
RUN --> HK
RUN --> CL
HK --> EX
HK --> FS
CL --> GD
classDef src fill:#e6f6f2,stroke:#1e7b65,color:#08131a;
classDef run fill:#2a4d6e,stroke:#08131a,color:#ffffff;
classDef br fill:#fefbea,stroke:#916626,color:#08131a;
classDef as fill:#fdf3f3,stroke:#b22323,color:#08131a;
class SC src;
class RUN run;
class HK,CL br;
class EX,FS,GD as;runner.sh が scenario YAML を hook mock / CLI golden で実行し、 exit_code / file-state / golden diff で assert する。検証 scenario の format と参照 req_id を定める (REQ-VER-001 / 002)。
検証 scenario は tests/scenarios/ 配下の YAML ファイル (構造は §3.2 で定義)。
REQ-VER-001: Each verification scenario file SHALL be a YAML file placed under tests/scenarios/ with the structure defined in §3.2.
各 scenario は canonical な req_id を 1 つ参照する (rules §10 / relations §5.3、 または verification 固有の REQ-VER-*)。
REQ-VER-002: Each scenario SHALL reference one canonical req_id from rules.html §10 (REQ-CM-* / REQ-CI-*) or relations.html §5.3 (REQ-REL-*), or be a verification-specific REQ-VER-* defined in this §3.
scenario YAML の 2 種 (hook / cli-golden) の schema を定める。
assertion の主軸を exit_code に定める (REQ-VER-003 / REQ-VER-006)。
scenario の assertion 主軸は exit_code とする。
REQ-VER-003: Each scenario expect block SHALL specify exit_code as the primary assertion.
scenario assertion は permissionDecision JSON を主軸にすべきでない (upstream bug 既知ゆえ、 §3.3 の exit_code 中心と対)。
REQ-VER-006: Scenario assertion SHOULD NOT rely on permissionDecision JSON output as primary, due to known upstream bugs (Issue #37210 / #33106 / #39344 / #18312、 すべて Closed/not planned 状態 ―― 詳細は research §2.6)。
golden baseline の 2-dir model と accept 規律 (REQ-VER-004)。
golden baseline は TypeScript compiler 流 2-dir model (reference/ = VCS golden / local/ = 実行生成・gitignore)。 更新は明示的 accept workflow (--accept) を経る。
REQ-VER-004: baseline file SHALL follow the TypeScript compiler 2-dir model: tests/baselines/reference/ (VCS 管理、 golden) vs tests/baselines/local/ (実行生成、 .gitignore)。 baseline 更新は明示的 accept workflow (例: runner.sh --accept) を経る MUST。
各 EARS pattern と Gherkin 構文 (Given / When / Then) の 1:1 対応を下表に示す。
| EARS pattern | Gherkin 対応 |
|---|---|
| Ubiquitous | Given (初期) + When (任意) + Then |
| Event-Driven | Given (前提) + When (トリガー) + Then |
| State-Driven | Given (状態) + When + Then |
| Unwanted Behavior | Given + When (エラー) + Then (復旧) |
| Optional Feature | Given (機能有効) + When + Then |
試作検証専用の REQ-VER-007〜023 を定義する (各 scenario file が SSoT 実装)。
spec_path 外への spec (.html) 作成は、 PreToolUse path-boundary hook が deny する (exit 2)。
REQ-VER-007: IF a Write tool creates a .html file carrying <meta name="folio-doc-type" content="spec"> outside the configured spec_path, THEN the PreToolUse path-boundary hook SHALL deny (exit 2)。 scenario: scenarios/path-boundary.yaml (ADR-0003 §2.1 Option Pragmatic、 絶対 path 配下も第 2 branch *"/$2"* で gate)。
cluster の README.html index に未掲載の新規 .html 作成は、 PostToolUse README-index hook が notify する (exit 2、 file は作成済の staleness 通知)。
REQ-VER-008: IF a Write tool creates a .html file under a cluster directory that contains a README.html index, and the new file is not listed in that index, THEN the PostToolUse README-index hook SHALL notify (exit 2、 PostToolUse のため file は作成済)。 scenario: scenarios/readme-index.yaml。 これは check 型 staleness 通知で、 ADR-0003 §2.1「README update」の試作 slice。 ADR literal は「自動更新 (mutate)」だが 試作は check 型を採用 (divergence trace = specs/README.html §6.2)。
live-load した session で plugin を統合検証するとき、 agent が e2e runbook を実 Edit/Write で歩いて live hook を発火させ、 観察を golden baseline に記録する。 一次 assertion = file 作成有無 (deny ⇒ 不在 / notify ⇒ 存在)。
REQ-VER-009: WHEN the folio plugin is verified as an integrated tool in a live-loaded session (§4.1 Step 2 worktree integration), an agent SHALL walk the e2e runbook scenarios (tests/e2e/runbook.md) by issuing real Edit/Write tool calls so the live hooks fire, and SHALL record per-scenario observations as golden baselines under tests/e2e/baselines/reference/。 primary assertion = file 作成有無 (PreToolUse deny ⇒ file 不在、 PostToolUse notify ⇒ file 存在 + 通知)。 deny は transcript に確実には現れない (research §2.6) ため、 exit code でなく file system state を一次 assertion とする。 実装 = tests/e2e/ (runbook + golden、 sandbox 単体テストの mock payload 検証 (REQ-VER-001〜008) とは別 layer の統合検証)。
folio inventory は relations §4.1 schema 準拠の inventory.json を生成する (§4.4.1 の dir を走査、 cli-golden diff で検証、 非決定的 generatedAt は除外)。
REQ-VER-010: WHEN the folio inventory CLI subcommand runs, it SHALL produce an inventory.json conforming to the relations.html §4.1 schema (required fields $schema / generatedAt / folioVersion / specs[]), scanning the directories defined in relations.html §4.4.1。 conformance は §3.4 golden baseline との kind: cli-golden diff で検証する (非決定的 generatedAt は normalize.exclude_paths で除外)。 scenario: scenarios/inventory-gen.yaml (REQ-VER-011 harness が実行)。
kind: cli-golden の scenario は、 harness が bin/folio を repo root で実行し output と golden を jq 正規化して byte-exact 比較 + exit_code 一致を assert する (CLI 検証の SSoT、 hook 経路と別)。
REQ-VER-011: WHEN a scenario carries kind: cli-golden, the verification harness SHALL (1) run bin/folio with the scenario command at repo root, (2) normalize both the produced output_file and the golden via jq -S with normalize.exclude_paths deleted, and (3) assert byte-exact equality of the normalized JSON plus exit_code match。 golden 更新は明示的 accept workflow (--accept、 §3.4 REQ-VER-004) を経る MUST。 これは hook scenario (kind: hook) の exit-code 中心 assertion (REQ-VER-003) とは 別の assertion 経路を成す (CLI subcommand 検証の SSoT、 ADR-0018 で trace)。
folio prime は Tier 1 inventory digest (per-spec @id/title/summary/doc-type/status) を stdout に出力する (inventory.json stale なら自動再生成、 実 context 注入は SessionStart e2e で検証)。
REQ-VER-012: WHEN the folio prime CLI subcommand runs, it SHALL emit a Tier 1 inventory digest (per-spec @id / title / summary / doc-type / status、 relations.html §4.2 Tier 1) to stdout as agent-readable text, auto-regenerating inventory.json if missing or stale (ADR-0007 §2.4)。 conformance は §3.4 golden baseline との kind: cli-golden (capture: stdout + compare: text) diff で検証する。 scenario: scenarios/prime-digest.yaml。 SessionStart hook による実 context 注入 (ADR-0007 §2.1、 matcher 省略で全 source 発火) は e2e (REQ-VER-009 型) で検証 = SessionStart:startup 注入 PASS (fresh session で Tier 1 digest が context に注入されることを確認)。 PreCompact は ADR-0007 amend (2026-05-25) で除去 (stdout 非注入)。
folio validate は spec 集合を走査し (a) internal link-integrity + (b) jsonld structural の 2 gate を検査する (exit 0=clean / 1=violation / 2=tool error、 --root で検査 root override 可、 prose gate は試作 scope 外)。
REQ-VER-013: WHEN the folio validate CLI subcommand runs, it SHALL scan the spec set (relations.html §4.4.1 と同一 = design-intent/{spec,decisions,research} の <head> JSON-LD .html) and check two gates: (a) internal link-integrity — each JSON-LD relation @id resolves to an existing target file, and any #anchor resolves to an existing id in that target (REQ-CI-010 の (b)+(c)); (b) jsonld structural — parse + required @context/@id/@type + @context == object (REQ-CI-016、 ADR-0004 Option B Light)。 exit code は 0 = clean / 1 = violation 検出 / 2 = tool error (jq 不在等、 fail-closed)、 違反は human-readable report を stdout 出力する。 検査 root の override (test isolation) を提供する MUST。 external URL check と prose gate (REQ-CI-011〜015) は 試作 scope 外 (Phase X4+、 ADR-0020 §2.2)。 conformance: clean 集合は scenarios/validate-clean.yaml (cli-golden、 capture:stdout、 exit 0)、 violation 検出 (missing file ref / broken anchor / jsonld 構造違反の 3 種) は実装時に scenarios/validate-violations.yaml + seeded fixtures で 実証する MUST (exit 1 + report)。 ADR-0020 で trace。
folio init は canonical Layer 1 consumer layout (構造のみ) を scaffold する — design-intent/{spec,decisions,research}/ + 各 README.html + folio.config.yaml。 既存 file は preserve (idempotent)、 本文は seed せず lazy-materialize、 生成 skeleton は folio validate clean。
REQ-VER-014: WHEN the folio init [<dir>] CLI subcommand runs, it SHALL scaffold the canonical Layer 1 consumer layout の純粋な構造のみ — design-intent/{spec,decisions,research}/、 各 cluster の README.html (P-6 entry point、 index は実在 file のみ dc:hasPart 宣言)、 design-intent/spec/{constitution,overview}.html (project starter template、 通常 conforming JSON-LD)、 および folio.config.yaml (rules §2 schema: folio_version / spec_path / caller_marker_env / caller_marker_value / review_model) — そして既存 file は preserve (idempotent、 上書きしない)。 constitution / overview の本文 (実体) は seed しない — folio-architect の greenfield onboarding grilling が引き出した中身を Phase E で lazy-materialize する (§7.1、 ADR-0031 が ADR-0024 の seed 挙動を revise)。 これで空 placeholder が "done" に残る hollow-spec を構造的に排除する。 生成された skeleton は folio validate が clean (exit 0) である MUST (P-6 orphan + link-integrity + jsonld structural を満たす seed)。 exit code は 0 = success / 2 = tool error (fail-closed)。 conformance は scenarios/init-scaffold.yaml (cli-golden) で検証する: init を temp root に実行 → exit 0 + 生成 tree の file 集合 + folio.config.yaml の golden 比較 + folio validate --root <tmp>/design-intent clean。 init は tree (複数 file) を生成するため既存 cli-golden harness (単一 output) の拡張が要る可能性があり、 その HOW は実装時に確定する。 scope 決定は ADR-0024 (seed 挙動は ADR-0031 で revise) で trace。
folio fix は各 forward relation に対応する reverse relation を target に materialize する (idempotent、 exit 0)。 併せて folio validate は第 3 gate broken-reverse を持ち、 fix 適用後は 3-gate clean になる (detect↔remediate 対)。
REQ-VER-015: WHEN the folio fix CLI subcommand runs, it SHALL materialize, for each forward relation declared in any spec (relations §5.1 の 6 dual pair), the corresponding reverse relation on the target spec's <head> JSON-LD (REQ-REL-002)、 idempotent に (既存 reverse は重複追加しない)、 exit 0。 併せて folio validate は第 3 gate「broken-reverse」を持ち、 forward に対応する reverse が target に欠ける場合 violation を報告する MUST (REQ-REL-004、 exit 1)。 folio fix 適用後は folio validate (3-gate) が clean になる (detect↔remediate ペア)。 conformance は cli-golden + fixture で検証する: (a) fix が reverse を materialize + exit 0、 (b) fix 後 validate clean、 (c) idempotency (再 fix で no-op)、 (d) broken-reverse を seed した fixture に validate が exit 1。 scenario / golden / harness の HOW は実装時に確定。 scope 決定は ADR-0025 で trace (validate を ADR-0020 の 2→3 gate に拡張)。
folio-architect SKILL の Phase F は review agent (spec-review-{ears,vocabulary,ssot}) を並列 spawn し structured findings を集約する。 検証は structural (agent .md frontmatter + scoped name resolvable) + e2e (seeded violation の flag 観察) の 2 経路。
REQ-VER-016: WHEN the folio-architect SKILL executes Phase F (Quality Review), it SHALL spawn the Phase F review agents in parallel (spec-review-{ears,vocabulary,ssot} = X4-D の 3 軸、 残 3 軸 structure/temporal/stakeholder は X5+) and aggregate their structured findings (severity / location / 違反 rule / 修正提案)。 検証は 2 経路: (a) structural — 各 review agent の agents/<name>.md frontmatter (name / description / model / read-only tools) が妥当で、 scoped name folio:<name> が resolvable (frontmatter name 一致 + folio-architect SKILL が当該 scoped name を参照) であること (決定的、 sandbox 可)。 (b) e2e — 既知 violation (EARS 欠落 / forbidden synonym / domain 越境) を seed した spec に /folio-architect を invoke し、 3 review agent が該当 violation を flag することを観察する (REQ-VER-009 型 agent-driven runbook)。 LLM agent は非決定的ゆえ golden 比較でなく 「検出有無」 を一次 assertion とする。 conformance: (a) は scenarios/agent-structure.yaml (kind: agent-structural、 file 静的検査・決定的・exit 0)、 (b) は tests/e2e/ runbook S-G (live walk は plugin reload を要すため別 fresh session)。 review agent は read-only (Edit は folio-architect が集約後に適用、 caller-marker author 一元性)。 scope 決定は ADR-0027 で trace。
folio validate は object xref の 4 gate を追加検査する: resolve-integrity / uniqueness / tooltip-consistency (universal) + completeness (opt-in、 orphan-mention)。 broken-xref / stale-tooltip / orphan-mention は exit 1 + report (rules §9 / ADR-0034 §2.6)。
REQ-VER-017: WHEN the folio validate CLI subcommand runs, it SHALL additionally check four object cross-reference gates (ADR-0034 §2.6): (1) resolve-integrity — each <a class="xref" href> resolves to an existing definition site; (2) uniqueness — each canonical ID is defined at most once as an id-bearing SSoT element; (3) tooltip-consistency — each data-tooltip matches the definition's current essence (§9.2 contract); these three are universal. (4) completeness — orphan-mention detection, applied only to docs that opt in via <meta name="folio-xref-completeness" content="enabled"> (conditional, REQ-XREF-002)。 conformance は scenarios/xref-validate-clean.yaml (cli-golden、 capture:stdout、 exit 0) + scenarios/xref-validate-violations.yaml (broken-xref / stale-tooltip / orphan-mention の 3 種を seed、 exit 1 + report)。 real corpus は xref dogfood 前 (opt-in 0) ゆえ本 gate 追加後も validate-clean.yaml が clean を維持する (additive、 keystone)。
folio fix は opt-in doc の prose 内 canonical token 初出を <a class="xref" href data-tooltip> に wrap する (option F materialize、 href + essence は §9.1/§9.2 contract、 idempotent)。 RED (orphan-mention) → fix → GREEN (completeness clean)。
REQ-VER-018: WHEN the folio fix CLI subcommand runs, it SHALL, for each doc that opts into xref-completeness, wrap the first prose occurrence of each auto-materializable canonical token (P-N / REQ-* / ADR-NNNN) into <a class="xref" href data-tooltip> with the href and data-tooltip derived from the §9.1 registry / §9.2 essence contract, idempotently (re-run = no-op、 既存 xref を持つ token は skip)。 非 opt-in doc では no-op (real corpus 不変、 keystone)。 conformance は scenarios/xref-fix.yaml (kind: cli-fix: pre-fix validate が orphan-mention で RED → fix exit 0 → post-fix validate clean → 再 fix idempotent)。 scope 決定は ADR-0034 §2.5 (option F) で trace。
folio validate は glossary gate を追加検査する: marked term (<span class="term" data-term> に統一) の data-term が vocabulary.yaml に解決し、 data-tooltip が definition と一致すること。 <a class="term"> は anchor-residue として block し (span 統一、 rules §9.5)、 生成 glossary.html の term 集合の vocab 一致は nav-regen-drift の byte 検査が含意する。 folio fix は marked term の tooltip を definition から materialize する (glossary は Class B best-effort・機構詳細は machine 部)。
WHEN the folio validate CLI subcommand runs and a vocabulary.yaml with definition fields exists, REQ-VER-019: it SHALL check a glossary gate over each marked term (<span class="term" data-term="<canonical>">, whether explicit or auto-marked — 共に span 形に統一): (1) resolve — data-term resolves to a canonical entry in vocabulary.yaml; (2) tooltip-consistency — data-tooltip equals that entry's definition; (3) anchor-residue — enforcing the span-only marking convention of rules §9.5 (which is the normative SSoT) by blocking any <a class="term"> in living spec/research prose ([glossary-anchor-residue]、 vocabulary.yaml 解決に依らず); all block (exit 1) on failure。 generated glossary.html term-set parity with vocabulary.yaml is structurally implied by the nav-regen-drift byte check (no independent parity gate; the symmetric difference is surfaced in the regen-drift violation message, conformance scenarios/glossary-parity-drift.yaml)。 folio fix SHALL materialize each marked term's data-tooltip from the definition, idempotently。 unmarked term mentions SHALL NOT be gap-linted (Class B best-effort)。 vocabulary.yaml 不在 / definition 無しは gate skip (additive、 keystone)。 conformance は scenarios/glossary-validate.yaml (unresolved + stale-tooltip を seed した fixture に validate が exit 1 + report、 resolved+consistent な clean 側は real corpus の glossary dogfood が keystone)。 scope 決定は ADR-0034 §2.8 で trace。
folio validate は dual-audience-structural gate を検査する: data-audience="human" requirement container (verbose section.req + compact details.spec-row の両 form) に machine 対応部存在 (-1) / requirement-id 整合 (-2) / data-audience 値域 (-3) / machine 部 aria-hidden 不在 (-4) / EARS-pattern 整合 (-5) を強制し違反は exit 1。 意味的 fidelity は floor 対象外 = ceiling (spec-review-fidelity agent) の領分。
REQ-VER-020: WHEN the folio validate CLI subcommand runs, it SHALL check a dual-audience-structural gate over each data-audience="human" requirement container — both the verbose section.req card and the compact details.spec-row form, keyed by data-audience semantic (container-class 非依存、 ADR-0033 §2.5 / rules §7.2 2-form) — enforcing the five structural safety invariants REQ-DA-STRUCT-1..5: (1) the presence of a data-audience="machine" 子孫 (孤立 human の排除); (2) requirement-id 整合 (container id ↔ badge ↔ machine ears-id); (3) data-audience 値域 (machine | human のみ); (4) machine 部に aria-hidden 不在 (AI / accessibility-tree からの可視性保全); (5) EARS-pattern 整合 (human badge ↔ machine data-ears-pattern)。 違反は report + 非ゼロ exit (block gate)。 意味的 fidelity (essence が machine normative を忠実要約か) は本 floor の対象外であり、 ceiling = Phase F の spec-review-fidelity agent が担う (二層 enforcement、 ADR-0028 / ADR-0033 §2.4)。 本 gate は追加 arm として runtime-CDN script 検出 (rules §8 REQ-DA-JS-2 の vendoring 違反、 ADR-0039 §2.5 = 単一行 heuristic) も担う — conformance は validate-violations.yaml の cdn-script.html seed。 conformance は scenarios/validate-violations.yaml (da-violations.html が -1〜-5 を section.req + details.spec-row の両 form で 1 card 1 違反 = 計 10 違反 seed、 exit 1 + report) + scenarios/validate-clean.yaml (real corpus の dual-audience dogfood が clean 側 keystone)。 scope 決定は ADR-0033 §2.4 で trace (ADR-0020 の validate scope を dual-audience floor に拡張)。
folio validate は render-safety gate を検査する: <pre class="mermaid"> ブロック内の subgraph 多行タイトル (title bracket ["…"] に <br> を含む) を flag する。 mermaid が cluster-label の縦 padding を 1 行分しか確保せず 2 行目が子ノード/枠に overlap する render-後の幾何欠陥を、 DOM を見れない static lint が「既知の overlap パターン」として捕捉する (node label の <br> は対象外)。 違反は exit 1 (block)。 任意 overlap の網羅は browser-render を要する ceiling の領分。
REQ-VER-021: WHEN the folio validate CLI subcommand runs, it SHALL check a render-safety gate over each <pre class="mermaid"> block, flagging any subgraph whose title bracket (["…"]) contains a <br> (multi-line subgraph title) as a [render-safety] violation with non-zero exit, because mermaid reserves only one line of vertical padding for the cluster-label so the second line overlaps child nodes / the box border at render time — a post-render geometric defect the static, browser-less validate cannot observe directly, so the gate pattern-lints the known overlap-prone form (rules §4.5)。 node-label <br> は対象外 (node が自動 sizing するため overlap しない); mermaid を持たない spec は vacuously clean。 conformance は scenarios/render-safety.yaml (多行 subgraph タイトル + 通常 node label <br> を併存させた fixture に validate が [FAIL] render-safety + exit 1 + violation 1 件のみ = subgraph タイトルだけ捕捉し node label を誤検出しない)。 本 static pattern-lint は browser を要さず既知パターンを deterministic に捕捉する floor であり、 任意の render overlap (単行でも長すぎる label の溢れ等) の網羅は browser-render を要する視覚 gate (ceiling、 REQ-VER-022) の領分 (二層)。 scope は ADR-0020 の validate-scope を render-safety floor に拡張。
CI の render-gate job は corpus 全 page を headless chromium で 3 viewport (375/768/1280) 実 render し、 render 後の DOM 幾何から mermaid 4 class + chrome 幾何 2 class を検出して merge を block する (render-safety の ceiling 層・floor = REQ-VER-021 / REQ-VER-023)。 render 不足は fail-closed、 全 page × viewport の screenshot を CI artifact 保存する (golden 化しない)。 決定性は vendored mermaid + pinned playwright + pinned font の三重 pin で担保する (6 検出 class・selftest 13 case・分類詳細は machine 部)。
REQ-VER-022: WHEN the CI render-gate job runs (push / pull_request), it SHALL render every corpus page (repo-root index.html + design-intent/**/*.html) in headless chromium at three viewports (375×667 / 768×1024 / 1280×900) and probe the post-render DOM geometry, flagging six defect classes with non-zero exit (merge block): (1) cluster-label-multiline — subgraph タイトルの縦 overflow (rendered height を svg の viewBox→rendered 縦比 (scaleY) で intrinsic px に正規化し、 useMaxWidth:true の縮小 render でも閾値が scale に引きずられない — mermaid は uniform scale で render するため縦横比は同値); (2) label-over-foreign-node / cluster-label-over-node — text label が自分以外の node 矩形と交差 (面積比、 scale 不変); (3) node-over-node — layout collision; (4) content-clipped — 要素が svg 可視範囲を溢れて clip され読めない (svg overflow は既定 hidden。 判定は rendered px = 視覚上の clip 量で、 (1) の intrinsic 正規化とは意図的に非対称); (5) horizontal-overflow (chrome 幾何 arm、 ADR-0039 §2.8) — 本文が viewport を溢れ document 全体に意図しない横スクロールが発生 (scrollWidth > clientWidth + 2px。 overflow-x:auto の意図された scroll container 内の wide content は document に寄与しないため発火しない。 culprit は clip/scroll 祖先を持たず viewport 右端を越える最外要素を最大 5 件 + 超過は残余 entry で報告し、 特定不能でも document 溢れ自体を報告する = fail-visible); (6) nav-over-content (chrome 幾何 arm) — nav landmark と本文 block の矩形交差 (面積比 > 0.15。 通常 flow の隣接 block は矩形が交差しないため chrome 注入の位置事故・absolute 化の崩れでのみ発火。 closed <details> の中身は Chromium が非 0 の phantom rect を返すため checkVisibility() で非描画要素を除外する)。 render-gate が測る post-render DOM は post-JS の既定表示 — 既定の人間プレゼン化 (ADR-0040 / rules §11.3) 以降、 data-audience を持つ page では chrome script が付与する body.audience-human 状態の幾何を測る (人間が実際に見る view を検査する)。 audience-human が隠す display:none の machine prose も同じ checkVisibility() filter が非描画として除外する (machine 部は幾何検査の対象外 = false positive を生まない)。 mermaid arm (1)〜(4) の検査対象 svg と期待図数は実 <pre class="mermaid"> block 数 (HTML コメント内・prose 内 escaped 言及は数えない — 図は人間層に置き machine 降格しない規律 (rules §11.5) により、 隠れた図の render 不足 false-fail を作らない)、 chrome arm (5)(6) は全 page が対象。 It SHALL fail-closed: render 不足 (rendered svg 数 < 実 block 数) は見逃しでなく failure に倒す。 It SHALL save a full-page screenshot of every page × viewport as a CI artifact (人間 review の補助に充てるのみで golden 比較は行わない — pixel golden は brittle ゆえ幾何 detector + 人間の目の二層で代替、 ADR-0039 §2.8)。 constitution.html (自身の @type が FolioConstitution) は advisory (表示のみ・exit 非影響、 編集禁止資産への blocking は frozen deadlock になるため。 floor の carve-out と同基準)。 coverage の穴は SHALL emit a warning として可視化し silent pass にしない: 幾何対象 (.node/.cluster) を持たない図型 (sequenceDiagram 等) は vacuous-coverage、 .node は出すが図型 (aria-roledescription) が flowchart/graph でない型 (stateDiagram 等 — 構造だけでは flowchart と区別できない) は uncalibrated-coverage (幾何は測るが threshold は flowchart 較正)。 検出力は selftest (fixture 13 case、 violation kind の完全一致判定 (mermaid arm と chrome arm の和集合): detector (1)〜(6) 全 arm の発火 + scale<1 での (1) 正規化発火 (mutation 捕捉を実証済) + clean の誤検出なし + vacuous/uncalibrated 分類。 (2)(3)(4) の単独 arm は mermaid が通常生成しない欠陥ゆえ mermaid と同 class 構造の合成 SVG で、 (5)(6) は合成 page で固定。 同一 fixture を 375/768 で走らせる対が viewport plumbing 自体を、 意図された scroll container (overflow-x:auto) と closed-details phantom rect の clean fixture が chrome arm の誤検出排除を回帰固定する) が mermaid/chromium 版 drift への回帰ガード。 決定性は vendored mermaid + pinned playwright + pinned font (CI が fonts-noto-cjk を install) の三重 pin — deterministic ゆえ blocking 適格 (ADR-0037 §2.3、 ADR-0028 の LLM-ceiling 非決定性根拠は本 gate に適用されない)。 conformance は tests/render-gate/check.py --selftest + .github/workflows/ci.yml の render-gate job (browser 依存ゆえ sandbox scenario でなく CI-only dogfood — consumer の folio validate には入れない、 配布される floor は REQ-VER-021)。 scope/二層化の決定は ADR-0037、 3 viewport 化 + chrome 幾何 arm + screenshot artifact の拡張は ADR-0039 §2.8 で trace。
folio validate は readability-floor gate を検査する (P-14 の floor 層): block 3 系 — viewport meta 欠落 / 孤立 EARS (machine 対応部があるのに essence が空) / 生成 landing hero の空・link ゼロ (dead-end)、 warn 5 系 — essence 句点数 > 4 / 可視 prose 単一段落 > 2000 codepoints / 人間層可視 prose > 12,000 codepoints (上限 box、 ADR-0040) / 章要旨 (p.section-essence) 不在 / 図ゼロ×長文 (> 6,000)。 frozen は warn 除外・essence block は living spec/research のみ・圧縮 3 arm は圧縮対象 (living spec + cluster README) のみ (stuck-RED 回避 + 一次資料層 skip)。
REQ-VER-023: WHEN the folio validate CLI subcommand runs, it SHALL check a readability-floor gate (ADR-0039 §2.7、 P-14 floor) with three blocking arms and five warning arms: block (1) viewport — every scanned doc carries <meta name="viewport"> inside <head> (head-anchored 走査で body / <pre> の raw/escaped 言及に誤反応しない。 rules §4.6 MUST の機械化。 ADR-0039 §2.7 列挙の 3 block への追加 arm = chrome 注入 slice の独立 ceiling 指摘「生成 page のみ viewport を持つ非対称」の是正、 divergence trace は本 REQ); block (2) essence 非空 — data-audience="human" requirement container が machine 対応部を持つのに人間向け要旨 (class="essence" / "req__essence") が無い・空 = 孤立 EARS (REQ-DA-STRUCT-1 の対称拡張。 living spec/research のみ — frozen decisions/ は remediate 不可 = stuck-RED ゆえ除外、 glossary gate と同基準。 container 外の machine 要素検出は ceiling の領分); block (3) landing hero — 生成 index (folio-generated marker、 hand-authored は keystone skip) の folio:landing-hero curated region が非空で <a href> を 1 本以上持つ (空 hero は build の placeholder 再 emit により nav-regen-drift と併発し、 両 violation が同じ remediate = folio build を名指しする); warn (4) essence 句点数 — essence 内の 「。」 が 4 個超 (日本語は語数の決定的計測が不能のため句点近似、 閾値 4 = corpus 較正 2026-06-11 実測 max 4); warn (5) 可視 prose 段落長 — prose_only 可視域 (pre / code / aside.machine-readable / chrome 除外、 class="ears" 完全一致の EARS 全文も除外) の単一 <p> 段落が 2000 codepoints 超 (UTF-8 継続 byte 除去による locale 非依存の決定計測。 閾値 2000 = corpus 較正 2026-06-11: blob 実測 9545 / 次点 1429 (gate 同一計測の codepoints) — blob 再発のみ見張る)。 加えて人間層プレゼン圧縮の warn 3 arm (ADR-0040 §2.5 / rules §11.5、 対象 = 圧縮対象ページ (living spec (subdir=spec、 constitution は scan 対象外) + doc-type=cluster-readme)、 frozen ADR と research の非 README (一次資料層) は skip。 ADR-0040 §2.6 の入口群のうち landing / glossary.html は build 所有の生成物で human-authored 長文 prose を持たないため v1 の機械検査対象外 = cluster README のみ gate し、 残りは ceiling / §11.4 walk に委譲する narrowing): warn (6) 人間層可視 prose 上限 — 既定表示で人間が読む量が約 12,000 codepoints (読了 20 分 box) 超。 計測は data-audience="machine" subtree (降格部、 同名 tag の depth 追跡) と <table> (線形読書しない = 満重みにしない、 v1 は 0 重み) を行 granular に除去 → prose_only → tag 除去 → codepoint 数 (除去は undercount 方向 = warn が鳴りにくい conservative); warn (7) 章要旨不在 — h2 章を持つ living spec ページに p.section-essence が 1 つも無い (v1 は page 単位 presence、 章単位への昇格は migration 後較正で判断); warn (8) 図ゼロ×長文 — 実 mermaid block ゼロかつ人間層可視 prose > 約 6,000 codepoints (目標 box。 block 数は raw grep で HTML コメント内を数えうるが overcount = warn 抑制側で conservative)。 warn 全系は doc-type=adr (frozen) を除外し、 exit code に影響しない (REQ-CI-014 と同じ warn channel)。 block violation は exit 1 + remediate tool/操作の名指し (ADR-0039 §2.9)。 warn 閾値の block 化は ceiling の指摘頻度を見て判断する (ADR-0039 §2.7 — 圧縮 3 arm は migration (folio-bqq) 完了後の corpus 実測で再較正してから昇格判断)。 conformance は scenarios/readability-floor.yaml (block 2 arm + warn 5 arm + hero dead-end + 圧縮 arm の正例 compress-over / 負例 compress-demoted (降格 + table 除去の回帰ガード)、 fix/build 済 fixture で他 gate clean = isolation) + scenarios/readability-floor-hero.yaml (hero 空 + nav-regen-drift 併発の対)。 意味的可読性 (文言の質) は floor の対象外 = ceiling (readability review lens、 ADR-0039 §2.8) の領分 (二層)。
全 REQ の検証被覆を一覧し、 scenario 列が空 = 未検証 gap を可視化する (例 VER-006)。
| REQ | EARS | derives-from | scenario (verifies) | verified |
|---|---|---|---|---|
| VER-001 | Ubiq. | ADR-0013 / §3.2 | scenarios/*.yaml | ✓ |
| VER-002 | Ubiq. | rules §10 / relations §5.3 | (全 scenario) | ✓ |
| VER-003 | Ubiq. | ADR-0017 (exit-code 中心) | (全 hook scenario) | ✓ |
| VER-004 | Ubiq. | ADR-0018 / §3.4 | (cli-golden 共通基盤) | ✓ |
| VER-006 | Ubiq. | research §2.6 | — (未検証 gap) | ✗ |
| VER-007 | Unw. | ADR-0019 / P-3 | path-boundary.yaml | ✓ |
| VER-008 | Unw. | ADR-0003 §2.1 | readme-index.yaml | ✓ |
| VER-009 | Event | ADR-0017 | e2e/runbook.md | ✓ |
| VER-010 | Event | relations §4.1 | inventory-gen.yaml | ✓ |
| VER-011 | Event | ADR-0018 | (全 cli-golden) | ✓ |
| VER-012 | Event | ADR-0007 | prime-digest.yaml | ✓ |
| VER-013 | Event | ADR-0020 | validate-clean / -violations.yaml | ✓ |
| VER-014 | Event | ADR-0024 / 0031 | init-scaffold.yaml | ✓ |
| VER-015 | Event | ADR-0025 | fix-bidirectional.yaml | ✓ |
| VER-016 | Event | ADR-0027 / 0029 | agent-structure.yaml (+ e2e S-G) | ✓ |
| VER-017 | Event | ADR-0034 §2.6 | xref-validate-clean / -violations.yaml | ✓ |
| VER-018 | Event | ADR-0034 §2.5 | xref-fix.yaml | ✓ |
| VER-019 | Event | ADR-0034 §2.8 | glossary-validate.yaml | ✓ |
| VER-020 | Event | ADR-0033 §2.4 / rules §7.3 | validate-violations (da-violations) / validate-clean.yaml | ✓ |
| VER-021 | Event | ADR-0020 / rules §4.5 | render-safety.yaml | ✓ |
| VER-022 | Event | ADR-0037 + ADR-0039 §2.8 / VER-021 (二層) | render-gate/check.py --selftest + CI render-gate job (CI-only、 sandbox 被覆外) | ✓ |
| VER-023 | Event | ADR-0039 §2.7 / P-14 | readability-floor / -hero.yaml | ✓ |
| NAV-001 | Event | ADR-0035 §2.5 | nav-validate-clean / -drift / -keystone.yaml | ✓ |
| NAV-002 | Unw. | ADR-0035 §2.5 | nav-validate-deadlink.yaml | ✓ |
| NAV-003 | Unw. | ADR-0035 §2.5 | nav-validate-reachability.yaml | ✓ |
| NAV-004 | State | ADR-0035 §2.4 / ADR-0039 §2.2 | nav-validate-curated-roundtrip.yaml | ✓ |
| NAV-005 | Unw. | ADR-0035 §2.5 | nav-validate-consumer-deadend.yaml | ✓ |
| NAV-006 | Unw. | ADR-0035 §2.5 (link-rot) | nav-validate-readme-linkrot.yaml | ✓ |
| NAV-007 | State | ADR-0039 §2.3 | chrome-validate-clean / -drift.yaml | ✓ |
walkable nav と chrome の検証要件 REQ-NAV-001〜007 を定義する。
生成 SRS プレゼンの done-condition (VER-024〜030) は、 readability box 整合のため別 spec srs-verification.html へ分離した (ADR-0040)。 folio 自身の verification (本 spec、 REQ-VER-001〜023 / REQ-NAV-001〜007) と consumer 生成物 (SRS) の verification を別 audience に分ける。
schema_version: "0.1" req_id: "REQ-VER-001" # REQ-CM-* / REQ-CI-* / REQ-REL-* / REQ-VER-* のいずれか ears_pattern: "ubiquitous" # event-driven | state-driven | ubiquitous | unwanted | optional description: | 本 scenario が検証する規範要件の概要 (EARS 文)。 setup: env: { ... } # 環境変数 fixtures: [ ... ] # fixture file の配置 scenarios: - name: "..." # scenario 名 given: # Gherkin Given (前提) env: { KEY: "value" } # 環境変数 (任意、 例 FOLIO_ARCHITECT_CONTEXT) file_path: "..." # tool に渡す対象 file path (相対 or 絶対) content: | # Write tool 時の file 内容 (任意、 marker/JSON-LD 検出に使用) ... when: # Gherkin When (トリガー) tool: "Edit|Write|NotebookEdit" # mock payload の tool_name expect: exit_code: 0|2|N # § 3.3 (assertion 戦略) 参照 stderr_contains: "..." stdout_contains: "..." decision: "allow|deny" # informative、 主 assertion は exit_code teardown: cleanup_files: [ ... ] scenario kind: 上記は hook を mock payload で叩く kind: hook (既定、 省略可) の schema。 CLI subcommand (bin/folio) を golden 比較で検証する kind: cli-golden は次の field を持つ (REQ-VER-011、 hook payload field は無視)。 出力ソース capture (file | stdout) と比較方式 compare (json | text) の 2 軸で 2 種の CLI 出力に対応する:
schema_version: "0.1" req_id: "REQ-VER-010" # 検証対象 REQ kind: "cli-golden" # hook (既定) | cli-golden description: | CLI subcommand の出力を golden と比較する。 command: ["inventory"] # bin/folio に渡す引数列 capture: "file" # file (既定) = output_file を読む | stdout = command の stdout を捕捉 output_file: "inventory.json" # capture:file 時、 CLI が生成する成果物 (repo root 相対) compare: "json" # json (既定) = jq -S 正規化 + exclude_paths 除外 | text = plain byte-exact normalize: exclude_paths: [".generatedAt"] # compare:json 時、 除外する非決定的 field (jq path) golden: "baselines/reference/inventory.golden.json" # 期待値 (VCS 管理、 §3.4 REQ-VER-004) expect: exit_code: 0 例: inventory-gen.yaml (REQ-VER-010) は capture:file + compare:json (inventory.json を jq 正規化比較)。 prime-digest.yaml (REQ-VER-012) は capture:stdout + compare:text (folio prime の text digest を byte-exact 比較)。
各 EARS pattern と Gherkin 構文の対応:
変換 reference: conductofcode.io EARS-to-BDD / RequireKit。
以下は rules.html §10 / relations.html §5.3 に canonical REQ を持たない、 試作 plugin の検証専用 REQ (§3.1 REQ-VER-002 の verification-specific 条項に従う)。 対応 scenario file (REQ-VER-009 は agent-driven runbook) が SSoT 実装。
§3 の各 REQ-VER-* と §3.8 の REQ-NAV-* を derives-from (派生元原則 / ADR) × scenario (検証 file) × verified で一覧する (trace matrix は P-13 ゆえ普遍、 verified/scenario 列は verification mapping を持つ本 spec の opt-in、 ADR-0033 §2.6)。 folio validate は link しか検査しないため、 scenario 列が空 = link は valid だが未検証 = validate が捕らない gap をこの列だけが一目で surface する。
walkable nav (生成 index、 ADR-0035) と全ページ chrome (ADR-0039 §2.3) の検証 REQ。 実装時は scenario file を SSoT とする scenario-first 慣行で運用していたものを、 本節で定義 site (spec-row) に正式化する — 各 scenario の description は実装詳細 (fixture 構成・isolation 根拠) の SSoT として引き続き保持し、 本節は要件 (WHAT) の canonical 定義を担う。
検証を段階的に導入していくための 3 ステップの進め方と、 将来の完成形と今の試作段階がどう違うかを定めます。 いきなり全部ではなく、 軽い単体検証から統合・隔離へと積み上げます。
実装 phase 章: 3-Step approach と、 完成形 vs 試作段階の差異を定める。
段階導入の 3-Step approach を定める (REQ-VER-005)。
| Step | 方式 (WHAT) | API call | 適用 Phase |
|---|---|---|---|
| Step 1 | hook script unit test (script の stdin / exit code / stdout を実機で verify) | 不要 | Phase X3 最初 = MUST |
| Step 2 | worktree-based integration test (test project + hook 設定で sandbox 構築) | API key 注入で可 | Phase X3 中盤 = SHOULD |
| Step 3 | container-based isolation + integration test (外部 API allowlist 経由で外部依存制御) | 必須 (Anthropic API のみ allowlist) | Phase X4+ = MAY |
Phase X3 着手時に Step 1 (hook script unit test) を実装する (Step 2/3 は SHOULD/MAY で段階追加)。
REQ-VER-005: Phase X3 着手時点で Step 1 (hook script unit test) を MUST 実装する。 Step 2 / Step 3 は段階追加 (SHOULD / MAY)。
完成形 (long-term vision) と試作段階を 4 軸で対比 (完成形列は X5-γ で v1.0 実体へ reconcile)。
| 軸 | 完成形 (Phase X5+) | 試作段階 (Phase X3、 現在) |
|---|---|---|
| 検証対象 plugin | §7.2 8 specialist agents 完全実装 | §7.6 minimal (1 skill + 4 hook + 6 script + 1 CLI)、 ADR-0003 |
| scenario カバー範囲 | 全 use case (8 specialist + 全 spec file × CRUD) | 8 use case (caller marker / path boundary / JSON-LD lint / README update / context inject / 双方向 link / inventory regen / EARS coverage、 research §7.5) |
| runner | Inspect AI / Promptfoo 統合候補 | 独自 bash runner (tests/runner.sh、 試作) |
| CI 統合 | 全 scenario PR ごと auto-run + merge gating | scenario 1-3 (caller marker / path boundary / JSON-LD lint) のみ CI gate、 残りは warn のみ |
まだ解決していない論点と、 起票を保留している決定記録 (ADR) 候補、 すでに採用済みの関連 ADR を補足としてまとめます。 詳細は調査記録を参照します。
補足章: 未解決 Gap (§5.1)・起票保留 ADR 候補 (§5.2)・acceptance 済の関連 ADR (§5.3)。
未解決の Gap を優先度付きで一覧する (詳細は research §10.1)。
| Gap | 優先度 | 概要 | resolution 条件 |
|---|---|---|---|
| 1 | MUST | Issue #39344 fix 確認 | もし fix 済なら permissionDecision: deny 信頼可能、 REQ-VER-003 SHOULD NOT を更新 |
| 2 | MUST | runner.sh 具体仕様 (bash + yq vs Python + PyYAML) | Phase X3 Step 1 着手時に最低限の設計選択 |
| 3 | SHOULD | claude plugin validate --strict 全 check 項目 | Phase X3 validate 実行時に網羅 |
| 4 | SHOULD | Issue #18312 URL | research §2.6 Evidence Table 補完 |
| 5 | LOW | fast-check 公式 docs 直接確認 | Phase X4+ TypeScript plugin 実装時 |
起票保留 ADR 候補 (ADR-0017/0018 起票済・ADR-0014/0016 は未起票)。
| ADR ID 候補 | title | resolution 条件 |
|---|---|---|
ADR-0014 | EARS REQ → scenario 導出規範 | 本 §3.5 で実質規範化済、 別 ADR 不要と判断 (本 commit で見送り) |
ADR-0016 | exit code 中心 assertion 規範 | Gap 1 (Issue #39344 fix) 確認後 |
| ADR-0017 | hook unit test vs integration test 境界 | 起票済 2026-05-25 (Track 1/2 後 retrospective、 ADR-0017 / decisions/README §3) |
| ADR-0018 | golden baseline 管理 (2-dir model + accept workflow) | 起票済 2026-05-25 (Track 1 実装後 retrospective、 ADR-0018 / decisions/README §3) |
本 spec 直結の acceptance 済 ADR (ADR-0003 / 0013 / 0015)。
詳細は plugin-sandbox-verification-research.html §10.1 Gaps を参照 (重複記述しない)。
本仕様が依拠する内部・外部の参照文書を一覧します。 読んでいて根拠をたどりたくなったら、 ここから元の文書へ進めます。
本 spec が依拠する内部・外部参照の一覧。
この仕様が前方照会する主要な文書を挙げます。 上位の原則 (constitution の P-x) や決定記録 (ADR) への前方照会で、 verification は照会の終わりではありません — より上位の文書へとつながっています。
本文に出てくる専門語を、 やさしい言葉で説明します。 読んでいて分からない言葉が出てきたら、 ここに戻れます。