gh-timeline
FreeUse this to view the timeline of GitHub issues or pull requests. Note that it does not fetch the title and description, unlike `gh pr view`.
The English summary is being prepared. Showing the original description.
Use when GitHub may have shipped a GraphQL schema update affecting timeline event unions (PullRequestTimelineItems / IssueTimelineItems), when the gh-timeline dispatcher fallback is hitting more than expected, before a gh-timeline release, or when the user requests phrases like "verify timeline coverage", "audit gh-timeline schema", "check for new timeline events", "GitHub schema changed". Walks through schema diff → patch → test update → live verification → optional fixture refresh, marking each spot where a human decision is required.
The English summary is being prepared. Showing the original description.
Review the instructions the agent will receive before you install.
このリポジトリ (gh-timeline) は GitHub の GraphQL union を 2 つ網羅する:
PullRequestTimelineItems (PR timeline 用)IssueTimelineItems (Issue timeline 用)GitHub は schema を不定期に更新する。新規 event 追加・既存 field の変更・deprecate が起きると、本リポジトリの宣言と乖離して dispatcher fallback に落ちたり、 そもそも query が schema error で落ちる可能性がある。
このスキルは その差分検出 → 修正 → 検証 までを段階的に案内する。
以下のいずれかが当てはまれば本スキルを invoke する:
coverage_test.go の TestDispatchCoverage_* が failgh timeline を叩いたら未知の __typename が頻発[Step 1: Schema diff]
↓
[Step 2: Triage] ← 人間判断ポイント
↓
[Step 3: Patch code]
↓
[Step 4: Update tests]
↓
[Step 5: Live verification] ← 人間判断ポイント
↓
[Step 6: (任意) Fixture refresh] ← PLAN.md へ委譲
⚠️ マーク = 人間の判断や手動操作が必要な箇所。
GitHub の現行 union member を introspection で取得し、本リポジトリの宣言と比較する。
# 作業ディレクトリへ移動
cd "$(git rev-parse --show-toplevel)"
# 1.1 GitHub schema 側の union member
gh api graphql -f query='
query {
pr: __type(name: "PullRequestTimelineItems") { possibleTypes { name } }
issue: __type(name: "IssueTimelineItems") { possibleTypes { name } }
}' --jq '.data.pr.possibleTypes[].name' | sort -u > /tmp/pr_upstream.txt
gh api graphql -f query='
query { issue: __type(name: "IssueTimelineItems") { possibleTypes { name } } }
' --jq '.data.issue.possibleTypes[].name' | sort -u > /tmp/issue_upstream.txt
# 1.2 本 repo 側の宣言 (graphql tag を抽出)
grep -hE '`graphql:"\.\.\. on [A-Za-z0-9]+"`' internal/timeline/query.go \
| grep -oE 'on [A-Za-z0-9]+' | sed 's/^on //' | sort -u > /tmp/repo_declared.txt
# 1.3 PR union の差分 — 本 repo は両 union 分を 1 ファイルに宣言しているので
# pr_upstream に対してのみ厳密チェック (issue union は subset)
echo "== upstream PR にあって repo に無い (新規対応が必要) =="
comm -23 /tmp/pr_upstream.txt /tmp/repo_declared.txt
echo "== repo に宣言があるが upstream PR に無い (deprecated 検証) =="
comm -13 /tmp/pr_upstream.txt /tmp/repo_declared.txt | grep -v -E '^(Issue|PullRequest)$'
echo "== issue union の追加分 (PR 共通でない場合) =="
comm -23 /tmp/issue_upstream.txt /tmp/pr_upstream.txt
Step 1 の結果を 4 つに分類する。
| 差分カテゴリ | 内容 | 対処 |
|---|---|---|
| (A) 新規追加 | upstream にあって repo にない typename | Step 3 で fragment + dispatch case + handler を追加 |
| (B) 削除 (deprecated) | repo にあって upstream にない typename | Step 3 で fragment / case / handler を撤去。Projects classic 系は EOL 後しばらく schema には残ることがある — GitHub Docs で確認 |
| (C) フィールド変更 | typename は同じだが field が変わった | introspection だけだと検出不可。GitHub Docs / Changelog で event 型の field 一覧を目視確認 |
| (D) 命名変更 | A と B の両方に同時出現 | 既存 fragment / case を rename で対応 |
⚠️ 人間判断が要るところ:
新規 event <FooEvent> 1 つにつき以下を実施:
internal/timeline/nodes.go)actor + createdAt しか使わないなら commonEvent を embed:
type fooEventFragment struct {
commonEvent
SomeSpecificField githubv4.String
}
actor / createdAt が独自フィールド (例: submittedAt) なら個別宣言:
type fooEventFragment struct {
ID githubv4.ID
Author actorFragment
SubmittedAt githubv4.DateTime
}
internal/timeline/query.go)prTimelineNode に追加:
FooEvent fooEventFragment `graphql:"... on FooEvent"`
PR / Issue 両 union のメンバなら issueTimelineNode にも同じ tag で追加。
internal/timeline/dispatch.go)dispatchPRNode (および該当すれば dispatchIssueNode) の switch に case 追加:
case "FooEvent":
return handleFooEvent(t, n.FooEvent)
internal/timeline/dispatch.go)既存 handler の summary は 動詞句 + 対象フィールド の形に統一されている
(added label bug, assigned bea, merged deadbee into main 等)。フィールドが
空でも動詞句は残すことで、最低限の意味を保つ。新規 handler もこの流儀に揃える:
func handleFooEvent(typename string, f fooEventFragment) Event {
verb := "did foo to" // ⚠️ 文言は人間判断
summary := verb
if target := string(f.SomeSpecificField); target != "" {
summary = fmt.Sprintf("%s %s", verb, target)
}
return Event{
Type: typename,
Actor: string(f.Actor.Login),
Timestamp: f.CreatedAt.Time,
Summary: summary,
Ref: Ref{NodeID: graphqlIDString(f.ID)},
}
}
ペアになる add/remove 型 event (Labeled/Unlabeled, Assigned/Unassigned,
Milestoned/Demilestoned, ReviewRequested/ReviewRequestRemoved 等) は 同じ
handler を共有して typename で動詞を切り替える のが既存流儀:
verb := "added label"
if typename == "UnlabeledEvent" {
verb = "removed label"
}
⚠️ 人間判断:
added label か labeled か など)(column %q) のような
括弧書き、(%s) の duration 等。handleConnected / handleProjectChange /
handleUserBlocked が実例)shortSHA() を通すこと (handleMerged 参照)B カテゴリの event は上記 4 つを削除。fragment 型は他の event と共有していないかを git grep で確認してから削除する。
internal/timeline/coverage_test.go は reflection で struct tag を列挙するため、
Step 3.2 で query.go に追加した時点で対応する sub-test が自動で生まれる。
mise run test -- -run TestDispatchCoverage
# 期待: TestDispatchCoverage_prTimelineNode/<NewType> が新規に PASS
internal/timeline/dispatch_test.go の table-driven テストに代表的なケースを追加。
name は 何を assert しているかが伝わる文 にする (例: "FooEvent uses the
foo'd verb with target name") — assert behavior, not implementation の原則:
{
name: "FooEvent uses the foo'd verb with target name",
node: func() prTimelineNode {
n := prTimelineNode{Typename: "FooEvent"}
n.FooEvent.Actor.Login = "alice"
n.FooEvent.CreatedAt = dt(ts)
n.FooEvent.SomeSpecificField = "bar"
return n
}(),
wantType: "FooEvent",
wantActor: "alice",
wantSummary: "foo'd bar",
},
ペア event を追加した場合は、両方の typename と 特定フィールドが空 の fallback も別ケースとしてカバーする。
mise run fmt
mise run lint
mise run test
3 つ全部 0 issue / pass で次へ。
実 PR / Issue で発火するかを確認:
# 該当 event が出る PR が分かっていれば
go run . --repo OWNER/REPO PR_NUMBER --json | jq -r '.[] | select(.type=="FooEvent")'
# 出る PR が思い当たらない場合 → GitHub search で探す
gh search prs "<キーワード>" --json url,number --limit 5
⚠️ 人間判断:
.timeline-fixtures/PLAN.md 末尾の「取得不能 7 種」表に追記本 repo の reference fixture (sushichan044/gh-timeline の PR-A / PR-B / Issue-A / Issue-B)
を新 event に対応させたい場合、.timeline-fixtures/PLAN.md の該当 Phase に
操作を追加してから再実行する。
判断基準:
スキル完了時、以下が全て満たされること:
[ ] Step 1 の schema diff で repo と upstream が乖離なし
[ ] Step 3 の patch がコミット済み (conventional commit: feat/fix/refactor)
[ ] mise run fmt / lint / test が全て 0 issue / pass
[ ] 新 event について Step 5 で Live 観測 or Step 6 の fixture 拡張 or
取得不能と判定して PLAN.md に追記
[ ] (該当すれば) .timeline-fixtures/PLAN.md を最新化
[ ] commit message に「新 event X 件、削除 Y 件」のような差分サマリを記載
Issue / PullRequest のような top-level union 名 (timelineQuery 自体の
... on PullRequest 等) は本スキルの差分対象外。grep のフィルタで除外するdatabaseId (Int) が int32 overflow することがある (comment_id 4G 超え)。
fragment では int64 で受ける (既存 issueCommentFragment / pullRequestReviewFragment 参照)gh timeline 自体が schema 変更で動かなくなった場合 (= まず query が
schema error で落ちる) は本スキルではなく直接 internal/timeline/query.go を
最小修正してから本スキルを起動する.timeline-fixtures/PLAN.mdinternal/timeline/dispatch.gointernal/timeline/query.gointernal/timeline/nodes.gointernal/timeline/coverage_test.gointernal/timeline/dispatch_test.goNo reviews yet. Tell others what you thought.
No login needed. Share how it worked for you, ask a question, or note where it did not run.
No comments yet. Be the first to write one.
Use this to view the timeline of GitHub issues or pull requests. Note that it does not fetch the title and description, unlike `gh pr view`.
The English summary is being prepared. Showing the original description.