アーキテクチャ
このページでは、heal がどんなファイルを作り、いつ書き出され、何を含むかを説明します。ナッジが出ない原因を調べたり、JSON 出力に対してスクリプトを書いたり、heal がバックグラウンドで何をしているのかを理解したいときに役立ちます。
git commit │ ▼.git/hooks/post-commit ──► heal hook commit │ ├──► オブザーバー(LOC, complexity, churn, …, lcom) │ (run_all を 1 回; 結果は下流で使用) │ └──► stdout: Severity ナッジ (Critical / High Finding のみ)
ユーザー: heal status(または `claude /heal-code-patch`) │ ▼heal status ──► calibration.toml で Finding を分類 │ ├──► .heal/findings/latest.json │ (FindingsRecord — TODO リスト) │ ├──► fixed.json ↔ regressed.jsonl を整合 │ └──► Severity ごとのビューを stdout に描画heal は単一バイナリです。両方の経路がこれを通ります。デーモンも、スケジューラも、バックグラウンドプロセスも、履歴ストリームも一切ありません。post-commit フックは全オブザーバを 一度だけ 実行してナッジを出し、終了します(永続化は行いません)。
オンディスクのレイアウト
Section titled “オンディスクのレイアウト”heal init 直後:
<your-repo>/├── .heal/│ ├── .gitignore # 自動 — 将来用の予約(現状は空)│ ├── config.toml # 自分で編集する(git 追跡対象)│ ├── calibration.toml # 自動 — heal init / heal calibrate(git 追跡対象)│ └── findings/ # git 追跡対象 — チームで TODO を共有│ ├── latest.json # 現在の FindingsRecord(TODO リスト)│ ├── fixed.json # 修正済み記録の有界マップ│ ├── accepted.json # 「直さない」と判断した finding の記録│ └── regressed.jsonl # 再検出された修正の追記専用監査トレイル│├── .git/hooks/post-commit # `heal hook commit` を呼ぶ 1 行のシム│└── <agent skills> # 検出した各エージェントごとに 1 ツリー(`heal init --yes` 後) │ # .claude/skills/ (Claude Code) │ # .agents/skills/ (OpenAI Codex) ├── heal-cli/ # Code ファミリ ├── heal-code-patch/ ├── heal-code-review/ ├── heal-setup/ ├── heal-doc-pair-setup/ # Docs ファミリ ├── heal-doc-scaffold/ ├── heal-doc-review/ ├── heal-doc-patch/ ├── heal-test-reporter-setup/ # Test ファミリ ├── heal-test-review/ └── heal-test-patch/同梱の 11 スキルは検出した各エージェントへ同一バイト列で展開されます。[features.docs] や [features.test] を後から有効化したときに、すでにインストール済みのスキル本体がそのまま意味を持つようになる仕組みです(再展開は不要)。
config.toml、calibration.toml、findings/ の中身はすべて git で追跡されるので、同じコミット上のチームメイトは同じ Severity ラダーと解消キューを共有できます。
何がいつ書かれるか
Section titled “何がいつ書かれるか”| ファイル / ディレクトリ | 書き出し元 | タイミング |
|---|---|---|
.heal/config.toml | heal init | セットアップ時に一度。自由に編集可。 |
.heal/calibration.toml | heal init / heal calibrate | セットアップ時、その後は明示的な再 calibrate 時。 |
.heal/findings/latest.json | heal status | 新規 heal status(キャッシュミス経路)ごと。 |
.heal/findings/fixed.json | heal mark fix(/heal-code-patch から呼出) | /heal-code-patch のコミット着地ごと。 |
.heal/findings/accepted.json | heal mark accept(/heal-code-review から呼出) | チームが「設計上のもので直さない」と判断した項目を記録時。 |
.heal/findings/regressed.jsonl | heal status(整合パス) | 修正済み Finding が再検出されたとき。 |
.heal/doc_pairs.json | /heal-doc-pair-setup スキル([features.docs] 有効時) | ユーザがスキルを実行したとき。HEAL は読み取り専用。 |
<agent>/skills/heal-*/ | heal init(検出した各エージェント)/ heal skills install(Claude のみ) | エージェントごとに一度。heal init --force --yes でリフレッシュ。 |
イベントログも、月次ローテーションも、.heal/snapshots/ / .heal/logs/ / .heal/reports/ も存在しません。heal は現在の状態と regressed.jsonl の小さな監査トレイルだけを保持します。
.heal/docs/ だけは例外で、/heal-doc-scaffold を実行すると [features.docs] scaffold_root(デフォルトは .heal/docs/)に生成済みドキュメントツリーが書き出されます。HEAL 自身はこのツリーを 読む だけで、書くのはスキル側だけです。
Findings キャッシュ(項目一覧の保管場所)
Section titled “Findings キャッシュ(項目一覧の保管場所)”.heal/findings/ には 4 つの成果物が並びます。latest.json と regressed.jsonl の writer は heal status だけ、fixed.json の writer は heal mark fix だけ、accepted.json の writer は heal mark accept だけです。
latest.json — 現在の TODO
Section titled “latest.json — 現在の TODO”{ "version": 4, "id": "9f8e7d6c5b4a3210", // (head_sha, config_hash, worktree_clean) の FNV-1a hex "head_sha": "a0a6d1a…", "worktree_clean": true, "config_hash": "9f8e7d6c5b4a3210", // config + calibration の FNV-1a "severity_counts": { "critical": 2, "high": 5, "medium": 12, "ok": 84 }, "findings": [ /* Vec<Finding> */ ]}heal status は (head_sha, config_hash, worktree_clean=true) がキャッシュレコードと一致するときショートサーキットします — 同じコミット上での再実行は無料です。id はそのタプルの決定論的な FNV-1a ダイジェストなので、同じコミット + 設定 + ワーキングツリー状態は常にバイト同等の内容を生みます。だからこのファイルは git に tracked のまま、各チームメイトの git status を汚さずに済みます。
fixed.json — 有界の修正記録
Section titled “fixed.json — 有界の修正記録”BTreeMap<finding_id, FixedFinding> を 1 つの JSON オブジェクトとしてシリアライズしたもの。各エントリは決定論的な finding_id をキーにします。
{ "ccn:src/payments/engine.ts:processOrder:9f8e…": { "commit_sha": "a1b2c3", "fixed_at": "2026-04-30T05:14:22Z" }}有界です — 追記専用ではありません。新規 heal status で過去に fixed だった finding_id が再出現すると、heal は fixed.json から取り除いて regressed.jsonl に 1 行追記し、レンダラーが警告を出します。
regressed.jsonl — 監査トレイル
Section titled “regressed.jsonl — 監査トレイル”.heal/ 配下で唯一の追記専用ファイルです。再検出イベントごとに JSON を 1 行追加し、「修正したはずが再検出された」という警告を表示するためだけに使います。
accepted.json — 「直さない」レーン
Section titled “accepted.json — 「直さない」レーン”BTreeMap<finding_id, AcceptedFinding> を 1 つの JSON オブジェクトとしてシリアライズしたもの。heal mark accept が writer で、/heal-code-review スキルが「この項目は設計上避けられないので解消対象から外す」という判断を記録するときに呼びます。
{ "ccn:src/payments/engine.ts:processOrder:9f8e…": { "reason": "設計上分岐が多い(税金エンジン)", "file": "src/payments/engine.ts", "metric": "ccn", "severity": "critical", "hotspot": true, "metric_value": 31.0, "summary": "CCN=31 processOrder (TypeScript)", "accepted_at": "2026-04-30T05:14:22Z", "accepted_by": "Alice <alice@example.com>" }}fixed.json とは異なり、accepted エントリは finding が再出現しても消費されません。解消キューでの存在を無期限に抑制し、heal status は Accepted: N findings ヘッダ行と、--all 指定時の 📌 Accepted セクションでそれらを別途表示します。
Finding.accepted: bool はレンダリング時に accepted.json を finding リストに畳み込むことで装飾されます — latest.json 自体は raw observer truth を保ち、accepted: true を持ちません。これにより accept のオン / オフを切り替えても再スキャンは不要です。
エントリの削除: ファイルを手編集して該当行を消すか、スキルフローから heal mark accept --remove を呼びます。次の heal status で元の finding が再び表面化します。
このキャッシュは jq で直接覗けます。
jq '.severity_counts' .heal/findings/latest.jsonjq 'keys | length' .heal/findings/fixed.jsonjq 'keys | length' .heal/findings/accepted.jsontail .heal/findings/regressed.jsonlCalibration(Severity 基準の調整)
Section titled “Calibration(Severity 基準の調整)”calibration.toml は Severity を扱う各メトリクスのコードベース相対パーセンタイル区切りを保持します。heal init が初回スキャンから計算し、heal calibrate --force がオンデマンドで更新します。config.toml の floor_critical / floor_ok は calibrate されたパーセンタイルに勝ちます。再 calibrate は 絶対に自動では行いません — CLI › heal calibrate を参照。
Calibration(Severity 基準の調整)と policy: 2 つのレイヤ
Section titled “Calibration(Severity 基準の調整)と policy: 2 つのレイヤ”heal はコード健全性の 測定 と、それに対して何を行うかの 意図 を分離しています。
- Calibration レイヤ(
.heal/calibration.toml+ metric ごとの[metrics.<m>]override)は「この Finding は赤か?」を判定。3 段の分類器 —floor_critical(逃げ道)+floor_ok(卒業ゲート、proxy メトリクスのみ)+ パーセンタイル区切り — が Severity を生成します。このレイヤは測定の問い: 値が文献閾値とプロジェクト分布に対してどこに位置するか、に答えます。 - Policy レイヤ(
config.tomlの[policy.drain])は「その Finding はアクション対象か?」を判定。(Severity, hotspot)の組が 3 つの drain tier (T0 /must、T1 /should、Advisory) のいずれかにマップされます。このレイヤは意図の問い: チームが何を drain するとコミットするか、に答えます。
両レイヤは直交しています — 再 calibrate は Severity 境界を動かしますが policy には触れません。逆に policy を厳しく/緩くしても観測は再実行されません。チームは通常 calibration を文献デフォルト近くに保ち、自分たちの帯域に合わせて [policy.drain] を調整します。
解消キュー モデル
Section titled “解消キュー モデル”heal status は非 Ok の Finding を [policy.drain] 駆動で 3 つのバケットに分けます。
| Tier | デフォルト spec | レンダラー挙動 | Skill 挙動 |
|---|---|---|---|
| T0 / 解消キュー | must = ["critical:hotspot"] | 常に表示、Severity 🔥 desc 順。 | /heal-code-patch が 1 件ずつ解消。 |
| T1 / 余裕があれば解消 | should = ["critical", "high:hotspot"] | デフォルト表示、別セクション。 | レビュー対象、自動解消 しない。 |
| Advisory | それ以外の非 Ok | --all 時のみ表示。 | 自動解消 なし、余裕のあるときに review。 |
Severity::Ok の Finding は 解消対象外です。レンダラーは Ok 🔥 pre-section(上位 10% hotspot だがメトリクスフロア未満)と隠し合計カウントで表示します。
Override の可視化: [metrics.<m>] floor_ok / floor_critical が文献デフォルトと異なる場合、heal status はヘッダ行に override: ccn floor_ok=15 [override from 11] のような注釈を出力します。CI ログや PR diff で policy 変更が監査可能になります。
[policy.drain] の DSL は <severity>(hotspot 不問)または <severity>:hotspot(hotspot=true 必須)。Severity トークンは小文字: critical / high / medium / ok。詳細は設定 › Drain ポリシーを参照。