Files
yoi/docs/report/2026-08-06-ticket-search-audit-visibility.md
T

9.3 KiB

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だけを返す場合があった。

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になる場合があった。

Ticket 00001KZ46DP6K state planning

body_max_bytesevent_limitを大きくしても常にfull projectionになるとは限らず、Workerは「eventがない」のか「表示が省略された」のかを区別できない。

Implementation reportがtyped eventではない

Implementation reportとcommit evidenceは通常のMarkdown commentとして保存されている。

## 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だけを返す。

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がない

TicketRelationRecordTicketRelationQueryはあるが、誤登録や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を受け取る。

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を使い、querystateslinked_ticket_idupdated_*、cursor paginationを提供する。domain固有filterとresult型は混ぜない。

横断検索toolは追加しない

同じqueryをQueryTicketQueryObjectiveへそれぞれ実行すれば十分である。異なる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.evidenceattentionはMarkdown headingをparseせず、このtyped authorityをqueryする。

retrievable item revisions

ShowTicketのoptional revision selectorまたはbounded version projectionでitem historyを取得できるようにする。専用tool追加は、同じprojectionではsize/authorityを分離できない場合だけ検討する。

TicketItemRevision {
  revision_id,
  title,
  body_digest,
  body or bounded diff,
  edited_at,
  source,
}

Implementation report/review/close resolutionから対象item revisionを参照する。

stable tool projection

QueryTicketShowTicketQueryObjectiveShowObjectiveは、同じparameterなら常に同じshapeのbounded JSONを返す。省略時は明示的なtruncatedreturnednext_cursorを返し、「entryなし」と「projection省略」を区別する。

write commandは副作用単位で明示する

relationのadd/remove、Queue、review、Close、item editなどはauthorization、precondition、idempotency、notification、compensation、audit/result型が異なるため、generic MutateTicketMutateObjectiveへ統合しない。明示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を限定できる。