Notes
ドキュメントの鮮度を自動チェックして陳腐化を防ぐ
プロジェクトのドキュメント(ADR、README、設計ドキュメント等)がコードの変更に追従できず、古い情報のまま放置される問題があった。手動で定期的に見直すのは続かない。
Before(手動):
気づいたときにドキュメントを見直す → 気づかないまま数ヶ月放置されるドキュメントが発生。新メンバーが古い設計ドキュメントを信じて実装してしまうリスク。
After(自動):
ドキュメントファイルの最終更新日と関連コードの変更日を突き合わせ、乖離が大きいものをリストアップする「doc freshness guard」を導入。
# 例: 90日以上更新されていないドキュメントを検出
find docs/ -name "*.md" -mtime +90 -print
# 関連コードが更新されているのにドキュメントが古いケースを検出
# ドキュメント内で参照しているファイルパスやシンボル名をgrepし、
# そのファイルの最終コミット日とドキュメントの最終コミット日を比較
ポイント:
- 「最終更新が古い」だけでなく「関連コードが変わったのにドキュメントが更新されていない」を検出するのが重要。安定しているドキュメントまでノイズにならないように
- スケジュールタスクやCI上で定期実行し、結果をissueやSlackに通知すると放置されにくい
- ドキュメントのフロントマターに
related_paths: [src/auth/]のようなメタデータを持たせると、関連コードとの紐付けが正確になる
自律開発エージェントにPR数ゲートを設けてスループットを制御する
スケジュールタスクで自律的にissueを拾って実装・PR作成するエージェントを動かしていたが、レビューが追いつかないまま次々とPRが作られて並行ブランチが増えすぎる問題があった。
仕組み:
- エージェント起動時にオープンPR数をチェックし、閾値(例: 3件)以上なら何もせず終了する
- レビュー・マージが進んでPR数が減ると、次回のスケジュール実行で新しいissueを拾い始める
# scheduled-task prompt
事前にGitHubのPull Requestを確認し3つ以上が存在している場合は、
以降の処理を行わず。ここで処理をやめる
(並行作業が発生しすぎないように)
エージェント側の実装:
open_prs=$(gh pr list --state open --json number --jq 'length')
if [ "$open_prs" -ge 3 ]; then
echo "Open PRs: $open_prs (threshold: 3). Skipping."
exit 0
fi
やってみてどうだったか:
- レビュー待ちPRが溜まらなくなり、1つずつ確実にマージできるペースになった
- 閾値は「1人でレビューできるPR数」に合わせるのがよさそう。個人プロジェクトなら2〜3件
- PR数だけでなく「最後のPRが何時間前に作られたか」も見ると、バースト的な生成をさらに抑制できそう
Cloudflare D1のテストでは readD1Migrations + applyD1Migrations でマイグレーションを適用する
@cloudflare/vitest-pool-workers でD1を使うテストを書くとき、テストヘルパーにCREATE TABLE文をハードコードしていた。マイグレーションファイルを追加するたびにテストヘルパーも手動で同期する必要があり、乖離してテストが壊れることがあった。
@cloudflare/vitest-pool-workers が提供する readD1Migrations と applyD1Migrations を使えば、本番と同じマイグレーションファイルをテストDBにも適用できる。
// vitest.config.ts(Node.jsコンテキスト)
import {
defineWorkersConfig,
readD1Migrations,
} from "@cloudflare/vitest-pool-workers/config";
export default defineWorkersConfig(async () => {
const migrationsPath = path.join(__dirname, "migrations");
const migrations = await readD1Migrations(migrationsPath);
return {
test: {
poolOptions: {
workers: {
miniflare: {
d1Databases: { DB: { migrationsPath } },
bindings: { TEST_MIGRATIONS: migrations },
},
},
},
},
};
});
// test/helpers.ts(Workerコンテキスト)
import { env } from "cloudflare:test";
import { applyD1Migrations } from "cloudflare:test";
export async function resetDatabase() {
await applyD1Migrations(env.DB, env.TEST_MIGRATIONS);
// DELETE文でデータだけクリア(テーブル作成は不要)
await env.DB.exec("DELETE FROM orders; DELETE FROM users;");
}
ハマりどころ:
readD1MigrationsはNode.jsコンテキスト(vitest.config.ts)で呼ぶ。Worker内ではfsが使えないapplyD1Migrationsはcloudflare:testからimportする。Workerコンテキスト専用wrangler.tomlのmigrations_dirを設定するだけでは自動適用されない。明示的にAPIを呼ぶ必要がある
中間データにメタデータを含めておくと後工程の外部ツール呼び出しが減る
RAW現像パイプラインで、recipe.json(現像パラメータ)→ XMP変換 → darktable-cli実行、という流れを組んでいた。XMP生成時にクロップ値を決めるためにEXIF情報が必要だったが、recipe.jsonにEXIF情報がなく、毎回exiftoolを実行し直していた。
recipe.json生成時にEXIF情報を一緒に埋め込むようにしたところ:
- XMP生成が自己完結する — recipe.jsonだけ読めばXMPを作れる。
exiftoolの再実行が不要 - バッチ処理が速くなる — 75枚の一括現像で、1枚ごとの
exiftool呼び出しがなくなった - デバッグしやすい — recipe.jsonを見るだけで「このRAWのセンサーサイズはいくつで、WBは何Kだったか」がわかる
{
"file": "IMG_0001.ARW",
"recipe": { "exposure": 0.5, "temperature": 6300 },
"exif": {
"image_width": 6048,
"image_height": 4024,
"white_balance": "Auto",
"iso": 800
}
}
パイプラインの中間データは「次の工程が必要とする情報」を全部持たせておくと、工程間の結合度が下がる。後から「あのメタデータも必要だった」と気づいて追加するより、最初から入れておく方が手戻りが少ない。
エージェントスキルの「知識」と「ロジック」を分離してreferencesに蓄積する
写真現像エージェントで「ペットポートレートは5500K推奨」のようなドメイン知識をスキルのプロンプトに直書きしていたが、知識が増えるたびにプロンプトが肥大化する問題があった。
仕組み:
- スキルのプロンプト(ロジック層)には「referencesを読んで過去の好みを参考にせよ」とだけ書く
- ドメイン知識は
references/ディレクトリにファイルとして蓄積する - フィードバック結果から自動で新しいリファレンスを追加・更新する
skills/
arw-improve/
SKILL.md # ロジック: 手順と判断基準
references/
wb-preferences.md # 知識: シーン別WB傾向
exposure-tips.md # 知識: 露出調整の学び
やってみてどうだったか:
- スキルのプロンプトが安定する。知識が増えてもロジック部分は変わらない
- リファレンスはフィードバックのたびに追記されるので、使うほど精度が上がる
- 知識が構造化されているのでエージェントが「似たシーンの過去実績」を引きやすい
- 逆に、リファレンスが増えすぎるとコンテキストを圧迫するので、定期的に要約・統合する仕組みも必要になりそう
スケジュールタスクで開発作業を自動駆動する
AIエージェントの実装作業をスケジュールタスクとして登録し、バックログから自動的にissueを拾って実装→PR作成まで走らせるパターン。人間はレビューとフィードバックに集中できる。
仕組み:
story-devスキルをスケジュールタスクとして登録。定期的に自動起動- 起動するとバックログからReady状態のissueを1件選び、worktreeを作って実装し、PRを作成
- PRのCI(テスト、スクリーンショット差分等)が落ちたら、ユーザーが結果を伝えて修正を指示
scheduled-task (story-dev)
→ Ready issue を選択
→ worktree 作成
→ 実装 + テスト
→ PR 作成
→ CI結果待ち → 必要に応じてfix
1日で7件のissue(ラベルCRUD、ブロッカー管理、タイムライン、フィールドエラー表示、オーナー管理等)をこのフローで処理できた。
やってみてどうだったか:
- issueに
## Plan/DoD/verifyを事前に整備しておくと、エージェントが迷わず実装に入れる。issue品質が開発速度に直結する - CI失敗時の修正は人間がエラー内容を伝えるだけでよく、修正自体はエージェントが行う。「テストとスクリーンショット差分が落ちています」のような一言で十分
- 複数issueを直列でこなすので、途中のissueで入れた変更が次のissueのベースになる。ブランチ戦略(前のPRからブランチを切る等)の考慮が必要
AIの提案精度をフィードバックループで育てる
AIに創作パラメータ(写真の現像設定、デザインのトーン等)を提案させるとき、一発で好みに合うことは少ない。提案→評価→学習のループを仕組みとして組み込むと、回を重ねるごとに精度が上がる。
仕組み:
- 提案スキル — 過去のリファレンスを全読みしてからパラメータを生成する。リファレンスがなければ一般的なベストプラクティスで提案
- 適用 — 提案されたパラメータをツール(darktable-cli等)で適用し、結果を出力
- フィードバックスキル — ユーザーが5段階評価 + 良かった点/改善点/好みの方向性をテキストで入力
- リファレンス蓄積 — フィードバックを「学び」として抽象化し、
references/ディレクトリにMarkdownで保存。シーンタイプ・条件等のタグ付き
/propose → params.json → /apply → output
↓
/feedback → references/ に蓄積
↓
次回の /propose で references/ を参照 ←─┘
ポイントは、フィードバックを生のスコアではなく抽象化された学びとして保存すること。「この写真にはexposure +0.5が良かった」ではなく「暗所の室内写真ではシャドウの持ち上げが効果的」のように、別のシーンにも転用できる形にする。
やってみてどうだったか:
- スキルの
references/をコンテキストとして読むだけなので、ベクトルDB等の追加インフラが不要。ファイルベースで十分機能する - フィードバックのフォーマットを揃えておくと、AIが傾向を読み取りやすい
- 数回のフィードバックで「彩度はカメラJPEG並みにほしい」「シャドウの持ち上げは効果的」等の好みが反映されるようになった
darktable-cliでRAW現像を自動化してみた
RAWファイルにAI提案のパラメータを自動適用したくて、CLIからRAW現像できるツールを検討した。選択肢はdarktable-cli、rawtherapee-cli、Rust(libraw-rs)の3つ。darktable-cliを採用した。
使ってみた感想:
- 良かった点 —
darktable-cli input.ARW output.jpg --xmp sidecar.xmpの一行で、露出・コントラスト・ハイライト/シャドウ・シャープネス・NR・カラーグレーディング等の全パラメータを適用してJPEG出力できる。GUIなしのヘッドレス動作なのでCI/スクリプトに組み込みやすい - 気になった点 — XMPのパラメータ形式がdarktable独自のバイナリエンコーディング(C構造体をhexエンコード)で、プログラムからXMPを生成するのが一手間。Lightroom互換のXMLベースではない
- 向いている場面 — AIや機械学習で現像パラメータを提案し、結果をプレビューするパイプライン。人間がGUIを操作せずにRAW→JPEG変換を回したいケース
代替案との比較:
- rawtherapee-cli — PP3プロファイル形式でテキストベースなので生成しやすいが、パラメータ体系が独自
- libraw-rs (Rust) — WB・露出・明るさの4パラメータ程度しかカバーできず、コントラストやトーンカーブは自前実装が必要。RAW現像の本領を発揮するには不足
- darktable-cli — パラメータ網羅性が圧倒的。XMP生成の手間はPythonスクリプトで吸収できる
# macOSでのパス例
/Applications/darktable.app/Contents/MacOS/darktable-cli \
input.ARW output.jpg \
--xmp sidecar.xmp \
--width 2048 --height 2048
refs: https://docs.darktable.org/usermanual/4.8/en/special-topics/program-invocation/darktable-cli/
PreToolUse hookでAIエージェントの望まないコマンドパターンを禁止する
Claude Codeのエージェントがバックグラウンドタスクの完了を sleep && grep でポーリングし始めた。許可プロンプトが出るたびにユーザーが判断する必要があり、作業の邪魔になる。
PreToolUse hookを設定すると、特定のコマンドパターンをエージェントが実行する前にブロックできる。
// .claude/settings.local.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -q 'sleep.*grep'; then echo 'BLOCK: sleep+grep polling is not allowed' >&2; exit 2; fi"
}
]
}
]
}
}
仕組み:
PreToolUseはツール実行前に発火するフックmatcherでツール名を指定(Bashでシェルコマンドを対象に)- exit code 2 でツール実行をブロックし、エラーメッセージがエージェントに返る
- エージェントは別のアプローチを試みるようになる
ポーリング以外にも、rm -rf や git push --force など危険なパターンの防止に使える。エージェントの行動を事後チェックでなく事前にゲートする仕組みとして便利。
refs: https://docs.anthropic.com/en/docs/claude-code/hooks
AIエージェントでプラクティスのギャップ分析→バックログ自動追加を行う
開発ツールやCI/CD周りのベストプラクティスを定期的に棚卸ししたいが、手動だと「何が足りないか」のリストアップだけで時間が溶ける。AIエージェントに既存構成の分析→Web上のプラクティスとの比較→不足分のIssue化までを一気通貫で任せてみた。
仕組み:
- エージェントにリポジトリの既存構成(スキル一覧、CI設定、フック設定等)を読ませる
- 「未適用のプラクティス」をWeb検索も交えてリストアップさせる
- 既存バックログとの重複チェックを行い、新規分だけIssue化する
# エージェントへの指示(概要)
1. ハーネスエンジニアリングにおいて、未適用のプラクティスを調べてリストアップ
2. 既存バックログと重複するものは除外
3. 残りをIssue化する
やってみてどうだったか:
- 24スキル・6スケジュールタスク・3レビューエージェントの構成を分析し、5件の未適用プラクティスを特定してくれた
- 既存バックログ(4件)との重複除外も正しく動いた
- 「エージェントが知っているベストプラクティス」の範囲に依存するので、ドメイン固有のプラクティスは拾えない。汎用的なプラクティス(セキュリティ監査、PR自動レビュー等)の漏れ検出に向いている
- 定期実行(週次・月次)にすると、ツールのアップデートで新しく使えるようになったプラクティスも拾える可能性がある
CSS columnsで手軽にMasonryレイアウトを実現する
カード一覧をMasonryレイアウト(Pinterest風の段違いグリッド)にしたいとき、CSS columns プロパティで簡単に実現できる。JavaScriptライブラリなしでレスポンシブ対応もできる。
.masonry-grid {
columns: 3 300px; /* 最大3列、1列の最小幅300px */
column-gap: 1rem;
}
.masonry-grid > * {
break-inside: avoid; /* カードが列をまたがないようにする */
margin-bottom: 1rem;
}
Tailwind CSSなら columns-3 と break-inside-avoid だけで済む。
<div class="columns-1 sm:columns-2 lg:columns-3 gap-4">
<div class="break-inside-avoid mb-4">カード1(高さ可変)</div>
<div class="break-inside-avoid mb-4">カード2</div>
<div class="break-inside-avoid mb-4">カード3</div>
</div>
注意点:
- CSS columnsは上から下に流れるので、並び順がグリッドと異なる(左→右ではなく上→下→次の列)
- カード高さが固定の場合はMasonryにする意味が薄い。コンテンツの高さがバラつくときに効果的
break-inside: avoidを忘れるとカードが列の境界で分断される
Cloudflare D1のテスト環境はCREATE TEMP TABLEを拒否する
D1のテスト環境(miniflare)でマイグレーションSQLを実行したら SQLITE_AUTH エラーになった。原因は CREATE TEMP TABLE 文。
D1はSQLiteベースだが、セキュリティポリシーで一部のSQLite機能を制限している。CREATE TEMP TABLE はその一つで、テスト環境でもプロダクション環境でも使えない。
マイグレーションで一時テーブルが必要な場合の代替手段:
- 通常テーブルを使って最後にDROPする —
CREATE TABLE tmp_xxx→ 処理 →DROP TABLE tmp_xxxで同じことができる - CTEやサブクエリで完結させる — 一時テーブルなしで書き換え可能なケースも多い
-- NG: D1では SQLITE_AUTH エラー
CREATE TEMP TABLE migration_data AS SELECT ...;
-- OK: 通常テーブルを使って最後にDROP
CREATE TABLE migration_data AS SELECT ...;
-- ... 処理 ...
DROP TABLE migration_data;
lefthookで既存マイグレーションファイルの変更をブロックする
AIエージェントにDB機能の実装を任せたら、既存のマイグレーションファイルを直接書き換えてしまった。CREATE TEMP TABLE → CREATE TABLE のような「テストを通すための修正」をマイグレーションに入れてしまうパターン。マイグレーションファイルは一度適用したら変更禁止が鉄則なので、ハーネス(ガードレール)として仕組み化した。
仕組み:
- lefthookのpre-commitフックで、既存マイグレーションファイルがstagingされていたらエラーにする
- CLAUDE.mdにルールを明記して、AIがそもそも編集しないよう指示する
- 新規マイグレーションの追加は許可、既存ファイルの変更のみブロック
# hook-migration-guard.sh の要点
MIGRATION_DIR="migrations"
CHANGED=$(git diff --cached --name-only -- "$MIGRATION_DIR/" | while read f; do
git show "HEAD:$f" 2>/dev/null && echo "$f" # HEADに存在する = 既存ファイル
done)
if [ -n "$CHANGED" ]; then
echo "ERROR: 既存マイグレーションファイルの変更は禁止です"
exit 1
fi
やってみてどうだったか:
- lefthookフックは人間・AI両方に効くので、CLAUDE.mdだけに頼るより確実
- 「テストを通すためにマイグレーションを変える」というAIの判断は合理的に見えるので、ルール明記だけだと突破されがち。仕組みでブロックするのが大事
- 次はCI側にも同様のチェックを入れたい
