関連 issue: #81
作成日: 2026-07-09
ステータス: 実装検討案
この文書は、docs/Track_Ledger運用マニュアル.md の検討時点の内容と issue #81 のコメント群を前提にした Track Ledger の実装案である。
Track Ledger は、探索、試作、方針転換、やり直し、破棄、採用判断を、親 Issue / 子 Issue / docs / work_record / handover と連動して扱うための Butler capability とする。
Track Ledger は、ユーザーが毎回「これは Issue コメントに」「これは docs に」「これは Track に昇格して」と仕分ける運用にしない。
ユーザーは普通に相談する。
この案どう思う?
Claude の意見も踏まえてまとめて
これはやり直した方がよさそう
今の方針を整理して
Butler / Brain 側が内容を分類し、必要に応じて確認しながら適切な場所へ残す。
Track Ledger の運用ルールを Instructions やマニュアルに書くだけでは形骸化する。
実装では、次の4つを仕組みに埋め込む。
Brain が覚えているかに依存せず、Brain が普通に Butler を使うと自然に Track Ledger の流れに乗ることを目指す。
Track Ledger は Gitea Project ボードを使わない。
#80 で確認した通り、Gitea 1.25.5 には Project 操作用 REST API が存在しない。Track Ledger は Issue / label / comment / docs / work_record / handover を正本として構成する。
内部専用 DB だけに情報を閉じ込めない。
親 Issue 本文、子 Issue 本文、Issue コメント、label、docs から、人間が Gitea UI 上でも現在地と判断理由を追える状態を保つ。
| 用語 | 意味 |
|---|---|
| Track Ledger | 探索、試作、方針転換、やり直しの台帳機能 |
| 親 Issue | 作業全体の目的、現在の本線、Track 一覧を持つ Issue |
| Track / 子 Issue | ひとつの探索、試作、検証、方針転換、やり直し単位 |
| kind | Track をなぜ始めたか |
| status | Track の進行状態 |
| outcome | Track が終わった結果 |
| Issue 間 relation | 親 Issue 同士、または外部 Issue との由来、継続、置き換え関係 |
| baseline | 後続判断の土台になる確定事実 |
Track は kind / status / outcome の三軸で扱う。
kind:
- exploration
- experiment
- prototype
- pivot
- restart
- comparison
- baseline-check
status:
- in_progress
- paused
- finished
outcome:
- adopted
- discarded
- superseded
- inconclusive
- baseline
- needs-review
outcome は status=finished のときだけ設定する。進行中の track は outcome=null とする。
Track outcome と Issue 間 relation は別レイヤーとして扱う。
| relation | 意味 | 例 |
|---|---|---|
derives_from |
この Issue は別 Issue の結果や前提から派生した | #81 derives_from #80 |
continues_as |
旧 Issue の目的や問題意識を引き継ぎ、形を変えて新 Issue として続く | #80 continues_as #81 |
supersedes |
新 Issue が旧 Issue の役割や方針を置き換える。今後は新 Issue を正とする | 新設計 Issue が旧設計 Issue を置き換える |
split_from |
大きな Issue から一部を切り出した | UI だけ別 Issue へ分離 |
references |
判断材料として参照する | 調査 Issue を参照 |
blocks |
この Issue の解決が別 Issue をブロックしている | API 実装待ち |
continues_as と supersedes の違い:
continues_as: 旧 Issue の成果や問題意識は有効で、形を変えて続く。supersedes: 旧 Issue の役割や方針は新 Issue に置き換わり、今後は新 Issue を見るべき。#80 -> #81 は continues_as が自然である。#80 の Project API 調査は無効ではなく、#81 の baseline になっているためである。
初期実装では、Gitea Issue を正本とする。
| 情報 | 正本 |
|---|---|
| 親 Issue の目的 / 現在の本線 / Track 一覧 | 親 Issue 本文 |
| Track の kind / status / outcome / 判断理由 | 子 Issue 本文 |
| Brain 間レビュー / 判断過程 | Issue コメント |
| 長い比較 / マニュアル / 実装案 | docs |
| 実作業 / 変更ファイル / 検証 | work_record |
| 再開用の圧縮情報 | handover |
内部 DB は初期実装では作らない。必要なら将来、キャッシュや検索インデックスとして追加する。
人間が読める本文と、Butler が機械的に読める metadata を両立させる。
推奨は Markdown 内の HTML comment に JSON metadata を埋め込む方式である。
親 Issue metadata 例:
<!-- butler:track-ledger-parent
{
"version": 1,
"type": "parent",
"issue_id": 81,
"current_mainline": "親 Issue + 子 Issue + docs リンクを基本形にする",
"relations": [
{"relation": "derives_from", "source_issue_id": 81, "target_issue_id": 80},
{"relation": "continues_as", "source_issue_id": 80, "target_issue_id": 81}
],
"tracks": [
{"issue_id": 82, "kind": "exploration", "status": "finished", "outcome": "adopted"}
]
}
-->
子 Issue metadata 例:
<!-- butler:track-ledger-track
{
"version": 1,
"type": "track",
"parent_issue_id": 81,
"kind": "prototype",
"status": "finished",
"outcome": "discarded",
"artifacts": [],
"preserve_artifacts": [],
"discard_artifacts": [],
"next_starting_point": "別案を検討する"
}
-->
metadata は機械処理用であり、ユーザー向けの本文セクションも必ず維持する。
注意: relation は source_issue_id relation target_issue_id と読める向きで保存する。たとえば #81 derives_from #80 は {"relation":"derives_from","source_issue_id":81,"target_issue_id":80}、#80 continues_as #81 は {"relation":"continues_as","source_issue_id":80,"target_issue_id":81} とする。
親 Issue:
## 目的
## 現在の本線
## Track 一覧
### 進行中
### 採用済み
### baseline
### 保留中
### 却下 / 破棄
### 置き換え済み
## Issue 間 relation
## 未検証の論点
## 次にすること
## 後続セッションへの引き継ぎ
子 Issue:
## Track の目的
## 親 Issue
## kind
## status
## outcome
## 背景
## 仮説 / 確認したいこと
## 判断条件
## 結果
## 判断
## 判断理由
## 成果物
## 残すもの
## 捨てるもの
## 親 Issue へ反映する内容
## 次の出発点
## 関連 docs / work_record
Label は補助インデックスとして使う。
track/kind:exploration
track/kind:experiment
track/kind:prototype
track/kind:pivot
track/kind:restart
track/kind:comparison
track/kind:baseline-check
track/status:in_progress
track/status:paused
track/status:finished
track/outcome:adopted
track/outcome:discarded
track/outcome:superseded
track/outcome:inconclusive
track/outcome:baseline
track/outcome:needs-review
track/parent
track/child
正本は本文 metadata とし、label は Gitea UI の絞り込みと視認性のために使う。
track_record や track_capture が Issue コメントを作る場合、可能ならコメントにも軽い metadata を埋め込む。
<!-- butler:track-ledger-record
{
"version": 1,
"record_type": "review",
"decision": "baseline と Issue 間 relation を分ける方針で合意",
"promote_to_parent": true
}
-->
## Review
Claude / CODEX のレビュー結果...
これにより、track_check が「コメント上では合意しているが親 Issue に未反映かもしれない」を検出しやすくなる。
MCP tool は低レベル Gitea 操作名ではなく、Track Ledger の概念で公開する。
track_summarize(parent_issue_id=None)親 Issue 配下の Track と Issue 間 relation を読み、現在地を返す。
parent_issue_id 省略時は default issue を使う。
返却例:
{
"status": "ok",
"summary": "Track Ledger summary を作成しました",
"data": {
"parent_issue_id": 81,
"current_mainline": "...",
"tracks": {
"in_progress": [],
"adopted": [],
"baseline": [{"issue_id": 80, "summary": "Project API 非対応"}],
"paused": [],
"discarded": [],
"superseded": []
},
"relations": [
{"relation": "derives_from", "source_issue_id": 81, "target_issue_id": 80}
],
"next_actions": [
"未検証の論点を確認してください"
],
"warnings": []
}
}
track_start(parent_issue_id, title, kind, purpose, success_criteria=None, docs_refs=None)子 Issue を作成し、親 Issue の Track 一覧へ追加する。
処理:
track/child と track/kind:* / track/status:in_progress label を付ける。next_actions を返す。戻り値には必ず次アクションを含める。
例:
{
"status": "ok",
"summary": "track を開始しました",
"data": {
"track_issue_id": 82,
"next_actions": [
"判断条件を確認してください",
"作業事実は work_record に残してください"
],
"warnings": []
}
}
track_record(track_issue_id, summary, record_type=None, artifacts=None, decision=None, promote_to_parent=False)Track に判断材料やレビュー結果を記録する。
record_type 候補:
progressreviewdecisionriskartifactquestion短い判断材料は Issue コメントとして保存する。長い内容は track_capture へ誘導して docs 化を提案する。
track_finish(track_issue_id, outcome, decision, reason=None, artifacts=None, parent_update=True)Track を完了させる。
処理:
status=finished / outcome を更新する。track/status:finished と track/outcome:* に更新する。parent_update=True なら親 Issue の Track 一覧へ反映する。next_actions / warnings を返す。不整合:
outcome が空なら error または warning。outcome=adopted なのに親 Issue の本線更新がない場合は warning。outcome=superseded なのに置き換え先がない場合は warning。track_restart(parent_issue_id, reason, preserve_artifacts, discard_artifacts, next_starting_point, title=None)kind=restart の Track を作成する。
必須または警告対象:
reasonpreserve_artifactsdiscard_artifactsnext_starting_point戻り値には、旧成果物の退避や親 Issue 更新に関する next action を含める。
track_relation_add(source_issue_id, target_issue_id, relation, reason=None)Issue 間 relation を追加する。
source_issue_id relation target_issue_id と読める向きで指定する。
例:
track_relation_add(source_issue_id=81, target_issue_id=80, relation="derives_from") は #81 derives_from #80track_relation_add(source_issue_id=80, target_issue_id=81, relation="continues_as") は #80 continues_as #81relation:
derives_fromcontinues_assupersedessplit_fromreferencesblocks実装上は parent Issue の metadata と本文 Issue 間 relation セクションを更新する。
continues_as と supersedes は混同しやすいため、tool 側で hint を返す。
track_update_parent(parent_issue_id, mode="sync")子 Issue 群と Issue 間 relation から、親 Issue の Track 一覧と現在の本線を更新する。
mode:
preview: 変更案だけ返すsync: 実際に本文を更新する初期実装では preview を先に実装し、誤更新リスクを下げる。
track_check(parent_issue_id=None)不整合を検出する。
検出項目:
status=finished なのに outcome がないoutcome があるのに status=finished ではないkind=restart なのに必須項目がないsupersedes と continues_as の使い分けが曖昧返却例:
{
"status": "ok",
"summary": "Track Ledger check を実行しました",
"data": {
"warnings": [
{
"code": "missing_parent_ref",
"issue_id": 82,
"message": "子 Issue から親 Issue へのリンクがありません"
}
],
"next_actions": [
"track_update_parent(parent_issue_id=81, mode='preview') を実行してください"
]
}
}
track_capture(parent_issue_id, content, intent=None, apply=False)ユーザーや Brain の発言内容を分類し、保存先を提案または実行する。
分類先:
issue_commentdocstrackwork_recordparent_updatehandoverissue_relation初期実装では rule-based classifier とし、LLM には依存しない。
分類例:
| 条件 | 分類 |
|---|---|
| 短いレビュー、判断材料、合意 | issue_comment |
| 見出し付きの長文、比較表、マニュアル、実装案 | docs |
| 独立して読める検討単位、試作、検証 | track |
| 変更ファイル、検証結果、作業事実 | work_record |
| 「本線」「採用」「次にすること」の更新 | parent_update |
| 次セッションへの再開情報 | handover |
| #80 -> #81 のような親 Issue 間関係 | issue_relation |
apply=False の場合は提案だけ返す。
{
"status": "ok",
"summary": "保存先を分類しました",
"data": {
"classification": "docs",
"reason": "長い統合案であり、Issue コメントより docs が適切です",
"proposed_action": {
"tool": "docs_create_or_update",
"path": "docs/検討用/..."
},
"requires_confirmation": true
}
}
work_observework_observe() は Track Ledger 対象 Issue の場合、required_action または next_actions に Track Ledger の入口を含める。
現行の available_next は list[str] のため、dict を混ぜない。詳細は data.track_ledger や required_action に入れる。
例:
{
"required_action": {
"tool": "track_summarize",
"reason": "active issue has Track Ledger context",
"required_args": {"parent_issue_id": 81}
},
"data": {
"track_ledger": {
"detected": true,
"parent_issue_id": 81,
"has_warnings": true
}
}
}
work_recordwork_record は関連 track を推定または確認できるようにする。
案:
work_record(message, issue_ids=None, track_issue_id=None) を追加する。track_issue_id 省略時、default issue の進行中 track が1件なら関連付け候補にする。ただし、既存 API への破壊的変更は避ける。Phase 1 では Track 側から work_record 参照を持つだけでもよい。
handoverhandover_prepare は Track Ledger 対象 Issue の場合、track_summarize の要約を snapshot に含める。
含めるもの:
handover_read 後の required_action は、現行通り work_observe を返し、その後 work_observe が Track Ledger 入口へ誘導する。
issue_commentissue_comment 自体は残す。
ただし、Track Ledger 対象 Issue に長文コメントや決定コメントを追加しようとする場合、将来的には track_capture への誘導を返せるとよい。
初期実装では破壊的変更を避け、track_record / track_capture を新しい推奨入口にする。
docs 作成/更新の専用 backend がない場合、初期実装では Brain がファイル編集する。
Track Ledger は docs refs を metadata に保持する。
将来:
track_capture(... classification=docs, apply=True) で docs skeleton を生成新規:
butler/track_backend.py役割:
MCP tool から Gitea worker を直接呼ばない。
track_backend.py は Brain-facing の中間層として、既存 backend が wrap している操作は issue_backend 等へ委譲する。一方、label 作成/付与のように GiteaIssueWorker には operation があるが issue_backend に wrapper がない操作は、project_backend.py と同様に track_backend.py 内で TaskContract を組み立てて worker operation を呼んでよい。
つまり、直呼び禁止の対象は MCP facade であり、backend 層では既存 wrapper を優先しつつ、必要な場合に worker operation へ委譲できる。
mcp_facade.py公開 tool を追加する。
track_summarizetrack_starttrack_recordtrack_finishtrack_restarttrack_relation_addtrack_update_parenttrack_checktrack_captureInstructions / Usage Guide / available capabilities に Track Ledger の行動規約を追加する。
Phase 1 では新規 worker は不要。
既存の issue_backend / GiteaIssueWorker で Issue 読み書き、コメント追加、本文更新、label 付き Issue 作成を扱う。
ただし、label 操作は注意が必要である。
GiteaIssueWorker には create_label と add_labels が既に存在する。一方、現時点の issue_backend.py には、これらを Brain-facing backend から呼ぶ wrapper がない。したがって、Track Ledger の Phase 2 で label を付けるには、次のいずれかが必要になる。
issue_backend.py に create_label / add_labels wrapper を追加するtrack_ensure_labels / track_add_labels を track_backend.py 内で実装し、内部で既存 worker operation を TaskContract 経由で呼ぶ初期方針としては、Track Ledger 側に track_ensure_labels を持たせ、既存 issue_backend の公開面を急に広げすぎない。
Track Ledger は track/kind:*、track/status:*、track/outcome:*、track/parent、track/child など多数の label を使う。
これらを track_start のたびに作成してはいけない。create_label は無条件 POST であり、既存 label がある場合に重複作成エラーになる可能性があるためである。
したがって label は冪等な bootstrap として扱う。
追加候補:
track_ensure_labels(parent_issue_id=None)処理:
Phase 2 の書き込み tool は、label 付与前に track_ensure_labels 相当の処理を呼ぶ。ただし label 作成に失敗しても、本文 metadata の更新は可能なら継続する。label は補助であり、正本ではないためである。
対象:
docs/Track_Ledger運用マニュアル.mddocs/検討用/81_Track_Ledger実装案.md作業:
目的:
既存 Issue を壊さず、Track Ledger metadata を読める状態を作る。
実装:
track_backend.pytrack_summarizetrack_check read-onlyこの Phase では Issue 更新をしない。
テスト:
status=finished + outcome missing を warning にするrestart 必須項目 missing を warning にする目的:
Brain が明示的に Track 操作をできるようにする。
実装:
track_ensure_labelstrack_starttrack_recordtrack_finishtrack_restarttrack_relation_addtrack_update_parent(mode=preview)最初は track_update_parent を preview 中心にする。実更新は確認後に行う。
テスト:
track_start が子 Issue body を生成するtrack_ensure_labels が既存 label を重複作成しないtrack_start が label bootstrap 後に track/child と track/kind:* / track/status:in_progress を付与するtrack_finish が status/outcome/label 更新案を生成するtrack_restart が必須項目不足で warning/error を返すtrack_relation_add が continues_as / supersedes の hint を返すnext_actions を返す目的:
親 Issue 台帳を自動または半自動で保つ。
実装:
track_update_parent(mode=sync)テスト:
目的:
Brain がルールを忘れても流れに乗るようにする。
実装:
work_observe に Track Ledger required_action / next_action を追加handover_prepare に track_summarize 添付track_capture rule-based classifierwork_record 関連 track 推定_active_issue_info() が active issue の本文または Track Ledger 検出に必要な最小情報を保持するように拡張テスト:
work_observe が track_summarize を案内するwork_observe が active issue 読み取り結果を再利用し、不要な追加 read を増やさない目的:
ユーザーと Brain が使いやすい形に整える。
実装:
Claude のレビューで、#80 -> #81 の当てはめはまだ軽いと指摘された。
実装前に、次を手動または fixture として検証する。
検証:
baseline-check / finished / baseline として表現できるか。continues_as または derives_from で表現できるか。検証:
review / decision として扱えるか。検証:
これらを子 Issue テンプレートに入れた場合、冗長な項目や不足項目がないか確認する。
Track Ledger tool は共通して次を返す。
{
"status": "ok",
"summary": "...",
"data": {
"next_actions": [],
"warnings": [],
"required_action": null
}
}
next_actions は推奨アクション。Brain は原則従う。
required_action は次に必ず実行すべき操作。既存の Butler 形式に合わせる。
warnings は不整合や未反映の可能性。
track_finish で outcome が空track_restart で reason が空continues_as と supersedes の選択が怪しいavailable_next は現行 list[str] である。
Track Ledger の詳細情報を dict として混ぜない。必要な情報は data.track_ledger、required_action、next_actions へ入れる。
親 Issue 本文には手書きの文脈がある。
初期実装では、本文全体を丸ごと再生成しない。
方針:
mode=preview で止めるただし、現行の issue_backend.update_issue_body() は本文全体を無条件で上書きする。楽観ロックや ETag 相当の仕組みは現時点ではない。
そのため、read -> splice -> write の間に人間が Gitea UI で本文を編集した場合、その変更が失われる TOCTOU リスクがある。これは既存 issue_update_body も持つ弱点だが、Track Ledger の track_update_parent(mode=sync) は頻繁に使われる可能性があるため、より慎重に扱う。
対策:
track_update_parent(mode=preview) を先に実装する。mode=sync では、更新前に直近で読んだ本文と現在本文の差分を確認する。label が欠けても metadata / 本文から復元できるようにする。
label sync は便利だが、正本にしない。
Label 作成は冪等な bootstrap として扱う。track_start のたびに無条件で label を作成しない。
track_capture の初期分類は rule-based にする。
Brain が自然言語判断を補助してもよいが、Butler backend が LLM 呼び出しを前提にしない。
初期実装では docs 作成は Brain のファイル編集でよい。
track_capture は docs 化提案まで行い、自動生成は Phase 5 で扱う。
追加テスト候補:
tests/test_track_backend.pytests/test_mcp_facade.py への tool 一覧追加tests/test_handover_backend.py への Track summary 添付テストtests/test_work_record_capability.py への関連 track 推定テスト重点:
最小で価値が出るスライスは次である。
track_summarizetrack_checktrack_ensure_labelstrack_starttrack_recordtrack_finishtrack_relation_addこの段階では track_capture や work_observe integration がなくても、Track Ledger を手動で回せる。
ただし、形骸化防止のため、最終的には track_capture / work_observe / handover 連携まで必須とする。
Track Ledger 実装の完了条件:
kind / status / outcome を機械的に検査できるbaseline と Issue 間 relation を矛盾なく扱えるtrack_capture を Phase 3 に前倒しするかcontinues_as / supersedes の UI 表示をどうするか