folio は AI agent (Claude Code) と人間が共同で「仕様書 (architecture spec)」 を HTML 形式で書き続ける meta framework (P-2)。 Phase X1-X2 で rules.html / folio-self-spec.html / relations.html / constitution.html の 4 file が確定 (commit 75232b9)、 Phase X3 で「architecture/ という構造を作成し維持する」 試作 plugin の実装に着手する。 plugin の 6 機能候補:
これらを Claude Code plugin の skills / hooks / scripts / slash commands でどう組み合わせるか、 業界事例 (pre-commit / husky / ESLint / log4brains / Beads / twill) を踏まえて設計判断する必要がある。
追加で Q3 use case matrix (8 use case × skill/hook/script/CLI) / Q6 folio Phase X3 minimal viable plugin 設計推奨 / Q7 ADR 起票候補 を全 worker findings の統合で導出。
配置先: <plugin-root>/.claude-plugin/plugin.json。 manifest は optional (省略時は directory name から自動導出 + default 位置からコンポーネントを auto-discover)。
| カテゴリ | field 群 | 規則 |
|---|---|---|
| identity | name / displayName / version / description / author / homepage / repository / license / keywords | name は kebab-case (skill namespace に使用)、 version は semver (省略時は git SHA) |
| component (加算) | skills | default skills/ に追加 |
| component (置換) | commands / agents / outputStyles / experimental.themes / experimental.monitors | manifest 指定時は default ディレクトリをスキャンしない |
| component (独自 merge) | hooks / mcpServers / lspServers | 設定 file への merge ルール |
| runtime | userConfig / settings / dependencies / channels / $schema | userConfig は enable 時に prompt、 ${user_config.KEY} で hook command / MCP/LSP config / monitor / skill content (非 sensitive) に展開 |
全 path は ./ 相対指定 MUST。 環境変数: ${CLAUDE_PLUGIN_ROOT} (絶対パス) / ${CLAUDE_PLUGIN_DATA} (永続データ) / ${CLAUDE_PROJECT_DIR} (project root)。
my-plugin/
├── .claude-plugin/
│ └── plugin.json
├── skills/<name>/SKILL.md ← Progressive Disclosure 3-tier (推奨)
├── commands/<name>.md ← legacy flat .md (Skills と同一 frontmatter)
├── agents/<name>.md ← subagent 定義 (plugin agent は hooks/mcpServers/permissionMode 非対応)
├── hooks/hooks.json ← 自動 detection (plugin.json 明示不要)
├── scripts/ ← 慣習 (必須でない)、 ${CLAUDE_PLUGIN_ROOT}/scripts/ で参照
├── bin/ ← Bash の PATH に追加
├── monitors/monitors.json ← experimental、 stdout 各行が notification
├── output-styles/, themes/
└── .mcp.json, .lsp.json
| event | trigger | matcher | blocking (exit 2) | 主用途 |
|---|---|---|---|---|
SessionStart | session 開始/再開 | source enum | 不可 | context injection (Beads pattern) |
UserPromptSubmit | user prompt 送信時 | なし | 可 | prompt 検査・拡張 |
PreToolUse | ツール呼出前 | tool name (regex) | 可 | caller marker check / file protection |
PermissionRequest | 権限ダイアログ | tool name | 可 | auto allow/deny |
PostToolUse | ツール成功後 | tool name | 不可 (stderr → Claude) | lint feedback / auto-fix |
PostToolUseFailure | ツール失敗後 | tool name | 不可 | エラーログ収集 |
PostToolBatch | 並列バッチ完了後 | なし | 可 | agentic loop 停止 |
Stop / SubagentStop | 応答終了 | なし | 可 | end-of-session 検証 |
InstructionsLoaded | CLAUDE.md ロード時 | 5 source enum | 不可 | spec 自動 inject |
FileChanged | 監視 file 変更時 | literal name (regex 不可) | 不可 | 外部編集追従 |
PreCompact / PostCompact | context compaction | なし | 可 / 不可 | 状態保存・復元 |
Hook 5 type: command (shell コマンド、 args で exec form 可) / http (JSON POST) / mcp_tool (MCP tool 呼出) / prompt (Claude model 推論、 yes/no) / agent (subagent spawn、 experimental)。
stdin JSON (PreToolUse 例):
{
"session_id": "...", "transcript_path": "...", "cwd": "...",
"hook_event_name": "PreToolUse",
"permission_mode": "default|plan|acceptEdits|...",
"tool_name": "Write", "tool_input": {...}, "tool_use_id": "...",
"effort": {"level": "..."}
}
stdout JSON output (PreToolUse の deny 例):
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "caller marker not set"
}
}
frontmatter 全フィールド optional、 description のみ強推奨:
---
name: skill-name
description: "What it does... Use when..."
when_to_use: "Additional context..." # description に連結、 合計 1536 文字上限
allowed-tools: "Read Grep Bash(git *)"
model: sonnet
effort: medium
context: fork # subagent で実行
agent: Explore
hooks: {...} # skill ライフサイクル hook
paths: "src/**/*.ts" # glob で自動起動限定
---
| Level | 内容 | ロードタイミング | tokens |
|---|---|---|---|
| Level 1 (Metadata) | name + description | 常時 (session 開始時) | ~100 / skill |
| Level 2 (Instructions) | SKILL.md body | trigger 時のみ | 5,000 tokens 以下 |
| Level 3 (Resources) | reference.md / scripts/ | 明示参照時のみ | unbounded |
Dynamic context injection (skill 本文): !`<command>` 構文で skill ロード前にコマンド実行・出力で置換。
公式 docs では skills と commands は統合。 /slash-commands 独立ページは存在しない (skills page に redirect)。
| 方式 | path | 呼び出し |
|---|---|---|
| Skills (推奨) | <plugin>/skills/<name>/SKILL.md | /plugin-name:name |
| Commands (legacy) | <plugin>/commands/<name>.md | /plugin-name:name |
Agents (subagent) 配置: <plugin>/agents/<name>.md。 v2.1.63 で Task tool → Agent tool 改名 (Task は alias 継続)。 plugin agent では security 上 hooks / mcpServers / permissionMode が 無視される。 built-in: Explore (Haiku、 read-only) / Plan / general-purpose。
| pattern | 実現方法 | 公式記載 |
|---|---|---|
| (a) hook → script | hooks.json の type: "command" で ${CLAUDE_PLUGIN_ROOT}/scripts/... | あり |
| (b) skill → script | SKILL.md 本文で !`bash ...` または allowed-tools 経由 | あり |
| (c) skill → subagent (fork) | frontmatter context: fork, agent: Explore | あり |
| (d) slash command → skill | commands/ も skill system で動作 | あり |
| (e) hook → agent | type: "agent" (experimental) | experimental |
| (f) skill が hook を持つ | frontmatter hooks: でライフサイクル hook | あり |
| (g) hook が skill を直接 trigger | — | 公式 docs に記載なし |
| (h) subagent が skills プリロード | agent frontmatter skills: | あり |
| tool | 言語 | config | 実行モデル | 性能 | monorepo | 用途 |
|---|---|---|---|---|---|---|
| pre-commit (yelp) | Python | .pre-commit-config.yaml + 外部 .pre-commit-hooks.yaml | 順次 (default) | 中 (isolated env 構築) | 限定 | 多言語 / 外部 hook repo |
| husky | Node.js | .husky/ shell scripts | 順次 | 中 (Node 起動) | 手動 + lint-staged | JS/TS 標準 (週 5M DL) |
| lefthook | Go (single binary) | lefthook.yml | 並列対応 | 高 (Go binary) | root: で限定 | 高性能 monorepo |
| simple-git-hooks | Node.js | package.json | 順次 | 低 (zero deps) | 非対応 | 小規模 minimal |
| lint-staged | Node.js | .lintstagedrc / package.json | glob ごと並列 | 高 (staged のみ) | glob で自然対応 | husky/lefthook 併用 |
| pre-commit.ci | SaaS | .pre-commit-config.yaml の ci: section | PR 自動 | 高 (共有 cache) | repo 単位 | CI 強制 + autofix PR |
| 軸 | PreToolUse | PostToolUse | git pre-commit | CI |
|---|---|---|---|---|
| 発火 | AI ツール実行直前 | AI ツール成功直後 | git commit 時 | PR/merge 時 |
| 介入 (block) | 可 (exit 2 / JSON deny) | 不可 (stderr → Claude) | 可 (exit 非0) | 可 (status check) |
| bypass 可 | Claude 外編集は素通り | 同左 | --no-verify で完全バイパス | 不可 |
| cross-file | 1 file (Agent 経由で複数可) | 同左 | staged 全体一括 | repo 全体 |
| 自動修正 | PostToolUse で auto-fix | edit + stage 可 | hook 内 modify (複雑) | autofix PR |
Layer 1: PreToolUse / PostToolUse hook ← AI 編集時のリアルタイムチェック
Layer 2: git pre-commit hook ← commit 時 (human + AI 両方)
Layer 3: CI (GitHub Actions) ← merge gate (bypass 不可)
Xygeni / AdamJ の指摘: --no-verify の使用を完全に抑制することはできないため、 git hook でチェックしているのと同じ内容を CI/CD パイプラインでも実施することがベストプラクティス。
| use case | 推奨 | 理由 |
|---|---|---|
| caller marker check | PreToolUse + pre-commit hook | AI 編集 block + 人手保護 |
| JSON-LD lint | PostToolUse (即時 feedback) + pre-commit (--no-verify 保険) | AI へのリアルタイム feedback + final gate |
| broken link detection | CI | 全 file scan で hook には遅い、 PR merge gate が適切 |
| 双方向 link materialize | PostToolUse auto-fix | atomic write + git add の連携 (idempotent 必須) |
| pattern | 説明 | 採用例 |
|---|---|---|
| (a) Extension Point declarative 宣言 | 「宣言 manifest → runtime 登録」 | 全ツール (ESLint plugin.rules / Sphinx setup(app) / MkDocs entry_points / Antora register() / Docusaurus factory) |
| (b) Rule + Autofix + Meta 三位一体 | 検出ロジック + 修正 + メタ情報を 1 unit | ESLint {meta, create(context)} / Stylelint createPlugin(name, fn) |
| (c) Lifecycle Event 順序保証 | named event の pipeline 構造 | Antora 14 step / Sphinx 8+ event / MkDocs Global→Page 2 層 / Docusaurus loadContent→contentLoaded→postBuild / Backstage 3 stage |
| (d) Context Object | plugin が runtime context 受領 | ESLint context / Sphinx app / MkDocs config/files/page / Docusaurus LoadContext + actions |
module.exports = {
meta: {
type: "problem",
fixable: "code",
hasSuggestions: true,
schema: [{ type: "object", properties: {...} }], // JSON Schema Draft-04
docs: { url: "..." }
},
create(context) {
return {
"Identifier": (node) => {
context.report({
node,
messageId: "...",
fix(fixer) {
return fixer.replaceText(node, newText);
}
});
}
};
}
};
Autofix 安全性原則:
hasSuggestions として提供meta.fixable 強制宣言: fix 提供時に未宣言ならエラー → 意図しない autofix の防止--fix-dry-run: 変更を実行せず結果のみ返すclass MyPlugin(BasePlugin):
@event_priority(-50)
def on_files(self, files, config):
files.append(File.generated(config, "virtual.md", content="..."))
return files
def on_page_markdown(self, markdown, page, **kwargs):
return modified_markdown # None → 元のまま
Event 分類:
on_startup / on_shutdown / on_serveon_config / on_pre_build / on_files / on_nav / on_env / on_post_buildon_pre_page / on_page_markdown / on_page_content / on_page_context / on_post_page順序保証あり: contextStarted → playbookBuilt → beforeProcess → contentAggregated (ファイル追加可能) → uiLoaded → contentClassified (分類済み catalog 使用可能) → documentsConverted → navigationBuilt → pagesComposed → redirectsProduced → siteMapped → beforePublish → sitePublished → contextStopped / contextClosed。
EntityProvider (エッジ)
→ getProviderName() で一意 bucket
→ connect(connection)
→ connection.applyMutation({type: 'full'|'delta', entities: [...]})
'full': bucket 全体置換
'delta': {added, removed}
→ EntityProcessor (中継、 推奨は Provider に移行)
→ preProcessEntity / postProcessEntity
→ 新 entity / relation / error を emit
→ Stitching (最終 catalog API 出力)
Provider 削除 → 即時 purge。 Processor はオーファン化のみ。 Provider は webhook / schedule / event 駆動 (柔軟)、 Processor は固定 loop。
GitHub source 直接 fetch (packages/core/src/adr/domain/Adr.ts):
class Adr extends AggregateRoot<Props> {
// ADR-A が ADR-B に supersede される時
supersedeBy(superseder: Adr): void {
const relation = new AdrRelation(this, "superseded by", superseder);
this.body.setHeaderMetadata("Status", relation.toMarkdown());
superseder.markAsSuperseder(this); // ← ADR-B 側に逆参照を自動追加
}
private markAsSuperseder(superseded: Adr): void {
const relation = new AdrRelation(this, "Supersedes", superseded);
this.body.addLinkNoDuplicate(relation.toMarkdown()); // 重複防止
}
}
双方向 materialize の仕組み:
adr-A.supersedeBy(adr-B) を呼ぶ"superseded by: [adr-B-slug]" に書き換えadr-B.markAsSuperseder(adr-A) 自動呼び出し"Supersedes: [adr-A-slug]" 追加 (重複防止付き)ADR link resolver の設計決定 (log4brains ADR 20201027): markdown parsing はドメイン層責務 (infra 層ではない)。 slug と filename を分離 → rename に強い。
steveyegge/beads は SQLite + Dolt のデュアルストレージで Issue dependency graph を管理し、 AI agent (Claude Code 等) に動的 context を inject する。
bd setup claude # ~/.claude/settings.json に自動登録
{
"hooks": {
"SessionStart": [{"command": "bd prime"}],
"PreCompact": [{"command": "bd prime --stealth"}]
}
}
bd prime: 現在の issue 状態 + workflow rules を agent に注入 (~1-2k tokens、 MCP mode は ~50 tokens)bd prime で real-time data 注入)コンテキスト効率化: IssueMinimal model で ~80-95% トークン削減、 ~10-50k → ~2-5k。 9 ツール対応 (Aider / Claude Code / Cursor / Factory.ai / Gemini CLI / Mux / Junie / OpenCode / Codex CLI)。
# adr new -s OLD_ID で supersede
_adr_add_link "$old_adr" "Superceded by" "$new_adr"
_adr_add_link "$new_adr" "Supercedes" "$old_adr"
# custom: TARGET:LINK:REVERSE-LINK
adr new -l "0004:Amended by:Amends"
_adr_add_link ヘルパで grep/sed/awk による markdown 操作。 MADR (Markdown ADR) 自体には自動 cross-ref 更新機能はなく、 テンプレートのみ提供 (adr-tools / log4brains 等ツール層でカバー)。
extends: ["spectral:oas"]
rules:
my-rule:
description: "パス名は kebab-case"
severity: warn # error / warn / info / hint / off
given: "$.paths[*]~" # JSONPath
then:
function: pattern
functionOptions:
match: "^[a-z/-]+$"
severity 5 段階: error / warn / info / hint / off。 autofix は公式未対応 (validation のみ)。 Specmatic は specmatic backward-compatibility-check で非互換変更を exit 1 でブロック (folio の breaking change gate に応用可能)。
verified: ローカル file 直接読取 (/home/shuu5/projects/local-projects/twill/main/plugins/twl/hooks/hooks.json 他)。
twill hooks.json の要点抜粋:
{
"hooks": {
"PreToolUse": [
{ "matcher": "Edit|Write|NotebookEdit",
"hooks": [{"type": "command", "command": "pre-tool-use-worktree-boundary.sh"}] },
{ "matcher": "Bash",
"hooks": [{"type": "command", "command": "pre-tool-use-host-safety.sh"}] },
{ "matcher": "Edit|Write",
"hooks": [{"type": "command", "if": "Edit(deps.yaml)|Write(deps.yaml)",
"command": "pre-tool-use-deps-yaml-guard.sh"}] },
{ "matcher": "Edit|Write|NotebookEdit",
"hooks": [{"type": "command", "command": "pre-tool-use-spec-write-boundary.sh"}] },
{ "matcher": "Skill",
"hooks": [{"type": "command", "if": "Skill(phaser-*)",
"command": "pre-tool-use-phase-gate.sh"}] }
],
"PostToolUse": [{ "matcher": "Edit|Write|NotebookEdit", ... }],
"PostToolUseFailure": [{ "matcher": "Bash", ... }],
"PreCompact": [...],
"PostCompact": [...]
}
}
spec-write-boundary.sh の caller marker pattern:
caller="${TWL_TOOL_CONTEXT:-}"
case "$caller" in
"") exit 0 ;; # user manual → allow
tool-architect) ... allow + warn ;;
*) jq -nc '{hookSpecificOutput:{permissionDecision:"deny",...}}'; exit 0 ;;
esac
R-N 規律体系 (R-1〜R-44) — folio が継承すべき pattern:
| Rule | 内容 |
|---|---|
| R-1 | 新 file 追加時は README.html index table に entry 追加 MUST |
| R-2 | 新 file 追加時は architecture-graph.html に node + edge 追加 MUST |
| R-3 | orphan 禁止 (inbound link ≥ 1) |
| R-4 | 削除/rename 時は inbound link 全更新 MUST |
| R-7 | spec 編集前に TWL_TOOL_CONTEXT=tool-architect 設定 MUST |
| R-8 | PR merge gate: broken link 0 + orphan 0 (CI) |
| R-14 | declarative form (時系列マーカー / 過去 narration 禁止) |
twill 多層防御 L1-L5: L1 Skill (SKILL.md + rules) / L2 bats (unit test) / L3 PreToolUse hook + MCP tool / L4 Vale / textlint (optional) / L5 CI (spec-link-check.yml + spec-content-check.yml)。
| pattern | 説明 | 採用例 |
|---|---|---|
| (1) Domain entity + lifecycle event + side-effect handler | mutation method → 自動副作用呼出 | log4brains supersedeBy() → markAsSuperseder() |
| (2) Hook gate + caller context injection | env var で caller 識別 + 機械的許可/拒否 | twill TWL_TOOL_CONTEXT / Beads bd prime |
| (3) 静的ルール定義 + 機械検証 | YAML/Java/MD 宣言 → validator → CI gate | ArchUnit / Spectral / twill R-N |
| (4) 動的 context injection | SessionStart hook → real-time data 注入 | Beads bd prime |
| (5) SSoT + cross-reference enforcement | 複数 SSoT 相互参照 + 追加削除時の副作用規律 | twill R-1〜R-8 |
| tool | 言語 | 役割 | folio 適合性 |
|---|---|---|---|
| ajv + ajv-cli | Node.js | JSON Schema validator | @context/@type/@id 構造 + required check に最適、 最速 |
| jsonschema + check-jsonschema | Python | JSON Schema validator | iter_errors() で全エラー取得、 Python ecosystem 統合 |
| pyld | Python | JSON-LD processor | FrozenDocumentLoader でオフライン、 on_property_dropped で unmapped property 検出 |
| jsonld.js | Node.js | JSON-LD processor | {safe: true} で lossy transform 検出、 カスタム documentLoader |
| pySHACL | Python | SHACL validator | RDF graph constraint (minCount/datatype/pattern)、 学習コスト高 |
| schemaorg-jsd | Node.js | Schema.org + ajv | Schema.org 型のみ、 folio カスタム型未対応 |
| TypeScript | hybrid | 2024/01 archived、 採用不可 |
folio 推奨 2 層構成:
@context存在・値、 @type enum、 @id URI format){
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["@context", "@type", "@id"],
"properties": {
"@context": { "type": ["string", "object", "array"] },
"@type": { "type": "string" },
"@id": { "type": "string", "format": "uri" },
"dc:title": { "type": "string" },
"folio:version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+" },
"folio:depends-on": { "type": "array", "items": {...} }
},
"additionalProperties": true
}
#!/usr/bin/env bash
set -euo pipefail
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
[[ -z "$FILE_PATH" || "$FILE_PATH" != *.html ]] && exit 0
[[ ! -f "$FILE_PATH" ]] && exit 0
VALIDATION_OUTPUT=$(python3 "${CLAUDE_PLUGIN_ROOT}/scripts/validate_jsonld.py" "$FILE_PATH" 2>&1)
VALIDATION_EXIT=$?
if [[ $VALIDATION_EXIT -ne 0 ]]; then
echo "JSON-LD validation failed: $FILE_PATH" >&2
echo "$VALIDATION_OUTPUT" >&2
exit 2 # ← Claude が stderr を読んで修正に取り組む
fi
exit 0
| pattern | 動作 | 用途 |
|---|---|---|
| Pattern A: warn only | exit 0 + stderr | dev 中・規約移行期 |
| Pattern B: block + feedback | exit 2 + stderr | 確定規約、 Claude に修正促す |
| Pattern C: auto-fix | idempotent edit + os.replace() で atomic | 安全な機械修正 (双方向 link 等) |
auto-fix の安全性: idempotent + Biome の lint --write バグから学び、 まずは warn → 手動修正が安全。
<!-- folio-lint-disable jsonld-required:dcterms:contributor --> (HTML comment、 lint script で parse).folio-lint.json で rule 別に error/warn/off.folio-lint-allow.json で file glob + rule 組み合わせwarn に降格、 移行後 error 復帰実績例: ESLint // eslint-disable-next-line + reportUnusedDisableDirectives / Ruff # noqa: E501 / golangci-lint //nolint:errcheck。
/dev/tty 不可)subprocess.run(['python3', 'script.py', file_path], shell=False)--dangerously-skip-permissions でも hook は動作 (PreToolUse deny で override 可能)[Claude Edit/Write]
↓ PostToolUse hook ← AI 即時 feedback (修正コスト最低)
[Claude が修正完了と判断]
↓ git commit
↓ pre-commit hook ← human + AI 両対応、 final gate (`--no-verify` 回避リスクあり)
[push]
↓ CI lint ← 唯一 bypass 不可
推奨: 3 つ全て併用。
| 隔離対象 | binding location | 例 |
|---|---|---|
| 環境変数 default 値 | .claude-plugin/plugin.json userConfig | caller_marker_env: "FOLIO_ARCHITECT_CONTEXT" |
| hook script 実装 | .claude-plugin/scripts/ | validate_jsonld.py、 pre-edit-boundary.sh |
| CI script 実装 | .claude-plugin/scripts/ | link-integrity.py (lychee wrapper) |
| Claude Code 固有命名 | CLAUDE.md (P-2 Markdown 例外) | Skill / Hook 命名規約 |
8 use case の best fit を全 researcher の findings から導出 (実装 Step 番号は §7.4 を参照):
| use case | skill | hook | script | CLI (bin/) | best fit | Step |
|---|---|---|---|---|---|---|
| architecture/ 構造強制 | folio-architect | PreToolUse | path-validator.sh | — | hook + script | 2 |
| README 自動更新 (新 spec で index 追記) | — | PostToolUse | readme-updater.py | folio inventory | hook + script | 4 |
| JSON-LD lint (Edit 完了時) | — | PostToolUse | validate_jsonld.py | folio validate | hook + script | 3 |
| broken link 検出 | — | — | lychee runner | — | CI (GitHub Actions) | 7 |
| 双方向 link auto-materialize | — | PostToolUse | bidir-materialize.py | folio fix | hook + script (Pattern C) | 8 |
| caller marker check | folio-architect (set 担当) | PreToolUse | check-caller-marker.sh | — | skill (set) + hook (verify) | 2 |
| context injection (Tier 1 inventory 自動読込) | folio-architect | SessionStart | inject-inventory.sh | folio prime | hook + script | 5 |
| inventory.json 自動 regenerate | — | PostToolUse + FileChanged | regen-inventory.py | folio inventory | hook + CLI | 5 |
folio-self-spec.html §7.2 が定義する完成形は 8 skill + 8 agent + 7-phase PR cycle だが、 試作駆動哲学に従い Phase X3 着手は 1 skill から:
| category | component | 役割 |
|---|---|---|
| skills/ (1) | folio-architect/SKILL.md | spec edit の唯一 author、 caller marker 設定の起点 |
| hooks/hooks.json (4 hook) | SessionStart | inventory inject (Beads pattern §5.2) |
| PreToolUse: caller marker | env var 未 set or 異値で deny (twill pattern §5.5) | |
| PreToolUse: path boundary | spec dir 外への作成を block | |
| PostToolUse: JSON-LD lint + README update | schema check + index 自動更新 | |
| scripts/ (6) | inject-inventory.sh | SessionStart / PreCompact で inventory.json を context inject |
check-caller-marker.sh | PreToolUse、 rules.html §10.1 REQ-CM | |
check-path-boundary.sh | PreToolUse、 spec dir 外の spec 作成を block | |
lint-jsonld.sh + validate_jsonld.py | PostToolUse、 ajv/pyld 2 層検証 | |
update-readme-index.sh + update_readme_index.py | 新 spec 追加時 README index 自動更新 | |
link-integrity.py | CI script (lychee wrapper)、 broken link 0 | |
| bin/ (1) | folio CLI | init / validate / inventory / prime / fix サブコマンド |
Step 1 (基盤): plugin.json + bin/folio (init + validate サブコマンド)
↓
Step 2 (boundary 強制): scripts/check-caller-marker.sh + check-path-boundary.sh
+ PreToolUse hook 登録
↓
Step 3 (lint): scripts/lint-jsonld.sh + validate_jsonld.py + PostToolUse hook
↓
Step 4 (auto-update): scripts/update-readme-index.sh + PostToolUse hook
↓
Step 5 (context injection): scripts/inject-inventory.sh + SessionStart / PreCompact hook
+ bin/folio prime
↓
Step 6 (skill): skills/folio-architect/SKILL.md (caller marker set の起点)
↓
Step 7 (CI integration): scripts/link-integrity.py + .github/workflows/spec-check.yml
↓
Step 8 (双方向 link materialize): scripts/bidir-materialize.py + PostToolUse hook (auto-fix mode)
各 step は独立して動作可能。 Phase X3 minimal は Step 1-3 のみで「動くもの」 を成立させ、 Step 4-8 は段階的に追加 (試作駆動哲学に合致)。
.folio-lint.json で集約--no-verify 保険) → CI (final gate)| ADR ID 候補 | title | 内容 | 関連 finding | 優先度 |
|---|---|---|---|---|
| ADR-0003 | plugin architecture: skill + hook + script + CLI 役割分担 | folio plugin の最小構成 = 1 skill (folio-architect) + 4 hook + 6 script + 1 CLI (folio)。 P-11 binding 隔離原則を構造化。 | §7.2 + §7.3 | MUST (Phase X3 着手前) |
| ADR-0004 | JSON-LD lint mechanism: ajv + pyld 2 層構成 | JSON Schema validator (高速 structural) + JSON-LD processor (semantic / unmapped property)。 PostToolUse hook で起動、 exit 2 + stderr で Claude feedback。 | §6.1 + §6.3 | MUST |
| ADR-0005 | 双方向 link auto-materialize の実装場所と pattern | log4brains の supersedeBy() → markAsSuperseder() domain entity + lifecycle side-effect handler pattern を採用、 PostToolUse hook で auto-fix mode (idempotent + atomic) |
§5.1 + §6.4 Pattern C | SHOULD |
| ADR-0006 | caller marker hook 実装 | PreToolUse hook + env var で spec edit を folio-architect skill 専任化、 twill spec-write-boundary pattern 継承 | §5.5 + §2.6 (a) | MUST |
| ADR-0007 | context injection mechanism (Beads-inspired) | SessionStart + PreCompact hook で inventory.json (Tier 1) を agent に inject、 動的 issue 状態は folio prime CLI 経由 |
§5.2 + §7.3 | MUST |
| ADR-0008 | 3 層防御原則 (PostToolUse / pre-commit / CI) | layer 別役割: PostToolUse=AI 即時 feedback, pre-commit=--no-verify 保険, CI=final gate (bypass 不可) |
§3.3 + §6.7 | MUST |
| ADR-0009 | linter false positive 抑制 4 戦略 | inline suppression comment / severity tuning / allowlist / temporal downgrade を併用。 ESLint + Ruff + golangci-lint の実績 pattern 継承 | §6.5 | SHOULD |
| ADR-0010 | autofix 安全性原則 | idempotent + atomic + suggestion/fix 分離 + meta.fixable 強制 + --fix-dry-run。 ESLint pattern を採用、 Biome 反面教師 | §4.2 + §6.4 | SHOULD |
| ADR-0011 | inventory.json auto-generation timing | spec file Edit/Write 後 (PostToolUse hook) + FileChanged hook (外部編集) で regen、 手動再生成は folio inventory CLI。 stale 状態は CI で detect |
§4.3 + §4.4 + §7.3 | SHOULD |
| ADR-0012 | hook 配置と plugin agent 制限の対処 | plugin agents は hooks/mcpServers/permissionMode 非対応 (security)。 これらが必要な場合は .claude/agents/ にコピー、 または skill frontmatter hooks: で対応 |
§2.5 | SHOULD |
起票手順: 各 ADR について (1) user に提示 → (2) user 承認取得 → (3) scratch/decisions/ADR-NNNN-<slug>.html に Accepted status で起票 → (4) 関連 spec section に cross-ref を追加。 CLAUDE.md の制約により自動起票は禁止。
host-safety / deps-yaml-guard / phase-gate / failure-learn / PostCompact) は省略している。 folio 設計時に実 file (~/projects/local-projects/twill/main/plugins/twl/hooks/hooks.json) を直接参照のこと。supersedeBy / markAsSuperseder / addLinkNoDuplicate) は verified だが、 行番号は本調査範囲外。 将来 log4brains バージョンアップで実装位置が変わっても追跡可能なように、 直接 URL (Adr.ts) を保持する。scratch/decisions/ 配下への新規起票は user 承認必須。 自動生成 + commit は禁止。 各 ADR を起票する前に user に提示し承認を取得する手続きを徹底する。architecture/ 未存在状態と check-path-boundary.sh の対象パス: folio CLAUDE.md では architecture/ dir は Phase X3 完了後に user 主導で作成予定であり、 現時点では存在しない。 §7.2 / §7.3 の path boundary 機能は実装時に対象パスを scratch/specs/ (現状) から architecture/spec/ (Phase X3 完了後) に切替可能な userConfig 駆動の設計とすること。 例: ${FOLIO_SPEC_PATH:-scratch/specs}。folio-init / folio-architect / folio-spec-edit / folio-validate / review-* × 4) を定義。 本レポート §7.3 の minimal viable plugin は「1 skill (folio-architect) から着手」 を推奨。 試作駆動哲学に合致した段階的成長 (1 → 8) を意図しており、 完成形 8 skill は Phase X3 完遂後 (Phase X4+) の目標と位置付ける。exit 2 + stderr が確実な fallback」 と述べているが、 twill spec-write-boundary.sh は実際には exit 0 + JSON output のみ (exit 2 未使用)。 folio 実装時は Phase X3 Step 2 (boundary 強制) で empirically verify する。overrides/ dir) の詳細 — folio が SSG patterns を直接採用しないなら不要prettier-plugin-html) の AST 詳細 — folio HTML spec の format 統一が必要になった時点で再調査全 76 sources (公式 docs 35 + GitHub リポジトリ 14 + 技術 blog / 比較記事 23 + ローカル twill 4)。 確認日 2026-05-23。
~/projects/local-projects/twill/main/plugins/twl/hooks/hooks.json — twill hooks.json (verified)~/projects/local-projects/twill/main/architecture/spec/gate-hook.html — twill gate-hook spec~/projects/local-projects/twill/main/plugins/twl/skills/tool-architect/refs/spec-management-rules.md — twill R-N 規律~/projects/local-projects/twill/main/architecture/decisions/ADR-0013-spec-clean-architecture.md — twill ADR-0013/tmp/research-search-5592bf08/