作成日: 2026-03-31 改訂日: 2026-03-31(v3: SQLite移行・手順遵守の強制メカニズム・BACKLOGステータス追加) 調査方法: Codex (GPT-5.4) + Claude Opus WebSearch の並行調査 対象: Telegram秘書(Claude Code)→ 作業セッション dispatch(指示を送って作業させる)構成
あなたが普段やっていること:
ready と返ってくる問題: 秘書は「作業セッションが終わった」ことは確認しているが、「元の指示の意図を満たしているか」は確認していない。
この文書は、「秘書がちゃんと検証してから完了報告する仕組み」の設計書。
今の秘書のジョブ管理はこうなっている:
PENDING(未着手)→ SENT(送信済み)→ DONE(完了)/ FAILED(失敗)/ BLOCKED(止まっている)
DONEにする条件が「作業セッションから ready が返ってきた」だけ。
「指示通りにできたか」の確認がゼロ。
Codex (GPT-5.4) の調査で出た核心:
作業セッションを「自分で判断して動く賢い存在」として信頼するより、 「言われたことをやる作業者」として扱い、秘書が検証と制御の責任を持つ方が安定する。
つまり、作業セッションが「できました」と言っても、秘書が「本当にできてるか」を自分で確認する。
世の中のAI管理ツール(フレームワーク = AIの動きを制御するための仕組み)を調査した。 共通して言えることは:
- 作業者の「できました」を信用しすぎない
- 管理側が「完了条件のリスト + 確認処理」で検証する
- 人間の判断が必要な場面だけ、人間に聞く
| 何をするか | 具体的な仕組み |
|---|---|
| 全体の管理 | 作業の流れを「状態の図」として定義する。「今どの状態か」「次にどこに進むか」を明示的に書く |
| 状態の記録 | 全員が共有する状態メモがあり、各作業者が読み書きする。任意の時点の状態を保存(チェックポイント)でき、そこからやり直せる |
| 作業の割り振り | 図の上で「次はこの作業者」と明示的に制御する。「監督役が次の担当を選ぶ」パターンも組める |
| 失敗への対応 | 作業者ごとに「失敗したら何回やり直すか」を設定できる。チェックポイントから途中再開も可能 |
| 確認の仕組み | 「ここで一旦止めて人間に確認する」という中断命令がある。検証用の作業者が出力をチェックして、ダメなら差し戻す |
秘書との相性: ◎ 一番近い。 「作業の流れを図で管理して、途中で検証して、ダメなら戻す」という考え方が、秘書の「指示を送る → 確認する → 足りなければ追加指示する」と一致する。
| 何をするか | 具体的な仕組み |
|---|---|
| 全体の管理 | 「このイベントが起きたら次にこれをやる」という流れで動く |
| 状態の記録 | 状態をデータベースに自動保存できる |
| 作業の割り振り | チーム単位で作業を呼び出す。「マネージャー役が作業を割り振る」パターンも選べる |
| 確認の仕組み | 作業ごとに「出力のチェック関数」(ガードレール = 品質の番人)を設定できる。チェックに引っかかったら自動でやり直す |
| 人間への確認 | 「ここで人間に聞く」という処理を挟める |
秘書との相性: △ 業務アプリを素早く作るには向いているが、秘書のようにtmuxで作業セッションを制御する用途には合わない。ただし「作業ごとにチェック関数を付ける」という発想は参考になる。
| 何をするか | 具体的な仕組み |
|---|---|
| 全体の管理 | AI同士がメッセージをやり取りして協力する方式。「毎ターン誰が発言するか」を制御する |
| 状態の記録 | 会話の履歴が暗黙の状態記録になる(明示的な状態メモはない) |
| 作業の割り振り | AIが次の発言者を選ぶ方式や、明示的にバトンタッチする方式がある |
| 確認の仕組み | 「特定のキーワードが出たら終了」という終了条件や、「ツール実行前に人間が承認/拒否する」仕組みがある |
| 品質チェック | 作業者とは別の「批評家AI」が品質をチェックするループを組める |
秘書との相性: △ AI同士の会話には向いているが、秘書のケースでは「会話」より「ジョブの管理」が主役。「作業者とは別のAIが検証する」という発想は取り入れられる。
| 何をするか | 具体的な仕組み |
|---|---|
| 全体の管理 | 作業ステップとイベントを配線して動かす。順番に実行、同時に実行、引き継ぎなど複数のパターンを提供 |
| 状態の記録 | ステップごとに状態を持つ。実行ログも取れる |
| 確認の仕組み | 人間を挟むループ、マネージャーが全体を制御する方式がある |
秘書との相性: △ 考え方は良いが、まだ実験段階の機能が多い。Microsoft系のサービス(Azure, Teams等)を使うなら選択肢に入る。
秘書のtmux + Telegram + Claude Code構成では、既存ツールをそのまま使うより、LangGraphの考え方(状態の保存と復元、明示的な状態遷移、検証ステップ)を自分たちの仕組みに取り入れるのが最適。
VMAO(2026年3月の論文)が「計画 → 実行 → 検証 → 再計画」の4段ループを検証し、検証ステップを入れることで完了度が5段階中3.1→4.2に改善したことを示した。
今まで検証できなかった根本原因は、最初の指示が曖昧なまま作業に流れていたこと。 だから最初に「何ができたら完了か」を明確にする。
あなたの入力: 「コールリマインダーの課金をパック制に変えて」
↓ 秘書が以下のように整理する
【意図の整理】
やりたいこと: Stripe連携をサブスクリプションから回数パック制に変更
制約:
- Payment Link方式で実装
- 既存ユーザーのデータを壊さない
やらないこと:
- UI変更は今回のスコープ外
完了条件(何ができたら終わりか):
- パック購入のPayment Linkが生成できる
- 購入後にコール回数が加算される
- テストが通る
証拠として必要なもの:
- Payment LinkのURL
- テスト実行結果
あなたに聞かないと決められないこと:
- パック価格(500円/1000円/2000円のどれか)
「あなたに聞かないと決められないこと」がある場合、作業を始めずにまず聞く(WAIT状態にする)。
この整理をもとに、作業セッションに渡す「作業指示書」を作る:
【作業指示書】
作業内容: "Stripe Payment Link方式でパック購入を実装"
期待する成果物: "Payment Link生成API + 回数加算Webhook"
確認項目:
- "tsc --noEmit(型チェック)がエラー0"
- "vitest(テスト)が全パス"
- "Payment Link URLが返る"
制限時間: 60分
リスク: 中(Stripe本番に影響しうる)
作業セッションには曖昧な自然文ではなく、上の作業指示書をそのまま渡す。 作業セッションが終わったら、以下の情報を秘書が取得する:
作業セッションから取得する情報:
何をしたか: 1-2行のまとめ
作成/変更したファイル: ファイルのリスト
実行したコマンド: コマンドと結果
作業者自身の評価: 参考値(これだけでは信用しない)
作業中に出た疑問: 未解決の疑問
重要: 作業者自身の「できました」は参考値に過ぎない。本当にできたかは秘書が確認する。
検証は3段階で行う:
第1層: 自動チェック(機械的に確認できること)
- ファイルが存在するか
- コマンドがエラーなく完了したか
- テストは通ったか
→ 自動で合格/不合格が出る
第2層: 条件チェック(完了条件との照合)
- 完了条件を1つずつ確認する
- 制約に違反していないか
- 「やらないこと」に手を出していないか
→ 合格 / 一部未達 / 不合格
第3層: 意図チェック(元の指示の趣旨との照合)
- 元の「やりたいこと」と実際の成果物を照らし合わせる
- 「やったこと」が「やりたかったこと」と一致しているか
- AIが審査役として判定する
→ 合格 / 一部未達 / 不合格 / あなたの判断が必要
検証結果のまとめ:
【検証レポート】
結果: 合格 / 一部未達 / 不合格 / あなたの判断が必要
完了率: 80%(完了条件3つ中2つを満たした)
合格した項目: ["テスト通過", "Payment Link URL生成確認"]
不合格の項目: ["回数加算Webhookの実装が未完了"]
満たされていない意図: ["購入後にコール回数が加算される が未達"]
リスク: ["Stripe本番キーがハードコードされている"]
次のアクション: "Webhook実装を追加指示として再送信"
一部未達や不合格の場合、足りない部分だけを「差分チケット」として再計画する:
【差分チケット】
足りないこと: "購入後のコール回数加算が未実装"
元の意図: "パック購入 → 回数加算"
できている部分: "Payment Link生成は完了"
追加で必要な作業: "Stripe Webhook受信 → DB更新ロジック"
優先度: 高
「最初からやり直せ」ではなく「ここだけ足りない」と伝える。 作業セッションに全部の情報を再送しない。
あなたの入力(Telegram)
│
┌────▼─────────┐
│ 計画 │ 入力 → 意図の整理 → 作業指示書
│ │ あなたに聞くことがある? → 聞く(WAIT)
└────┬─────────┘
│
┌────▼─────────┐
│ 実行 │ 作業指示書 → 作業セッションに送信 → 作業者が実行
│ │ 制限時間超過 → 失敗
└────┬─────────┘
│ ready(作業完了の合図)
┌────▼─────────┐
│ 検証 │ 第1層: 自動チェック → 第2層: 条件チェック → 第3層: 意図チェック
│ │
└────┬─────────┘
│
┌───┴────┐
│ │
合格 一部未達/不合格
│ │
完了 ┌──▼──────┐ あなたの判断が必要
│ │ 再計画 │ │
│ └──┬──────┘ WAIT(聞く)
│ │
│ 差分チケット
│ │
│ ┌────▼─────────┐
│ │ 再実行 │ 足りない部分だけ再送信(最大1回)
│ └────┬─────────┘
│ │
└───────┘
テスト駆動開発(TDD = テストを先に書いてから実装する開発手法)の考え方は、秘書の検証にそのまま使える。
TDDでは:
| TDDでやること | 秘書の検証でやること | 具体的にどうなるか |
|---|---|---|
| 失敗するテストを先に書く | 完了条件を先に決める | 作業を始める前に「何ができたら完了か」を定義する。これがないタスクは着手しない。「修正して」→ ダメ。「lintエラー0、テスト通過、説明文追加」→ OK |
| テストを通す最小限のコードを書く | 検証を通る最小限の成果物をまず取る | 完璧な最終版ではなく、完了条件を満たす最小の成果物を先に取る。「まずテスト通す」→「次に説明文を整える」と段階的に進める |
| テストが通った状態で構造を改善する | 検証の仕組み自体を改善する | 失敗した検証の理由を蓄積して、次回の計画フェーズに反映する |
TDDで「テストを先に書く」のと同じように、タスクを実行する前に「何なら合格か」を先に書く。
TDD:
1. テストを書く(まだ実装していないので当然失敗する)
2. テストが失敗することを確認する
3. 実装する(テストが通るようにする)
秘書の検証:
1. 完了条件を書く
2. 今はそれを満たしていないことを確認する(当然満たさない)
3. 作業セッションに送って実行する(完了条件を満たすことを目指す)
完了条件のない仕事は着手しない。 これが最も重要な原則。 「やっておいて」では検証できない。「何ができたら完了か」が言葉になっていることが前提。
TDDでは「テストを通す最小限のコード」を書く。 秘書の検証でも同じ: 完了条件を満たす最小の成果物を先に取る。
悪い進め方: 「全部完璧にしてから報告」
→ 時間がかかる、途中で方向性がズレても気づけない
良い進め方: 「まず完了条件の1つ目を通す → 検証 → 次へ」
→ 早い段階でフィードバックが得られる、ズレに早く気づける
TDDでは「テストが通った状態を維持しながら構造を改善する」。 秘書の検証でも、検証の仕組み自体を改善する。
最終的な成果物だけでなく、検証の過程こそが大事:
蓄積すべき情報:
- どの完了条件で判定したか
- どの差分チケットで差し戻したか
- 何回の再計画で収束したか
- どの種類の依頼が「あなたに聞く」に落ちやすいか
- よくある検証失敗パターン:
- 指示が曖昧で作業者が迷走した
- テスト結果を返し忘れた
- 入力が複数の解釈ができるものだった
秘書の改善対象を「回答の品質」から「検証の仕組みの品質」に広げる。そこが本体。
TDDでは、最初に「テストすべき項目をリストにまとめる」。 秘書の検証でも同じ:
1. あなたの入力から完了条件のリストを洗い出す
2. リストから「ひとつだけ」選び、作業指示書に変換して作業セッションに送る
3. 検証で合格を確認する(気づきは完了条件リストに追加)
4. 必要に応じて再計画(改善に相当)
5. リストが空になるまで 2 に戻る
BACKLOG(いつかやる)
│ ← Phase 3等の未着手項目はここに入る
│ ← ユーザーが「やる」と決めたら PENDING に昇格
▼
PENDING(未着手)
│
▼
SENT(送信済み)──────────────────────────┐
│ │
▼ │
IN_PROGRESS(作業中) │
│ │
▼ │
VERIFYING(検証中) │
│ │
├── 完了条件100%達成 ──→ DONE(完了) │
│ │
├── 一部未達/不合格 ──→ RETRY(再送信)──┘
│ (差分チケット付き、最大1回)
│
├── あなたの判断が必要 ──→ WAIT(あなたに聞く)
│
└── 回復不能 ──→ FAILED(失敗)
│
WAIT(あなたに聞いている状態)
│ │
├── 承認 ──→ DONE │
│ │
├── 差し戻し(追加指示付き)──→ SENT(再送信)
│ │
├── 拒否/中止 ──→ FAILED
│
├── 時間切れ(リスク低)──→ DONE(できた分で完了扱い)
│
├── 時間切れ(リスク高)──→ FAILED
│
└── 時間切れ(業務系)──→ EXPIRED(期限切れ。リマインド → 再確認)
BLOCKED(外部の都合で止まっている)──→ SENT(解消後)/ FAILED
WAITでもPENDINGでもない理由:
| ステータス | 意味 | BACKLOGとの違い |
|---|---|---|
| WAIT | あなたの判断を待っている。質問が1つある | BACKLOGには具体的な質問がない |
| PENDING | やることが決まっていて、あとは送信するだけ | BACKLOGはまだ「やるかどうか」が未定 |
| BACKLOG | 「いつかやる」候補。やるかどうか・いつやるかは未定 | — |
BACKLOGの運用ルール:
BACKLOGの記録形式:
{
"id": "j-100",
"status": "BACKLOG",
"message": "失敗パターンの蓄積 → 検証手順の改善",
"intent": "検証の失敗パターンをDBに蓄積し、次回の計画フェーズに自動反映する仕組み",
"doneWhen": ["(昇格時に確定)"],
"phase": "Phase 3",
"priority": "low",
"created_at": "2026-03-31T00:00:00Z"
}
WAITは「止まっている」のではなく「あなたに判断を返している状態」。 完了ではないが、あなたの返答で再開できる。
| 条件 | 今のシステムだとこういう場面 |
|---|---|
| 複数の正解がありあなたの好みで決めるしかない | 「パック価格を500円/1000円/2000円のどれにする?」 |
| 外部に影響する操作の実行前に許可が必要 | 「Stripe本番にPayment Linkを作成してよい?」 |
| 検証したが判断がつかない | 「テストは通ったが、非エンジニア向け説明が元の意図に含まれるか判断できない」 |
| 元の指示が曖昧で安全に補えない | 「"いい感じにして" の "いい感じ" が特定できない」 |
| 時間切れだが途中まではできた | 「3件中2件は完了。残り1件はAPI制限で実行できなかった」 |
{
"id": "j-042",
"status": "WAIT",
"wait": {
"reason": "choice(選択が必要)",
"question": "パック価格をどれにする?",
"options": [
"500円(5コール)← 試しやすい",
"1000円(12コール)← 最もお得(推奨)",
"2000円(30コール)← ヘビーユーザー向け"
],
"recommendedOption": "1000円(12コール)",
"contextSnapshotId": "snap-042-a(作業途中の状態を保存したID。再開時にここから続ける)",
"expiresAt": "2026-04-01T12:00:00Z",
"onTimeout": "remind(時間切れならリマインドを送る)"
}
}
全てのAI管理ツールに共通する効果的な整理:
やりたいこと: 何を達成したいか(あなたの元の指示を1行にまとめたもの)
完了条件: 何なら完了か(検証可能な条件のリスト = TDDの「テストリスト」に相当)
返し方: どういう形で結果を返すか
Task(
description="...", # やりたいこと
expected_output="...", # 完了条件(自然言語)
output_pydantic=Model, # 返し方(データの型)
guardrail=validate_fn, # チェック関数
guardrail_max_retries=2, # やり直し上限
)
state = {
"task": "...",
"criteria": ["5件以上", "各項目にURL含む"], # 完了条件
"draft": None,
"feedback": None,
"attempts": 0,
"ok": False,
}
2026-03-31の失敗: prompt.mdに「検証しろ」と書いてあったのに、秘書は検証せずに完了報告した。
「手順を書く」と「手順を守る」は別物。 自然言語で書いた手順は「お願い」であって「強制」ではない。LLMは指示を理解はしているが、コンテキストが長くなると優先順位が下がったり、省略したりする。
Codex (GPT-5.4) に相談した結果、手順を守らせる強さは以下の順:
弱い ←──────────────────────────────────→ 強い
prompt.md 構造化プロンプト hook検査 hook+ステートマシン
だけ +報告テンプレ (実行点 (状態遷移で
ート固定 でブロック) 順番を管理)
prompt.mdの指示を曖昧な自然文から構造化する。「やってね」ではなく「この形で出せ」。
悪い例(今の書き方):
作業が終わったら検証してから報告してください。
良い例(構造化):
<completion_contract>
<workflow>
1. tmux readで作業結果を取得する
2. doneWhenの各項目を1つずつ照合する
3. 全項目の検証結果を記録する
4. 以下のJSON形式で完了報告する
</workflow>
<required_report_format>
{
"job_id": "j-XXX",
"task_status": "done|partial|failed|need_input",
"checks": [
{"condition": "完了条件の文言", "result": "pass|fail", "evidence": "確認した根拠"}
],
"intent_match": "yes|no|unclear",
"next_action": "none|retry|ask_user"
}
</required_report_format>
<hard_rules>
- checksが空のまま task_status を done にしてはならない
- evidence が空のチェック項目は fail 扱い
- intent_match が unclear なら task_status は need_input
</hard_rules>
</completion_contract>
コツ: 「禁止」より「何を出せ」を書く。モデルは「やるな」より「こう出せ」の方に従いやすい。
完了通知を自由文にしない。JSON固定にして、検証結果フィールドを必須にする。
{
"job_id": "j-042",
"task_status": "done",
"checks": [
{
"condition": "パック購入のPayment Linkが生成できる",
"result": "pass",
"evidence": "Payment Link URL: https://buy.stripe.com/xxx を生成確認"
},
{
"condition": "購入後にコール回数が加算される",
"result": "pass",
"evidence": "vitest webhook.test.ts 全パス(3/3)"
},
{
"condition": "テストが通る",
"result": "pass",
"evidence": "vitest --run 結果: 12 passed, 0 failed"
}
],
"intent_match": "yes",
"next_action": "none"
}
なぜ効くか: モデルは「最終出力の形」に引っ張られる。テンプレートにchecksフィールドがあると、「ここを埋めないと完成しない」という圧力がかかる。嘘を書いても result: pass と evidence: "" の矛盾で検出できる。
Claude Codeのhookシステムで、ソフトウェア的に強制する。秘書がtmuxで動くClaude Codeセッションなので、hookが使える。
Stop hook — 検証なしの終了をブロック:
秘書が「完了しました」と言おうとした時、検証結果がDBに記録されていなければ終了を拒否する。
#!/bin/bash
# .claude/hooks/stop_guard.sh
# Stop hookで「検証なしの終了」を防ぐ
set -euo pipefail
# 最新のアクティブジョブを確認
ACTIVE_JOB=$(sqlite3 jobs.db "SELECT id FROM jobs WHERE status = 'VERIFYING' LIMIT 1")
if [ -n "$ACTIVE_JOB" ]; then
# 検証結果が記録されているか確認
CHECKS=$(sqlite3 jobs.db "SELECT COUNT(*) FROM verification_checks WHERE job_id = '$ACTIVE_JOB'")
if [ "$CHECKS" -eq 0 ]; then
cat <<JSON
{
"decision": "block",
"reason": "検証が未完了。doneWhenの各項目を確認してから完了報告しろ。job_id: $ACTIVE_JOB"
}
JSON
exit 0
fi
fi
PostToolUse hook — 完了報告の形式チェック:
Telegram送信前に、報告がJSON形式で検証結果を含んでいるか確認する。
設計上の注意:
セッション圧縮(compact)後にルールを忘れる問題の対策。毎セッション開始時にcompletion_contractを再注入する。
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "cat .claude/secretary-contract.xml"
}
]
}
]
}
}
第1層 構造化プロンプト: 「何を出すべきか」を明確にする(理解させる)
第2層 報告テンプレート: 「検証しないと報告が完成しない」(動機づける)
第3層 hook: 「検証なしでは終了できない」(物理的に止める)
全部必要ではない。Phase 1では第1層 + 第2層で始めて、効果を測定してからhookを追加する。
段階1: 形式チェック — 型が合ってるか、必須項目があるか ← 必ずやる
段階2: 内容チェック — 件数・URL形式・文字数・禁止語 ← プログラムで書ける
段階3: AIによる判定 — 文体・網羅性・妥当性(主観的な基準) ← 必要な時だけ
段階4: 人間の承認 — 外部に影響する操作 ← 本当に必要な時だけ
秘書のケースでは段階3が中心になる(作業セッションの出力は自由な文章なので、形式チェックだけでは判定しにくい)。
while attempts < max_attempts:
result = worker(task, context) # 作業者にやらせる
verdict = verify(result, criteria) # 検証する
if verdict.ok:
return DONE(result) # 合格なら完了
context["feedback"] = verdict.feedback # 不合格ならフィードバックを追加
attempts += 1 # やり直し回数を増やす
return FAIL(last_result, last_feedback) # 上限に達したら失敗
重要な3つの原則:
評価: ○ 方向性は正しい。ただし単独では弱い
評価: ◎ これが最も効果的。全てのAI管理ツールの共通パターンと一致
expected_output、LangGraphのcriteriaと同じ発想作業を送る時:
1. あなたの入力 → 意図の整理(完了条件を先に決める)
2. あなたに聞くことがあれば → WAIT
3. 意図の整理 → 作業指示書に分解
4. jobs.db にやりたいことと完了条件を記録
5. 作業セッションに送信(完了条件を満たすことを目指す)
完了の合図を受け取った時:
1. 「検証中」に遷移
2. tmux read(画面の出力を読み取る)で作業結果を取得
3. 検証3段階:
a. 第1層: 自動チェック(テスト結果、ファイル存在等)
b. 第2層: 条件チェック(完了条件をどれだけ満たしたか、制約違反がないか)
c. 第3層: 意図チェック(元のやりたいこととの整合性)
4. 結果に応じて:
- 合格 → 完了 + Telegramで報告
- 一部未達 → 差分チケット付きで再送信(最大1回)
- 不合格 → 失敗 + Telegramで報告
- 判断つかない → WAIT + Telegramで質問
再計画時(仕組みの改善に相当):
- 失敗パターンを蓄積
- 次回の計画に反映
- 「検証の仕組みの品質」を継続的に改善
jobs.jsonは現在1200行超。以下の問題がある:
| 問題 | 影響 |
|---|---|
| 読み書きが重い | 1ジョブの更新でも全件読み込み→全件書き出しが必要 |
| 同時書き込み競合 | 秘書とdispatch-job.shが同時にファイルを触ると壊れる |
| クエリができない | 「SENT状態のジョブだけ取得」に全件走査が必要 |
| BACKLOGの追加で更に肥大化 | Phase 3の項目も入ると管理不能 |
SQLiteなら:
-- メインテーブル: ジョブ
CREATE TABLE jobs (
id TEXT PRIMARY KEY, -- "j-042"
message TEXT NOT NULL, -- 元の指示
intent TEXT, -- やりたいこと(秘書が整理)
session TEXT, -- 作業セッション名
status TEXT NOT NULL DEFAULT 'PENDING',
-- BACKLOG, PENDING, SENT, IN_PROGRESS, VERIFYING,
-- DONE, FAILED, BLOCKED, WAIT, RETRY, EXPIRED
phase TEXT, -- "Phase 1", "Phase 3" 等
priority TEXT DEFAULT 'medium', -- low, medium, high
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
sent_at TEXT,
done_at TEXT
);
-- 完了条件: 1ジョブに複数の条件
CREATE TABLE done_when (
id INTEGER PRIMARY KEY AUTOINCREMENT,
job_id TEXT NOT NULL REFERENCES jobs(id),
condition TEXT NOT NULL, -- "テストが通る"
sort_order INTEGER DEFAULT 0
);
-- 検証結果: 完了条件ごとの検証記録
CREATE TABLE verification_checks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
job_id TEXT NOT NULL REFERENCES jobs(id),
condition TEXT NOT NULL, -- done_whenの文言
result TEXT NOT NULL, -- "pass", "fail"
evidence TEXT, -- "vitest --run: 12 passed"
checked_at TEXT NOT NULL
);
-- 検証サマリ
CREATE TABLE verification_summary (
job_id TEXT PRIMARY KEY REFERENCES jobs(id),
intent_satisfied TEXT, -- "yes", "no", "unclear"
result_summary TEXT,
retry_count INTEGER DEFAULT 0,
max_retries INTEGER DEFAULT 1
);
-- WAIT状態の詳細
CREATE TABLE wait_details (
job_id TEXT PRIMARY KEY REFERENCES jobs(id),
reason TEXT NOT NULL, -- "choice", "permission", "unclear"
question TEXT NOT NULL,
options TEXT, -- JSON配列
recommended_option TEXT,
expires_at TEXT,
on_timeout TEXT DEFAULT 'remind' -- "remind", "fail", "done"
);
-- インデックス: よく使うクエリを高速化
CREATE INDEX idx_jobs_status ON jobs(status);
CREATE INDEX idx_jobs_session ON jobs(session);
CREATE INDEX idx_done_when_job ON done_when(job_id);
CREATE INDEX idx_checks_job ON verification_checks(job_id);
設計のポイント:
done_whenを別テーブルにした理由: 完了条件ごとに検証結果を紐づけるためwait_detailsを別テーブルにした理由: WAIT以外のジョブでは不要なカラムだからPRAGMA journal_mode=WAL;(初回接続時に実行)#!/bin/bash
# migrate-jobs-to-sqlite.sh
# jobs.json → jobs.db への移行
# 1. バックアップ
cp jobs.json jobs.json.bak
# 2. SQLiteデータベース作成(上記スキーマ)
sqlite3 jobs.db < schema.sql
# 3. jqでJSONを読み、SQLに変換してインポート
# 各ジョブに対して:
# - jobs テーブルにINSERT
# - doneWhen があれば done_when テーブルにINSERT
# - verification があれば verification_summary にINSERT
# - wait があれば wait_details にINSERT
# 4. 件数の整合性チェック
# JSON件数 == DB件数 を確認
# 5. 元のjobs.jsonはリネームして保持(1週間後に削除)
現在の dispatch-job.sh は jq で jobs.json を読み書きしている。以下の変更が必要:
変更前(jq + JSON):
jq '.[] | select(.status == "SENT")' jobs.json
jq '(.[] | select(.id == "j-042")).status = "DONE"' jobs.json > tmp && mv tmp jobs.json
変更後(sqlite3):
sqlite3 jobs.db "SELECT * FROM jobs WHERE status = 'SENT'"
sqlite3 jobs.db "UPDATE jobs SET status = 'DONE', done_at = datetime('now') WHERE id = 'j-042'"
段階的に移行する:
db_get_job, db_update_status, db_add_job等)をシェル関数で用意INSERT INTO jobs (id, message, intent, session, status, phase, priority, created_at, updated_at)
VALUES (
'j-042',
'コールリマインダーの課金をパック制に変えて',
'Stripe連携をサブスクリプションから回数パック制に変更。Payment Link方式で実装',
'line-call-reminder',
'SENT',
'Phase 1',
'high',
'2026-03-31T10:00:00Z',
'2026-03-31T10:00:00Z'
);
INSERT INTO done_when (job_id, condition, sort_order) VALUES
('j-042', 'パック購入のPayment Linkが生成できる', 1),
('j-042', '購入後にコール回数が加算される', 2),
('j-042', 'テストが通る', 3);
<completion_contract>
<!-- タスク完了検証(完了条件ファースト) -->
<precondition>
完了条件(done_when)のないタスクは作業に出さない。
「何ができたら完了か」がDBに記録されていることが前提。
</precondition>
<workflow>
1. ステータスを VERIFYING に遷移する
UPDATE jobs SET status = 'VERIFYING' WHERE id = ?
2. tmux read で作業結果を取得する
3. done_when の各項目を1つずつ照合する
4. 検証結果を verification_checks に記録する
INSERT INTO verification_checks (job_id, condition, result, evidence, checked_at) VALUES (...)
5. 以下のJSON形式で完了報告する
</workflow>
<required_report_format>
{
"job_id": "j-XXX",
"task_status": "done|partial|failed|need_input",
"checks": [
{"condition": "完了条件の文言", "result": "pass|fail", "evidence": "確認した根拠"}
],
"intent_match": "yes|no|unclear",
"next_action": "none|retry|ask_user"
}
</required_report_format>
<hard_rules>
- checksが空のまま task_status を done にしてはならない
- evidence が空のチェック項目は fail 扱い
- intent_match が unclear なら task_status は need_input
- 「完了した」という作業セッションの自己申告を鵜呑みにしない
- 完了条件に書かれていない追加作業は評価しない(範囲外)
- 迷ったらneed_input。誤って完了にするより、誤って聞く方がマシ
</hard_rules>
</completion_contract>
| やりたくなること | やらない理由 |
|---|---|
| 出力の型を厳密に定義する | 作業セッションの出力は自由な文章。型で縛れない |
| 自動でテストを実行して検証する | タスクの大半はコード変更ではない(調査・文書作成等) |
| 複数のAIで検証する | 秘書自身が1回検証すれば十分。複数AIでの検証は過剰 |
| 検証結果を点数化する | 合格/不合格/聞く の3値で十分。点数化は手間だけ増える |
| 検証ログを別ファイルに保存する | jobs.dbのverification_checksテーブルで十分。別ログは不要 |
| やり直し回数を動的に変える | 固定1回で十分。2回以上必要なら大抵指示の書き方が悪い |
| 意図の整理を完全に自動生成する | 秘書が生成 → あなたが確認する流れが安全 |
ストレージ移行(intentフィールド追加と同時にやる):
検証の仕組み:
5. 作業送信時に秘書が意図を整理して intent + done_when に記録する(完了条件を先に決める)
6. あなたに聞くことがある場合は送信せずWAIT
7. 秘書の行動指針(prompt.md)を構造化プロンプトに書き換え(completion_contract形式)
8. 完了報告をJSON固定テンプレートにする(第2層: 検証を報告に埋め込む)
9. ready → 検証中 → 3段階検証 → 完了/再送信/WAIT
10. WAIT時はTelegramで質問1つ + おすすめの選択肢
これだけで検証の80%はカバーできる。hookは効果測定後に追加判断する。
以下の項目はjobs.dbに status = 'BACKLOG' で登録する。 PENDINGにはしない(「いつかやる」であり「やることが決まっている」わけではないため)。
BACKLOG棚卸し: 週1回、BACKLOGの項目をTelegramで一覧提示。「やる→PENDING昇格」「まだ→据え置き」「いらない→削除」を聞く。