TL;DR
- 「モデルが賢くなれば Skill や AGENTS.md は削れる」という前提を、このブログのリポジトリの**全履歴(2025-08-11 から 2026-09-08、commit 1,064 本)**で検証しました。支持されませんでした(
社内データ)。 - Harness(
.agents/spec/scripts/tests/.claude/)の月次 net 行数は、変更があった 9 か月すべてでプラスです。マイナスの月は 1 度もありません(残る月は harness の変更自体が 0 件)。 - 同期間に追加されたファイルは 417 本、削除は 10 本。しかも 10 本のうち 5 本は再追加(4 本)と移設(1 本)で、実質的な削除は 5 本でした。そのうち 1 本は
tests/generate_gemini.test.mjs——テスト側を消して、テスト対象のスクリプトは今も残っています。 - 直近 14 日は追加 9,084 行 / 削除 729 行、ファイル削除 0 件。この 14 日はモデル移行を実際に行った期間(Coding Agentのモデル更新と回帰テスト)を含みます。賢くなっても削られていません。
- なので主張を書き換えました。負債の正体は「足しすぎ」ではなく **「削る根拠を出す仕組みが無いこと」**です。削除の根拠はモデル世代ではなく、参照エッジ / 二面検証の有無 / 検出実績という数えられる指標に置きます。
- 参照エッジ 0 の Skill は 6 本・3,115 行、二面検証の規約に載っていないスクリプトは 23 本中 14 本でした(
社内データ)。数えるスクリプトを本文に置いています。終了コードは 設定ミス=2 / 候補あり=1 / 候補なし=0 に分けています。 - ただし参照エッジ 0 は削除の十分条件ではありません。名前で呼ばれない仕組み(description からの自動選択)がある以上、この数字は候補であって判定ではありません。
はじめに:前作の次の問い
前回の記事「効いていないガードを見つける7つの型」で、このリポジトリに積んだガードのうち 7 種類が存在するのに何も守っていなかったことを書きました。書きながらずっと引っかかっていたのが、その次の問いです。
効いていないなら、消せばいいのでは。
そしてもうひとつ、界隈でよく見る言い方があります。「モデルが賢くなったのだから、細かい指示や補助ツールは要らなくなる。Harness は削れる」。実際、この 1 年で Skill や AGENTS.md を積み上げてきた身としては、そろそろ棚卸しの時期だという感覚はありました。
だから先に検証しました。この記事は「削るべきだ」という主張から始まりません。このリポジトリで実際に削られてきたのかを測るところから始めます。結果は予想と逆でした。
なお、この記事は提言ではありません。1 つのリポジトリで何が起きていたかの記録です。組織がどれだけ Harness に投資すべきか、どんな体制を組むべきかには踏み込みません。数字はすべて再現コマンド付きで書きます。
1. 「賢くなるほど削る」は起きているか
まず用語です。この記事で Harness と呼ぶのは、AI エージェントに仕事をさせるためにリポジトリに置いた足場一式です。具体的には次の 5 ディレクトリを指します。
| 層 | パス | 何が入っているか | 誰が読むか |
|---|---|---|---|
| 指示文層 | .agents/ .claude/commands/ | Skill / Agent 定義 / スラッシュコマンド | モデルだけ |
| 契約層 | spec/ | 守るべき規約・判定基準 | モデルと人間 |
| 実行層 | scripts/ tests/ | 検査スクリプトとそのテスト | 機械 |
この 3 層の増減を、月ごとに集計します。
git log --numstat --date=format:%Y-%m --format="M %ad" -- .agents spec scripts tests .claude \
| awk '/^M /{m=$2; next}
NF==3 && $1 ~ /^[0-9]+$/ {a[m]+=$1; d[m]+=$2}
END {for (k in a) printf "%s add=%d del=%d net=%d\n", k, a[k], d[k], a[k]-d[k]}' \
| sort
実行結果です(基準コミット 156f64f、2026-09-08 時点、社内データ)。
| 月 | 追加 | 削除 | net |
|---|---|---|---|
| 2025-08 | 224 | 47 | +177 |
| 2025-09 | 504 | 240 | +264 |
| 2026-02 | 29,979 | 1,655 | +28,324 |
| 2026-03 | 10,208 | 2,037 | +8,171 |
| 2026-04 | 5,221 | 154 | +5,067 |
| 2026-05 | 3,593 | 135 | +3,458 |
| 2026-06 | 1,358 | 204 | +1,154 |
| 2026-07 | 472 | 5 | +467 |
| 2026-09 | 9,084 | 729 | +8,355 |
**変更があった 9 か月すべてがプラスです。**マイナスの月がありません(表に出てこない月は harness の変更が 0 件でした)。削除行が追加行に占める割合は通算 8.6%(5,206 / 60,643)で、その大半は同一ファイル内の書き換えに伴う削除です。
ファイル単位で見ると、もっとはっきりします。
# 追加されたファイル数
git log --diff-filter=A --name-only --format="" -- .agents spec scripts tests .claude \
| sort -u | grep -c .
# 削除されたファイル数
git log --diff-filter=D --name-only --format="" -- .agents spec scripts tests .claude \
| sort -u | grep -c .
出力は 417 と 10 でした。削除は追加の 2.4% です。
しかもこの 10 本の中身を確かめると、さらに減ります。
git log --diff-filter=D --name-only --format="" -- .agents spec scripts tests .claude \
| sort -u \
| while read -r f; do
if [ -e "$f" ]; then echo "現存 $f"; else echo "消滅 $f"; fi
done
.agents/agents_list.txt など 4 本は削除後に再追加されて現在も存在し、scripts/scaffold_post.py は .agents/skills/scaffold-article/scripts/ へ移設されただけでした。約 13 か月の全履歴で本当に消えたのは 5 本です。
その 5 本の内訳がこの記事で一番の発見でした。
| 消えたファイル | 消えた日 | 中身 |
|---|---|---|
tests/generate_gemini.test.mjs | 2026-02-07 | テスト |
scripts/_deprecated_post_notion.mjs | 2026-02-07 | 下の残骸 |
scripts/post_notion.mjs | 2026-02-11 | 用途消滅(Notion 経由の投稿をやめた) |
scripts/install-hooks.sh | 2026-05-07 | 重複した hook 導入手順の統合 |
.agents/steering/seo-policy.md | 2026-06-06 | 重複ファイルの整理 |
1 行目です。**削除されたのはテストのほうで、テスト対象の scripts/generate_gemini.mjs(146 行)は今も残っています。**全履歴で 5 本しか削らなかったうち 1 本が、検証層を削って実行層を残す方向の削除でした。削除の方向を誰も判定していないことが、この 1 行に出ています。
そして直近 14 日です。
git log --since=2026-08-25 --diff-filter=D --name-only --format="" \
-- .agents spec scripts tests .claude | sort -u | grep -c .
0 です。この 14 日間に Harness へ 9,084 行が入り、ファイルは 1 本も消えていません。付け加えると、この期間には実際にモデル移行の作業が入っています(Coding Agentのモデル更新と回帰テスト)。モデルを新しくした期間に、Harness は repo 史上 2 番目の増加幅を記録しました。
だから主張はこう書き換えます。
「モデルが賢くなるほど Harness を削る」は、このリポジトリでは一度も起きていない。起きていないのは怠慢ではなく、削ってよいと言える根拠を出す仕組みが無いから。
なお、「月次 net がプラスのまま」という形は Harness だけの話ではありませんでした。同じ集計を repo 全体のディレクトリに広げると、判定を確定できた 15 対象のうち 12 が一度も純減した月を持っていません。この単調さを KPI の側から見ると、生成量を成果指標に置いた瞬間に悪化を表現できない数字になります。判定を保留する条件(観測月が少ない対象)まで含めて書き直したスクリプトは tokenmaxxingを避けるAI開発KPI にあります。
2. 増えているのはどの層か
負債があるとして、どこに溜まっているのかを見ます。層ごとの現在の規模です。
find .agents .claude/commands -name "*.md" -exec cat {} + | grep -c "" # 指示文層
find spec -name "*.md" -exec cat {} + | grep -c "" # 契約層
find scripts tests -type f -not -path "*__pycache__*" -exec cat {} + | grep -c "" # 実行層
基準コミットは表全体を通して 156f64f です(社内データ)。実行層のコマンドで __pycache__ を除いているのは、Python のテストを一度でも回した作業ツリーだと .pyc を -type f が拾い、行数が 2 割ほど増えるためです(実測 55 / 14,225 → 80 / 17,102)。
| 層 | ファイル数 | 行数 | 構成比 |
|---|---|---|---|
指示文層(.agents/ + .claude/commands/) | 273 | 30,534 | 61.8% |
契約層(spec/) | 18 | 4,613 | 9.3% |
実行層(scripts/ + tests/) | 55 | 14,225 | 28.8% |
| 合計 | 346 | 49,372 | 100% |
参考までに、この記事を書く直前(基準コミット 156f64f)の記事本文 217 本の総行数は 67,670 行でした。Harness は成果物の 73% の規模に育っています。
find content/posts -name "index.md" | grep -c ""
find content/posts -name "index.md" -exec cat {} + | grep -c ""
公開後にこれを実行すると、この記事自身が数に入るので 218 本になります。
つまり量として一番大きいのは指示文層です。ここは確かに「モデルが賢くなれば減らせる」候補に見えます。手順を細かく書かなくても察してくれるなら、書かなくていい。
ところが失敗の出方はそうなっていません。このリポジトリでは、記事制作で観測した失敗を spec/article_failure_ledger.md という台帳に 1 件 1 行で記録しています。2026-09-08 時点で 10 件です。
grep -cE "^\| L-[0-9]+ \|" spec/article_failure_ledger.md
grep -E "^\| L-[0-9]+ \|" spec/article_failure_ledger.md \
| awk -F'|' '{gsub(/ /,"",$4); print $4}' | sort | uniq -c
origin 列の内訳は harness が 8 件 / D-6 が 1 件 / D-12 が 1 件でした(社内データ)。10 件のうち 8 件は、Harness そのものの欠陥です。「モデルが変な記事を書いたのでガードが止めた」という行は 1 件もありません。
ここには注意が要ります。この台帳はすり抜けた失敗しか記録していません。ガードが止めた回数は記録していないので、「Harness は捕まえるより多くの欠陥を生む」とは言えません。言えるのは、観測された失敗の 8 割が Harness 自身に由来していたことだけです。選択バイアスのあるサンプルなので、そこまでで止めます。
それでも十分に効きます。**指示文を足せば直る失敗は、その 10 件の中に 1 件もありません。**壊れていたのは正規表現であり、終了コードの伝播であり、契約の書き方でした。指示文層は量として最大ですが、負債として痛んでいる場所とは一致していません。
3. なぜ削られないのか
このリポジトリの PR を遡って、足した PR と削った PR で何が違ったのかを見ました。行き着いたのは単純な非対称性です。
足すときは根拠が要らず、削るときは根拠が要る。
Harness を足す PR は、どれも同じ形をしていました。事故が起きて、そのケースを検出する分岐か契約を書き足す。既存の動作は変わらないので pnpm test は全部通る。レビューでも「これで再発しない」で通ります。実際、spec/ にある 18 ファイルのうち 10 本が直近 7 日(2026-09-02 以降)に新設されています。7 か月かけて 8 本だったものが、7 日で 10 本増えました。
git log --diff-filter=A --name-only --date=short --format="COMMIT %ad" -- spec \
| awk '/^COMMIT/{d=$2; next} NF{print d, $0}' | sort
削るほうは形が違いました。実質削除 5 本のうち、scripts/install-hooks.sh と .agents/steering/seo-policy.md の 2 本は重複の統合、scripts/post_notion.mjs とその残骸 1 本はその機能自体をやめたことによる削除です。**「まだ使う機能だが、この Harness は要らない」と判断して消した PR は 1 本もありませんでした。**そう判断するには何が壊れるかと誰が使っているかを言う必要があり、このリポジトリにはそれを示すコマンドが 1 つも無かったからです。チェスタトンの柵の話そのままで、柵を建てるのは自由ですが、抜くには「なぜ建ったか」を先に説明させられます。
変更があった月がすべてプラスという結果は、この非対称性の帰結として読めます。少なくともこのリポジトリでは、削らなかったのは判断の結果ではなく、判断材料が無かったからでした。
だとすると、このリポジトリで打つ手は決まりました。**削る側の根拠を、足す側と同じくらい安く出せるようにする。**具体的には、「これは削ってよい」を機械が数えられる形にします。以下の 2 節がその中身です。
4. 削る基準①:参照エッジ 0
一番安く数えられるのは、その Harness の名前が、他のどこからも呼ばれていないことです。
このリポジトリの Skill は .agents/skills/<name>/ に置かれ、Agent 定義・ワークフロー・スラッシュコマンド・契約から名前で参照されます。逆に言えば、どこからも名前が出てこない Skill は、少なくとも明示的な呼び出し経路を持っていません。
それを数えるスクリプトです。読者の任意のリポジトリでそのまま動きます。
#!/usr/bin/env bash
# harness-dead-weight.sh
# 検査対象ディレクトリのサブディレクトリ名が、参照元のどこからも
# 出現しないものを列挙する。
#
# 終了コード: 0=候補なし / 1=候補あり / 2=設定ミス・検査不能
set -euo pipefail
usage() {
echo "usage: $0 <target-dir> <search-path> [<search-path>...]" >&2
}
if [ "$#" -lt 2 ]; then
usage
exit 2
fi
TARGET_DIR="${1%/}" # 末尾スラッシュを落とす(後段の自己参照除外が外れるため)
shift
if [ ! -d "$TARGET_DIR" ]; then
echo "設定ミス: 検査対象ディレクトリが存在しません: $TARGET_DIR" >&2
exit 2
fi
SEARCH_DIRS=()
for d in "$@"; do
if [ -e "$d" ]; then
SEARCH_DIRS+=("$d")
else
echo "警告: 参照元が存在しないので除外します: $d" >&2
fi
done
if [ "${#SEARCH_DIRS[@]}" -eq 0 ]; then
echo "設定ミス: 実在する参照元が 1 つもありません。検査していません(合格ではありません)。" >&2
exit 2
fi
WORK_DIR="$(mktemp -d)"
trap 'rm -rf "$WORK_DIR"' EXIT
find "$TARGET_DIR" -mindepth 1 -maxdepth 1 -type d -print0 \
| xargs -0 -n1 basename \
| LC_ALL=C sort > "$WORK_DIR/targets.txt"
TARGET_COUNT="$(grep -c . "$WORK_DIR/targets.txt" || true)"
# 「検査対象 0 件」を「合格」と同じ出力・同じ終了コードにしない
if [ "$TARGET_COUNT" -eq 0 ]; then
echo "検査対象が 0 件でした: $TARGET_DIR にサブディレクトリがありません。" >&2
echo "これは合格ではありません。対象の指定を見直してください。" >&2
exit 2
fi
: > "$WORK_DIR/zero.txt"
while IFS= read -r name; do
[ -n "$name" ] || continue
refs="$(grep -rlF --exclude-dir=node_modules -- "$name" "${SEARCH_DIRS[@]}" 2>/dev/null || true)"
count="$(printf '%s\n' "$refs" | grep -vF "$TARGET_DIR/$name/" | grep -c . || true)"
if [ "$count" -eq 0 ]; then
lines="$(find "$TARGET_DIR/$name" -type f -exec cat {} + 2>/dev/null | grep -c "" || true)"
printf '%s\t%s\n' "$name" "$lines" >> "$WORK_DIR/zero.txt"
fi
done < "$WORK_DIR/targets.txt"
ZERO_COUNT="$(grep -c . "$WORK_DIR/zero.txt" || true)"
ZERO_LINES="$(awk -F'\t' '{s+=$2} END {print s+0}' "$WORK_DIR/zero.txt")"
echo "検査対象: ${TARGET_COUNT} 件 (${TARGET_DIR})"
echo "参照元: ${SEARCH_DIRS[*]}"
echo "参照エッジ 0: ${ZERO_COUNT} 件 / ${ZERO_LINES} 行"
if [ "$ZERO_COUNT" -eq 0 ]; then
echo "削除候補は見つかりませんでした。"
exit 0
fi
echo "---"
printf '%-40s %s\n' "候補" "行数"
while IFS=$'\t' read -r name lines; do
printf '%-40s %s\n' "$name" "$lines"
done < "$WORK_DIR/zero.txt"
echo "---"
echo "注意: これは削除候補であって削除判定ではありません。"
exit 1
意図的にそうしてある点を先に書きます。
| 設計 | 理由 |
|---|---|
| 終了コードを 2 / 1 / 0 に分けた | 「設定を間違えた」と「候補が無い」が同じ 0 だと、パスを打ち間違えたときに合格として通る |
| 検査対象 0 件を exit 2 にした | 対象が 1 件も無いのを「合格」と同じ出力で報告しない。前作 D5 で踏んだ型 |
すべての grep に || true を付けた | set -e 下でマッチ 0 件の grep は終了コード 1 を返し、何も表示せずスクリプトが死ぬ |
mktemp -d + trap | 固定の /tmp パスは並列実行と権限で壊れる |
引数の末尾スラッシュを ${1%/} で落とした | 落とさないと自己参照の除外が外れ、候補があるのに exit 0 を返す(下記) |
| 出力の最後に「判定ではない」と書いた | 後述の偽陽性があるため |
最後の 1 行は後から足したものです。このスクリプトを独立レビューに掛けたところ、.agents/skills/ のように末尾スラッシュ付きで渡した場合(シェルのタブ補完で自然に付きます)、自己参照の除外パターンが .agents/skills//<name>/ になって一致しなくなり、Skill 自身のファイルに出てくる自分の名前を「外部からの参照」として数えていました。出力は 削除候補は見つかりませんでした の exit 0 です。
# 同じ fixture・同じ参照元。target の末尾スラッシュの有無だけが違う
$ ./harness-dead-weight.sh /tmp/fx/skills /tmp/fx → 参照エッジ 0: 1 件 / exit=1
$ ./harness-dead-weight.sh /tmp/fx/skills/ /tmp/fx → 削除候補は見つかりませんでした / exit=0
これは spec/article_retrospective_loop.md が **E3(空振り合格)**と呼んでいる型そのものです。**削る基準を提示する記事に載せたスクリプトが、記事自身の説明している型で壊れていました。**掲載版は上の 1 行で修正済みで、末尾スラッシュの有無どちらでも exit 1 になることを確認しています。
このリポジトリでの実行結果です。
./harness-dead-weight.sh .agents/skills \
.agents/agents .agents/workflows .agents/rules .claude/commands spec scripts tests AGENTS.md
検査対象: 90 件 (.agents/skills)
参照元: .agents/agents .agents/workflows .agents/rules .claude/commands spec scripts tests AGENTS.md
参照エッジ 0: 6 件 / 3115 行
---
候補 行数
cleanup-stale-worktrees 133
docker-expert 439
i18n-localization 395
pr-reviewer 302
prisma-expert 393
typescript-expert 1453
---
注意: これは削除候補であって削除判定ではありません。
exit code は 1 でした。90 本の Skill のうち 6 本、3,115 行が、どこからも名前で呼ばれていません。指示文層 30,534 行の 10.2% です。
同じ考え方をスクリプトにも当てます。
ls scripts/*.py scripts/*.mjs \
| while read -r f; do
s="$(basename "$f")"; s="${s%.*}"
c="$(grep -rl --exclude-dir=node_modules -- "$s" \
package.json .agents .claude spec docs 2>/dev/null | grep -c . || true)"
[ "$c" -eq 0 ] && echo "参照0 $f"
done
scripts/gen_media_batch5.mjs(91 行)、scripts/move_images_batch5.mjs(42 行)、scripts/render_design_system_media.mjs(1,194 行)の 3 本が出ました。いずれも 2026-03 の一括処理スクリプトで、一度きりの作業のために書かれ、そのまま残っています。合計 1,327 行です。
この数字を削除判定にしてはいけない理由
ここが重要です。参照エッジ 0 は「呼ばれていない」の証拠ではありません。
Claude の Agent Skills は、SKILL.md の frontmatter にある description が常時ロードされ、モデルがそれを読んで自分で選びます(Agent Skills 公式ドキュメント)。**名前で呼ばれない起動経路が設計として存在します。**したがって docker-expert が参照エッジ 0 であることは、それが使われていないことを何ひとつ示しません。
このリポジトリでの扱いは次のようにしています。
- 参照エッジ 0 は候補リストとして扱う。ここから即座に消さない
- 消す前に、実行ログか会話履歴でその Skill が選択された記録を探す
- 記録が見つからないことは「使われていない」の証拠にならないので、先に
descriptionを狭めて発火条件を明示化し、それでも呼ばれない状態を作ってから消す
scripts/ 側は判断が違います。スクリプトは名前で呼ばれない限り実行されないので、参照エッジ 0 はそのまま到達不能を意味します。同じ指標でも、層によって強さが変わります。
5. 削る基準②:二面検証を持たない検査
もう 1 つの基準は、前作の続きです。
このリポジトリには spec/guard_self_check.md という契約があり、その R-1 が 二面検証を要求しています。正常な入力で通ることと、わざと壊した入力で落ちることの両方を確認する、という規約です。ミューテーションテストで言えば、変異体が生き残らないことを確かめる作業に当たります(Stryker の mutant states)。
R-2 は、その二面検証を持つべきガードを表で列挙しています。そこに載っているスクリプトを数えます。
# scripts/ 直下の検査対象母数
ls scripts/*.py scripts/*.mjs | grep -c .
# guard_self_check.md が名前を挙げているスクリプト
grep -oE 'scripts/[a-z_./-]+\.(py|mjs)' spec/guard_self_check.md \
| sed 's|.*/||' | sort -u | grep -c .
scripts/ 直下は 23 本、二面検証の契約が名前を挙げているのは 9 本でした(社内データ)。14 本が二面検証の対象外です。
内訳を見ると、この 14 本は 2 種類に分かれます。
| 種類 | 例 | 削除の扱い |
|---|---|---|
| 検査しないスクリプト(生成・変換) | generate_article_status.mjs / render_design_system_media.mjs | 二面検証の対象外で正常。削除基準に該当しない |
| 検査するのに二面検証が無いスクリプト | evaluate_articles.py / check_preflight.py | 前作の型そのもの。削るか、二面検証を足すかの二択 |
契約層も同じ目で見られます。spec/ 直下の *.md は 16 本(第 3 節の 18 は spec/worklog/ の 2 本を含む値です)で、ファイル名が tests/ から参照されているのは 7 本です。
for f in spec/*.md; do
b="$(basename "$f")"
c="$(grep -rl "$b" tests 2>/dev/null | grep -c . || true)"
echo "$c $b"
done | sort -n
これは弱い指標です。契約の名前がテストに出てこなくても、その契約を実装したスクリプトにはテストがあるケースがあります(article_claim_ledger.md と validate_claim_ledger.py がそれです)。偽陰性が出る指標なので、そのまま削除候補にはしません。使うのは逆向きで、「参照 0 かつ実装スクリプトも無い契約」は文章だけの契約です。
削ってはいけないもの
ここまでで削除候補を出しましたが、削除を進める前に確かめることがあります。削る余地がある層と、そもそも足りていない層は別だということです。
このリポジトリの失敗台帳には escape_mode という列があり、「なぜ Harness をすり抜けたか」を E1〜E8 で分類しています。そして spec/article_retrospective_loop.md に、各 mode に対応する予防規約の表があります。
grep -cE "^\| E[0-9] \|" spec/article_retrospective_loop.md
grep -E "^\| E[0-9] \|" spec/article_retrospective_loop.md | grep -c "無し"
出力は 8 と 2 でした。予防規約の欄を 1 つずつ読むと、こうなっています。
grep -E "^\| E[0-9] \|" spec/article_retrospective_loop.md | awk -F'|' '{print $2, "->", $6}'
| mode | 予防規約 | 種別 |
|---|---|---|
| E1 発火不能 | guard_self_check.md R-1 | テストあり |
| E2 判定の非伝播 | 同 R-7 | テストあり |
| E3 空振り合格 | 同 R-6 規約5 | テストあり |
| E4 部分測定の全体化 | 無し | — |
| E5 充足不能な契約 | article_risk_gate.md「記録の正本」節 | 散文のみ・機械検証なし |
| E6 転記ドリフト | check_risk_gate_drift.py | 登録済みの転記箇所のみ |
| E7 失効根拠 | article_working_artifacts.md | review-draft.md のみ |
| E8 未実行 | 無し(手順書の警告のみ) | — |
壊した入力のテストまである予防規約を持つのは E1 / E2 / E3 の 3 つだけです。8 つのうち 5 つは、無いか、散文だけか、対象が限定されています。
ついでに 1 つ見つかりました。この表を持つ spec/article_retrospective_loop.md 自身が、散文では「R-1〜R-7 は E1 / E2 / E3 / E7 しか覆っていない」と書いています。しかし同じファイルの表では、E7 の予防規約は article_working_artifacts.md であって guard_self_check.md ではありません。**契約文書の要約が、同じファイル内の表と食い違っています。**上の数え方は表のほうを採りました。
これは削除の話とは逆の結論になります。**このリポジトリの Harness には、削れる部分とまだ足りていない部分が同時にあります。**だから量を減らすことを目的に置いていません。置いているのは、行数を「検出実績のあるもの」へ寄せることです。
6. このリポジトリで決めた運用
以上を踏まえて、削除の扱いを次のように決めました。提言ではなく、このリポジトリで採用した手順です。
| # | 手順 | 根拠 |
|---|---|---|
| 1 | モデル世代を削除の根拠にしない | 全履歴に、モデル更新と Harness 減少の相関が 1 件も無い |
| 2 | 削除候補は harness-dead-weight.sh の参照エッジ 0 から出す | 数えられるので、足す側と同じ安さで根拠が出る |
| 3 | 指示文層の候補は即削除せず、先に description を狭める | 名前で呼ばれない起動経路があるため、参照エッジ 0 が偽陽性を出す |
| 4 | 実行層の候補(一括処理スクリプト)は削除してよい | 名前で呼ばれない限り到達しないので、参照エッジ 0 が到達不能と一致する |
| 5 | 削除は 1 PR = 1 削除にする | git revert 1 手で戻せることが唯一の rollback 手段になる |
| 6 | 検査するのに二面検証が無いスクリプトは、削るか二面検証を足すかを選ぶ。放置しない | 前作で 7 種類の空振りを観測している |
| 7 | 「テストを消して実装を残す」削除を禁止する | 実質 5 件の削除のうち 1 件がこれだった |
7 番は、この記事を書くまで規約になっていませんでした。tests/generate_gemini.test.mjs を消した時点では、誰も方向を判定していません。削除の非対称性は、削らないことだけでなく、間違った方向に削ることでも表面化します。
削除の順序も決めています。実行層 → 契約層 → 指示文層の順です。実行層は参照エッジ 0 が到達不能と一致するので判定が確実で、指示文層は最後まで判定が曖昧なままだからです。量が一番多い層を最後に回すのは非効率に見えますが、判定の確度が高い順に処理するほうが、間違った削除を戻す回数が減ります。
なお、ここで決めたのは削る側の手順だけです。逆側——「そろそろガードやスキルを足すべきだ」と気づく判定器——も同じように壊れます。同じリポジトリでその昇格判定を測ると、8 種のすり抜け型のうち 6 種で沈黙していました(AIの推論を自動化へ昇格させるループの穴)。足す判断と削る判断は、どちらも根拠を数える仕組みが無いところから崩れます。
7. この測定の限界
数字を出したので、どこまで言えるかも書きます。
**1 リポジトリ・約 13 か月(うち harness に変更があったのは 9 か月)の観測です。**個人運用のブログ生成リポジトリで、商用プロダクトではありません。チーム人数も 1 人です。「Harness は増え続ける」を一般法則として主張できるサンプルではありません。
**失敗台帳は選択バイアスがあります。**すり抜けた失敗しか記録していないので、「Harness が捕まえた回数」との比を出せません。台帳の 8 割が harness 由来であることは、Harness が有害であることを意味しません。
**参照エッジ 0 は削除の十分条件ではありません。**4 節で書いたとおりです。この記事のスクリプトを他のリポジトリで走らせて、出てきた候補をそのまま消すと壊れます。出力の最終行にその注意を書いてあるのは、そのためです。
**「モデルが賢くなるほど削れる」を否定できたわけでもありません。**測ったのは「このリポジトリで削られたか」であって、「削れたはずか」ではありません。削れたのに削らなかった可能性は残ります。ただ、削れたはずのものを削る仕組みが無い以上、その差は観測できません。観測できない主張を運用の根拠にしない、というのがここでの結論です。
Fowler の技術的負債の比喩で言えば、Harness の負債は「後で返す前提で借りた」ものではなく、返済窓口が開いていない種類の負債です(Technical Debt Quadrant の分類で言う inadvertent 側)。窓口を作るのが先で、いくら返すかはその後の話になります。
関連する記事として、コード側の負債は「AI開発の技術的負債」、コンテキストの肥大化は「AI開発のコンテキスト負債」、Skill の資産化そのものは「Agent Skill Hub」で扱っています。宣言した統制と実際に効いている統制のズレは「宣言された統制と実効統制」が近いです。
まとめ
- 「モデルが賢くなるほど Harness を削る」は、このリポジトリの全履歴(commit 1,064 本)で 1 度も観測されませんでした。変更があった 9 か月の net はすべてプラス、追加 417 ファイルに対し実質削除 5 ファイルです。
- 削られない理由は非対称性です。**足すのに根拠は要らず、削るのに根拠が要る。**そして削る側の根拠を出すコマンドが手元にありませんでした。
- 数えられる削除基準を 2 つ置きました。参照エッジ 0(Skill 6 本 3,115 行 / スクリプト 3 本 1,327 行)と、二面検証を持たない検査(23 本中 14 本が R-2 の表の外)です。
- ただし層によって指標の強さが違います。実行層では参照エッジ 0 が到達不能と一致しますが、指示文層では自動選択の経路があるため一致しません。
- 同時に、削ってはいけない側もはっきりしました。escape mode 8 つのうち予防規約が覆っているのは 4 つです。Harness は減らすものではなく、検出実績のあるほうへ寄せるものとして扱っています。
- 前作「効いていないガードを見つける7つの型」が「効いているか」を見る記事だとすれば、本記事は「効いていないものをどう手放すか」の記録です。両方を通して分かったのは、存在するかどうかを数えるのは簡単で、効いているかどうかを数えるのはいつも後回しになるということでした。
FAQ
Q1. モデルが賢くなったら Skill や AGENTS.md は本当に不要になりませんか
このリポジトリのデータでは判断できませんでした。測れたのは「削られたか」であって「削れたか」ではないからです。ただし観測できた事実として、モデル移行を行った直近 14 日に Harness は 9,084 行増え、ファイル削除は 0 件でした。少なくともこのリポジトリでは、モデル更新を削除の根拠にできる材料が集まっていません。
Q2. 記事のスクリプトが出した「参照エッジ 0」を、そのまま消してよいですか
消さないでください。Claude の Agent Skills は description からモデルが自分で選ぶ経路を持つため、名前で参照されていなくても呼ばれることがあります。このリポジトリでは、指示文層の候補は削除せず、先に description を狭めて発火条件を明示化する運用にしています。一方、scripts/ のように名前で呼ばれないと到達しない層では、参照エッジ 0 をそのまま到達不能と読んでいます。
Q3. なぜ終了コードを 2 / 1 / 0 に分けているのですか
「設定を間違えた」と「候補が無かった」を区別するためです。両方が 0 だと、パスを打ち間違えたまま合格として通ります。同じ理由で、検査対象が 1 件も見つからなかった場合も exit 2 にしています。このリポジトリでは、検査対象 0 件を合格として報告していた欠陥を過去に 3 系統で観測しています。
Q4. Harness を減らすことを目標値にしていますか
していません。行数を減らすことは目的にしていない、というのがこのリポジトリでの扱いです。escape mode 8 つのうち予防規約があるのは 4 つで、足りていない領域が同時に存在します。使っている基準は総量ではなく、参照エッジと二面検証の有無です。
Q5. 削除の PR はどう出していますか
1 PR = 1 削除にしています。理由は運用上の綺麗さではなく、git revert 1 手で戻せることが唯一の rollback 手段だからです。複数の削除や他の変更と混ぜた PR は revert できず、rollback の手順が書けなくなります。
References
- Martin Fowler — Technical Debt
- Martin Fowler — Technical Debt Quadrant
- Google SRE Book — Eliminating Toil
- Stryker Mutator — Mutant states and metrics
- Claude Docs — Agent Skills overview
- Anthropic Engineering — Effective context engineering for AI agents
- Wikipedia — G. K. Chesterton (Chesterton's Fence)
