これは実装案ではない。issue #81 で提案されている track capability が実現したとき、
Butler を使った issue 運用が日常的にどう変わるか、その「手触り」を先に持っておくための文書。
設計(API シグネチャ、データの持ち方、既存 issue_* / work_record との統合度など)は
この後の相談で詰める。ここでは先に「使っているときの景色」を合わせておきたい。
そのため、この文書には以下を書かない。
かわりに、具体的なシナリオを追って「その場面で何が起きているか」を描く。
| 用語 | この文書での意味 |
|---|---|
| 親issue | 「〇〇を作る」「〇〇を解決する」という大きな目的を持つ issue。track_summarize の対象。 |
| track | 親issueの下にぶら下がる、1回の探索・試作・方針転換・やり直しの単位。実体は子issue。 |
| kind | track の性質。experiment / prototype / exploration / pivot / restart など。 |
| outcome | track が終わったときの結果。adopted / discarded / superseded / inconclusive / baseline / needs-review。 |
| 本線 | 親issue時点での「今のところ採用されている案・実装」。track群の中から浮かび上がる。 |
track は「小さい issue」以上でも以下でもない。特別なオブジェクトを新設するのではなく、
既存の issue の使い方に「親子関係」「kind/outcome というラベル」「型を持った記録」を足すイメージ。
実は #80 → #81 の流れ自体が track capability が欲しくなった典型例なので、
これを track capability があった世界に置き直してみる。一番実感が持てるはずなので最初に置く。
「Gitea Project ボード的な整理をしたい」という発想が出た時点で、まず親issueを立てる。
track_start(parent_issue_id=None, title="探索・試作・やり直しを整理できるようにしたい", kind=None)
→ 親issue #80 相当が作成される
この時点ではまだ「Gitea Project を使う」と決め打ちしていない。親issueは目的(やりたいこと)
のレベルで立てる。手段はこの下の track で探る。
track_start(parent_issue_id=80, title="Gitea Project REST API の対応可否を調査", kind="exploration")
→ 子issue #80-a が作成される(実運用では番号は独立、本文の親リンクで #80 の下と分かる)
調査を進めるたびに:
track_record(track_issue_id=<80-a>, summary="公式swagger template全文を確認。Project用エンドポイント無し",
artifacts=["swagger確認メモ"], decision=None)
track_record(track_issue_id=<80-a>, summary="routers/api/v1/api.go のroute一覧を確認。Project系route無し",
artifacts=[], decision=None)
track_record(track_issue_id=<80-a>, summary="実環境 gitea.keinafarm.net へ detect_project_api を実行。404",
artifacts=["detect_project_api実行ログ"], decision=None)
track_record(track_issue_id=<80-a>, summary="Gitea本体のGitHub issue #14299,#35921,#36824を確認。Web UI専用機能と確定",
artifacts=[], decision="REST API非対応と確定")
途中経過が comment としてそのまま issue に積まれていく。ここは現状の運用(issue_comment で
逐一記録する)とほぼ同じ感覚。track_record は「issue_comment + 判断ラベル」くらいの薄さで良い。
track_finish(track_issue_id=<80-a>, outcome="baseline")
「Project API は無い」という調査結果そのものは discarded ではない。これは今後の判断の土台
(baseline)になる事実なので、outcome は baseline として残す。「捨てた」わけではなく
「土台として確定した」という区別がここで効いてくる。
調査結果を受けて、「Project ボードではなく Butler 自体に track capability を作る」という
方針転換が起きる。これは #80 の続きの作業ではなく、目的が変わった別の親issueとして立てる方が扱いやすい。
track_supersede(old_issue_id=80, new_issue_id=81, reason="Project APIが無いため、Project依存をやめ、Butler内でtrack capabilityを持つ方針へ転換")
#80 は「Project API 非対応の判定と手動fallback誘導」までを達成条件としてclose。
#81 は新しい親issueとして、#80 から引き継ぐ前提をそのまま本文に持って開始する。
(実際に今の #81 の本文がまさにこの形になっている)
#81 の中でこの後 track_start が複数回呼ばれ、案ごとに track が積まれていくはず。
ある程度たまったところで:
track_summarize(parent_issue_id=81)
を呼ぶと、たとえば次のような一覧が返ってくるイメージ。
親issue #81: track capability を追加したい
baseline:
- #80-a Gitea Project REST API調査 → API非対応と確定
superseded:
- #80 Gitea Project ボードを操作可能にする → #81へ方針転換
exploration (進行中):
- #81-a track を issue_* の薄いラッパーにする案
- #81-b track を独立データ構造で持つ案
未着手:
- track_restart の記録先設計
親issueを開いたBrain(人間でもよい)は、このsummaryを読むだけで「今何が決まっていて、
何がまだ探索中で、何が土台として確定しているか」を掴める。docs/検討用フォルダを
何ファイルも開いて時系列を追い直す必要がない。
架空の例として「音声通知チャンネルを追加する」という機能開発を考える。
track_start(parent_issue_id=None, title="音声通知チャンネルを追加する", kind=None)
→ 親issue #150
「Slack webhook で行けるはず」という前提で試作を始める。
track_start(parent_issue_id=150, title="Slack webhook経由での通知試作", kind="prototype")
→ #150-a
track_record(#150-a, summary="webhook URLでの送信は成功。ただし添付ファイルのサイズ制限に引っかかる")
同時に別の手段も気になったので、比較用に並行してtrackを立てる。
track_start(parent_issue_id=150, title="Discord webhook経由での通知試作", kind="prototype")
→ #150-b
track_finish(#150-a, outcome="needs-review") # サイズ制限の回避策待ち
track_finish(#150-b, outcome="adopted") # 添付制限が緩く、そのまま使える
運用に乗せたあとで、Discord webhook 側がレート制限に頻繁に引っかかることが分かった。
ゼロから作り直すのではなく、「何を捨て、何を残すか」を明示して restart する。
track_restart(parent_issue_id=150,
reason="Discord webhookがレート制限に頻繁に抵触。キューイング前提の設計に作り直す",
preserve_artifacts=["通知本文フォーマット処理", "添付ファイル圧縮処理"])
→ #150-c (kind=restart)
#150-c の本文には、テンプレートとして次が埋め込まれるイメージ。
## この restart で捨てるもの
- webhookへの同期直接送信ロジック
## この restart で残すもの
- 通知本文フォーマット処理(#150-b内で実装済み、流用する)
- 添付ファイル圧縮処理(#150-a内で実装済み、流用する)
## なぜやり直すか
Discord webhookのレート制限により、送信失敗・遅延が頻発するため。
## 次の出発点
キュー + リトライを前提にした送信ワーカーとして再設計する。
これが後から readable なまま残るのが肝で、「なぜかDiscord関連のコードが2種類ある」と
将来のBrainや人間が混乱する事態を防ぐ。
track_summarize(parent_issue_id=150)
親issueをcloseする前にこれを呼び、最終的にどのtrackが adopted になったか、
discarded / superseded になったものは何が理由だったかを本文に転記してからcloseする。
track/kind:pivot, track/outcome:adoptedhandover_prepare のスナップショットにtrack_summarize の出力をそのまま埋め込める。NEXT_ACTION を明示するhandoverルール([[feedback_handover_snapshot_quality]])と相性が良い。未着手 / needs-review 欄から| 資産 | track capability導入後の役割 |
|---|---|
docs/検討用/*.md |
収束後の正本。実装計画書、確定した設計。track群の結論を清書する置き場所。 |
| track(issue) | 収束前の過程ログ。何を試して、何を捨てて、なぜそうしたかの生々しい記録。 |
work_record |
track内の個々の作業コミットの記録。track_recordの下でも今まで通り使われる。 |
issue_comment |
track_recordが内部的に issue_comment を使う形になっても違和感はない(薄いラッパー)。 |
handover |
track_summarizeの出力を引き継ぎ内容に含める運び役。 |
つまり「docsに書くほど確定していないが、issue_commentだけでは判断理由が散らかる」
という中間の置き場所として track が機能するイメージ。
ここからは設計相談のための入り口として残す。結論は出していない。
track = 必ず子issueか?
3-2 のような小さい調査でも毎回issueを1つ作るのは重い可能性がある。
「issueを作るほどでもないtrack」(comment単位の軽量track)を許すか、
常にissue単位に統一して見通しの良さを優先するか。
track_record の粒度
4-2 のように、prototypeの試行錯誤を全部commentに残すと、issueが長大化しないか。
要約タイミング(track_finish時にまとめ直す等)を運用ルールとして持たせるか。
track_restart の「残すもの」の実体
4-4 の「残すもの」はコード資産であることが多い。issue本文に文章で書くだけでなく、
実コードのブランチ/コミットへのリンクをどう持たせるか。
track_summarize の出力先
親issue本文を毎回書き換えるのか、commentとして追記していくのか。
本文書き換えは最新状態が見やすい一方、履歴が消える。
ラベル運用の負荷
track/kind:* track/outcome:* をBrainが自動で貼るのか、人間の確認を挟むのか。