222 lines
9.3 KiB
Markdown
222 lines
9.3 KiB
Markdown
# Ticket検索・close監査に必要な履歴visibilityが不足している
|
|
|
|
Tracking Ticket: `00001KZRNHB35`
|
|
|
|
## 発生日
|
|
|
|
2026-08-06
|
|
|
|
## 概要
|
|
|
|
active Ticketから「Implementation reportやapprove、commit evidenceはあるがcloseされていないTicket」を監査した際、model-facing Ticket toolだけでは候補抽出とscope履歴の照合が難しかった。
|
|
|
|
この不足により、Workerが複数Ticketの履歴を目視で突き合わせ、`00001KZ46DP6K`のImplementation reportを別のWorkdir delete要件と誤対応させた。最終的にはfull thread、current source、commit `e6a2da54`を再確認して訂正したが、Ticket bodyの誤編集と誤った監査commentを発生させた。
|
|
|
|
誤判定自体はWorkerの確認不足である。一方、現在のtool surfaceはこの種の事故を防ぐための検索・version history・typed evidence projectionを提供していない。
|
|
|
|
## 観測した障壁
|
|
|
|
### `TicketList`から候補を抽出できない
|
|
|
|
今回のmodel-facing結果では、`TicketList`が次のようなcount summaryだけを返す場合があった。
|
|
|
|
```text
|
|
Listed 23 ticket(s) for state active
|
|
```
|
|
|
|
個々のTicket id、title、state、updated_atを取得できないため、既知のIDを使って`TicketShow`を全件呼ぶ必要がある。pagination offsetやevent/evidence filterもない。
|
|
|
|
必要だった検索条件は以下。
|
|
|
|
- activeかつImplementation reportあり
|
|
- activeかつapproveあり
|
|
- activeかつcommit evidenceあり
|
|
- `done`だが未close
|
|
- Implementation report後にitem body/titleが変更された
|
|
- unresolved `request_changes`あり/なし
|
|
- commit hashまたは本文文字列を含むTicket
|
|
|
|
### `TicketShow`のprojectionが安定しない
|
|
|
|
同じ`TicketShow`でも、state一行だけになる場合と、body/thread/relationsを含むfull JSON projectionになる場合があった。
|
|
|
|
```text
|
|
Ticket 00001KZ46DP6K state planning
|
|
```
|
|
|
|
`body_max_bytes`と`event_limit`を大きくしても常にfull projectionになるとは限らず、Workerは「eventがない」のか「表示が省略された」のかを区別できない。
|
|
|
|
### Implementation reportがtyped eventではない
|
|
|
|
Implementation reportとcommit evidenceは通常のMarkdown commentとして保存されている。
|
|
|
|
```markdown
|
|
## Implementation report
|
|
...
|
|
commit `e6a2da54`
|
|
```
|
|
|
|
そのため、backendは以下を構造化して検索できない。
|
|
|
|
- implementation reportの存在
|
|
- repository id
|
|
- base/head commit
|
|
- test evidence
|
|
- dirty state
|
|
- assignment id
|
|
- report対象revision
|
|
|
|
Markdown headingや文言に依存した監査になる。
|
|
|
|
### item editのversion historyを復元できない
|
|
|
|
threadの`item_edit` eventは、変更fieldとreplacement countだけを返す。
|
|
|
|
```text
|
|
Ticket item updated: title, body. Body replacement applied to 10 occurrence(s).
|
|
```
|
|
|
|
編集前title/body、編集後snapshot、item revision idを取得できない。Implementation reportがどのitem revisionを対象にしたかも記録されない。
|
|
|
|
このため、「実装完了後にTicketが別scopeへrescopeされた」のか、「Implementation reportの読み違い」なのかをthreadだけから確実に判定できない。
|
|
|
|
### lifecycle監査queryがない
|
|
|
|
`TicketDoctor`はschema/consistency diagnosticsには有効だが、次のsemantic lifecycle driftを検出しない。
|
|
|
|
- Implementation report + approve + reachable commitがあるのにplanning/inprogress
|
|
- state `done`だが未close
|
|
- close後にunresolved request_changesが追加された
|
|
- Implementation report後のmaterial rescope
|
|
- report commitがcurrent repositoryで解決不能
|
|
- MR current revisionとreview対象revisionが不一致
|
|
|
|
### relation削除operationがない
|
|
|
|
`TicketRelationRecord`と`TicketRelationQuery`はあるが、誤登録やstale relationを削除するtyped operationがない。storage直接編集はauthority bypassになるため実施できない。
|
|
|
|
## 影響
|
|
|
|
- active Ticket全件へのN+1 `TicketShow`が必要になる。
|
|
- known IDを持たないWorkerは候補自体を列挙できない。
|
|
- Markdown commentの目視照合で別Ticketのevidenceを混同しやすい。
|
|
- rescope後のcurrent bodyだけを見て過去Implementation reportを誤評価する。
|
|
- close漏れ監査が高コストで、Ticket state driftが蓄積する。
|
|
- 今回は誤ったTicket body editと監査commentを追加し、後から訂正eventを積むことになった。
|
|
|
|
## 提案
|
|
|
|
### read toolを`Query` / `Show`へ整理する
|
|
|
|
既存のselection-only `TicketList`へ検索責務を積み増すのではなく、read surfaceを次の責務へ整理する。
|
|
|
|
- `QueryTicket`: Ticket workflowのselection、全文検索、structured filter、attention候補
|
|
- `ShowTicket`: 一件のcurrent item、version refs、bounded thread、resolution、artifacts
|
|
- `QueryObjective`: Objectiveのselection、全文検索、state/linked-Ticket filter
|
|
- `ShowObjective`: 一件のcurrent bodyとlink projection
|
|
|
|
`QueryTicket`は次のoptional queryを受け取る。
|
|
|
|
```text
|
|
QueryTicket {
|
|
query?: string,
|
|
states?: [...],
|
|
event_kinds?: [...],
|
|
evidence?: implementation_report | commit | approved,
|
|
review_status?: none | approved | request_changes | unresolved_changes,
|
|
attention?: done_not_closed
|
|
| implementation_report_not_closed
|
|
| report_after_rescope
|
|
| unresolved_review
|
|
| missing_commit
|
|
| blocked
|
|
| unblocked,
|
|
related_ticket_id?: TicketId,
|
|
relation_kind?: RelationKind,
|
|
linked_objective_id?: ObjectiveId,
|
|
updated_before?: timestamp,
|
|
updated_after?: timestamp,
|
|
limit,
|
|
cursor?,
|
|
}
|
|
```
|
|
|
|
`attention`は自動mutationを行わず、候補と根拠だけを返す。Worker/Userが`ShowTicket`、review、repository authorityを再読してclose判断する。
|
|
|
|
resultは常にbounded summaryを返す。
|
|
|
|
- Ticket id/title/state/readiness
|
|
- matched field/event kind
|
|
- bounded snippet
|
|
- event id/sequence
|
|
- updated_at
|
|
- unresolved blocker/review count
|
|
- `next_cursor` / `truncated`
|
|
|
|
`QueryObjective`も同じ共通query infrastructureを使い、`query`、`states`、`linked_ticket_id`、`updated_*`、cursor paginationを提供する。domain固有filterとresult型は混ぜない。
|
|
|
|
### 横断検索toolは追加しない
|
|
|
|
同じqueryを`QueryTicket`と`QueryObjective`へそれぞれ実行すれば十分である。異なるauthorityとresult型を`WorkspaceSearch`へ混ぜるとsurfaceとprojectionが増えるため、横断toolは追加しない。
|
|
|
|
### typed Implementation report
|
|
|
|
Markdown bodyに加えて、最低限次をtyped attributesとして保存する。
|
|
|
|
- `assignment_id`
|
|
- `repository_id`
|
|
- `base_commit`
|
|
- `head_commit`
|
|
- `merge_request_id` / `revision_id`
|
|
- validation evidence refs
|
|
- dirty/untracked state
|
|
- source Runtime/Worker identity
|
|
|
|
reportは作成時のTicket item revisionを参照する。`QueryTicket.evidence`と`attention`はMarkdown headingをparseせず、このtyped authorityをqueryする。
|
|
|
|
### retrievable item revisions
|
|
|
|
`ShowTicket`のoptional revision selectorまたはbounded version projectionでitem historyを取得できるようにする。専用tool追加は、同じprojectionではsize/authorityを分離できない場合だけ検討する。
|
|
|
|
```text
|
|
TicketItemRevision {
|
|
revision_id,
|
|
title,
|
|
body_digest,
|
|
body or bounded diff,
|
|
edited_at,
|
|
source,
|
|
}
|
|
```
|
|
|
|
Implementation report/review/close resolutionから対象item revisionを参照する。
|
|
|
|
### stable tool projection
|
|
|
|
`QueryTicket`、`ShowTicket`、`QueryObjective`、`ShowObjective`は、同じparameterなら常に同じshapeのbounded JSONを返す。省略時は明示的な`truncated`、`returned`、`next_cursor`を返し、「entryなし」と「projection省略」を区別する。
|
|
|
|
### write commandは副作用単位で明示する
|
|
|
|
relationのadd/remove、Queue、review、Close、item editなどはauthorization、precondition、idempotency、notification、compensation、audit/result型が異なるため、generic `MutateTicket`や`MutateObjective`へ統合しない。明示commandを維持し、model-facing tool数はprofile・role・Flow別catalog projectionで抑える。
|
|
|
|
Implementation reportはtyped Ticket thread eventとして扱い、item revision参照とbounded query結果から監査可能にする。
|
|
|
|
## 推奨順序
|
|
|
|
1. `QueryTicket`を追加し、全文検索・state・updated time・thread kind・resolution・artifact・relation filterとcursorを入れる
|
|
2. `ShowTicket`へitem/threadの安定したversion referenceを加える
|
|
3. `QueryObjective`を同じquery/cursor infrastructureで追加する
|
|
4. `ShowObjective`へlink projectionを加える
|
|
5. typed Implementation reportとprofile/role/Flow別catalog projectionを追加する
|
|
6. WebUIに検索・filter・pagination・implementation-report表示を追加する
|
|
|
|
## 期待する監査手順
|
|
|
|
1. `QueryTicket(states=active, attention=implementation_report_not_closed)`で候補抽出。
|
|
2. candidateごとに`ShowTicket`でcurrent item revision、report対象revision、latest reviewを取得。
|
|
3. typed commit/MR evidenceをrepository authorityで検証。
|
|
4. unresolved `request_changes`とdependencyを確認。
|
|
5. User/Orchestratorが明示的にcloseする。
|
|
|
|
この順序なら、全TicketのMarkdown threadを目視で横断せず、scopeの異なるevidenceを誤対応させずにclose漏れを監査できる。横断検索toolやgeneric mutation toolを増やさず、roleごとのcatalog projectionでsurfaceを限定できる。
|