throughline 0.6.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +101 -1
- package/README.md +86 -28
- package/bin/throughline.mjs +20 -0
- package/docs/00_overview.md +12 -0
- package/docs/02_clear_auto_handoff_plan.md +47 -13
- package/docs/04_public_release_plan.md +1 -0
- package/docs/14_observer_completed_turn_feed_plan.md +290 -0
- package/docs/BUGHUB_RUNTIME_ERROR_STORE_PLAN.md +9 -3
- package/docs/adr/0002-observer-claude-completion-receipt.md +42 -0
- package/docs/adr/0003-observer-completed-chain-cursor.md +34 -0
- package/docs/adr/0004-observer-db-pair-projection.md +71 -0
- package/docs/adr/0005-observer-read-pagination.md +51 -0
- package/docs/adr/0006-observer-page-offset-proof.md +33 -0
- package/docs/adr/0007-observer-read-cli-contract.md +61 -0
- package/docs/adr/0008-observer-wait-deadline-cancel.md +81 -0
- package/docs/adr/0009-observer-integration-regression-and-docs.md +37 -0
- package/docs/adr/0010-observer-o1-phase-acceptance.md +49 -0
- package/docs/adr/0011-observer-o1-control-lane-reconciliation.md +34 -0
- package/docs/adr/0012-claude-stop-transcript-flush-barrier.md +32 -0
- package/docs/adr/0013-observer-read-busy-writer-gate.md +46 -0
- package/docs/adr/0014-two-phase-handoff-ghost-baton.md +112 -0
- package/docs/adr/0015-l1-summarizer-model-effort-ratio.md +81 -0
- package/docs/adr/0016-push-pull-recall-injection.md +93 -0
- package/package.json +1 -1
- package/rag/01-hooks/hook-stdout-10k-persisted-output.md +65 -0
- package/rag/INDEX.md +4 -0
- package/src/auditor-context.mjs +92 -11
- package/src/auditor-context.test.mjs +116 -1
- package/src/baton.mjs +27 -7
- package/src/baton.test.mjs +44 -0
- package/src/body-digest.mjs +9 -0
- package/src/cli/auditor-context.test.mjs +1 -1
- package/src/cli/factory-diagnostics.mjs +1 -0
- package/src/cli/factory-diagnostics.test.mjs +6 -2
- package/src/cli/observer-read.mjs +73 -0
- package/src/cli/observer-read.test.mjs +93 -0
- package/src/cli/observer-wait.mjs +123 -0
- package/src/cli/observer-wait.test.mjs +167 -0
- package/src/cli/recall.mjs +279 -0
- package/src/cli/recall.test.mjs +269 -0
- package/src/codex-rollout-memory.mjs +13 -0
- package/src/codex-rollout-memory.test.mjs +27 -0
- package/src/codex-thread-index.mjs +1 -1
- package/src/codex-thread-index.test.mjs +18 -0
- package/src/completed-turn-receipts.mjs +374 -0
- package/src/completed-turn-receipts.test.mjs +186 -0
- package/src/db-schema.test.mjs +9 -2
- package/src/db.mjs +20 -1
- package/src/decision-log.mjs +24 -0
- package/src/haiku-summarizer.mjs +93 -16
- package/src/haiku-summarizer.test.mjs +118 -9
- package/src/handoff-executor.mjs +161 -0
- package/src/hook-entrypoints.test.mjs +192 -12
- package/src/observer-codex-projection.test.mjs +49 -0
- package/src/observer-turn-feed.mjs +392 -0
- package/src/observer-turn-feed.test.mjs +339 -0
- package/src/observer-turn-wait.mjs +102 -0
- package/src/observer-turn-wait.test.mjs +122 -0
- package/src/pending-handoff.mjs +96 -0
- package/src/pending-handoff.test.mjs +107 -0
- package/src/prompt-submit.mjs +46 -1
- package/src/resume-context.mjs +337 -60
- package/src/resume-context.test.mjs +228 -1
- package/src/runtime-error-store.mjs +2 -1
- package/src/runtime-error-store.test.mjs +1 -1
- package/src/session-start.mjs +70 -233
- package/src/transcript-reader.mjs +32 -0
- package/src/turn-backfill.mjs +3 -2
- package/src/turn-backfill.test.mjs +10 -4
- package/src/turn-processor.mjs +90 -1
- package/src/turn-processor.test.mjs +142 -0
- package/src/windows-acl-test-helper.mjs +2 -2
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# Observer向けClaude/Codex completed-turn feed実装計画
|
|
2
|
+
|
|
3
|
+
**Status:** Active
|
|
4
|
+
|
|
5
|
+
**作成日:** 2026-07-14
|
|
6
|
+
|
|
7
|
+
**依頼元:** Observer v1
|
|
8
|
+
|
|
9
|
+
この文書は、指定projectで最後に完了したClaude/Codex親threadとhostを解決し、完了turnの差分readと最大一時間のwaitをJSON-only CLIとして提供するThroughline側の正本計画/TODOである。
|
|
10
|
+
|
|
11
|
+
## 目的
|
|
12
|
+
|
|
13
|
+
Observerはproject絶対pathだけを指定し、親threadの作り直しとClaude/Codex間のhost切替へ追従する。Throughlineはhostごとに実証された完了証拠を使い、進行中turnを除外したhost-bound opaque cursor、read、waitを所有する。Codexはrolloutの`task_complete`を使い、ClaudeはObserver側Phase 0と共同で実hostから確定した証拠だけを採用する。
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
Observer MCP adapter
|
|
17
|
+
└─ Throughline JSON CLIへwaitを要求
|
|
18
|
+
└─ host固有の完了証拠で閉じたcursorだけを監視
|
|
19
|
+
├─ changed / timeout / resync_required
|
|
20
|
+
└─ readでsnapshot / delta / thread_switchedを取得
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
MCP serverはObserverが所有する。Throughlineは既存の外部依存ゼロ構成を維持し、library + CLI契約だけを追加する。
|
|
24
|
+
|
|
25
|
+
## 非目標
|
|
26
|
+
|
|
27
|
+
- Throughline DB / hook / schemaの意味変更
|
|
28
|
+
- Observer state、Mailbox、dedupeの保存
|
|
29
|
+
- AI監査、助言生成、親への配送
|
|
30
|
+
- Throughline本体へのMCP SDKまたはMCP server追加
|
|
31
|
+
- DB / WAL / file mtimeを完了通知の正本にすること
|
|
32
|
+
- 同一projectで複数親が同時活動する場合の競合解決
|
|
33
|
+
- 既存Claude-facing hook、handoff、monitor挙動の互換破壊
|
|
34
|
+
- Codexの`task_complete`をClaudeへ推測適用すること
|
|
35
|
+
|
|
36
|
+
## 現行根拠
|
|
37
|
+
|
|
38
|
+
- `src/codex-rollout-memory.mjs`は`task_complete`を解釈し、`includeInFlightTurn:false`で進行中turnを除外できる。
|
|
39
|
+
- `src/codex-thread-index.mjs`はproject配下のrollout候補を解決できるが、現在の並び順はmtimeであり、feedの親選択には使わない。
|
|
40
|
+
- `src/auditor-context.mjs`はread-only DB、schema v8、project、origin session、user / assistant hashによるfreshness照合を持つ。
|
|
41
|
+
- Claude transcript/sessionと既存Stop hookはfirst-classのまま維持し、Observer用の「完了turn」境界は[ADR 0002](adr/0002-observer-claude-completion-receipt.md)のThroughline所有Stop receiptへ固定した。
|
|
42
|
+
- 現行DB projectionは進行中turnを含みうるため、`sessions.updated_at`、body ID、body件数をcursorにできない。
|
|
43
|
+
|
|
44
|
+
## 公開CLI
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
throughline observer-read \
|
|
48
|
+
--project /absolute/project \
|
|
49
|
+
[--after-cursor <opaque>] \
|
|
50
|
+
[--through-cursor <opaque>] \
|
|
51
|
+
[--page-token <opaque>] \
|
|
52
|
+
[--limit 10] \
|
|
53
|
+
--json
|
|
54
|
+
|
|
55
|
+
throughline observer-wait \
|
|
56
|
+
--project /absolute/project \
|
|
57
|
+
--after-cursor <opaque> \
|
|
58
|
+
[--timeout-seconds 3600] \
|
|
59
|
+
--json
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- stdoutは成功・既知状態とも単一JSONだけを返す。
|
|
63
|
+
- diagnosticsはstderrへ出す。
|
|
64
|
+
- 引数、permission、schema、I/Oのhard failureは固定code付きerror JSONとnon-zero exitにする。
|
|
65
|
+
- projectは既存directoryの絶対pathだけを受理し、realpathでcanonicalizeする。
|
|
66
|
+
- ObserverはCLIを子processとして呼び、DBやrolloutを直接読まない。
|
|
67
|
+
|
|
68
|
+
## Cursor契約
|
|
69
|
+
|
|
70
|
+
- schemaは`throughline.observer_cursor.v1`。
|
|
71
|
+
- tokenはopaqueかつboundedで、本文、path、secretを含めない。
|
|
72
|
+
- project identity、host identity、選択thread、host固有の完了pair prefixをThroughlineだけが検証できる情報を持つ。
|
|
73
|
+
- DB row ID、turn ordinal、mtime、`sessions.updated_at`を安定identityとして使わない。
|
|
74
|
+
- Observerは保存、比較、返送だけを行い、decode、採番、改変しない。
|
|
75
|
+
- project不一致、version不一致、同一threadのprefix不一致、rollbackは`resync_required`とする。
|
|
76
|
+
- 完了turnがまだ無いprojectにもempty baseline cursorを返し、最初の`task_complete`を待てるようにする。
|
|
77
|
+
|
|
78
|
+
## 最新親host/threadの解決
|
|
79
|
+
|
|
80
|
+
1. projectと同じ、または配下cwdを持つClaude sessionとCodex rollout候補を列挙する。
|
|
81
|
+
2. 各候補をhost固有のcompleted-only parserで解析する。
|
|
82
|
+
3. Codexは`task_complete`、ClaudeはPhase 0で実証した完了証拠で閉じたuser / assistant pairだけを候補にする。
|
|
83
|
+
4. 最後の完了時刻が最新のhost/threadを現在親として選ぶ。
|
|
84
|
+
5. 同一host内の同時刻はthread ID、source pathの順で決定的に解決する。異なるhostが一意に解決できない場合は`ambiguous_parent`としてfail closedにする。
|
|
85
|
+
|
|
86
|
+
rollout mtime、session index更新時刻、DB更新時刻だけで親を選ばない。v1は同一projectの活動親が一つという前提を明示する。
|
|
87
|
+
|
|
88
|
+
## Wait wire
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"schema": "throughline.observer_wait.v1",
|
|
93
|
+
"status": "changed",
|
|
94
|
+
"afterCursor": "tlc1:...",
|
|
95
|
+
"throughCursor": "tlc1:..."
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`status`:
|
|
100
|
+
|
|
101
|
+
- `changed`: 呼出時点または待機中にcompleted cursorが変化した。
|
|
102
|
+
- `timeout`: deadlineまで変化なし。返すcursorは入力と同値。
|
|
103
|
+
- `resync_required`: cursorがproject、version、rollback、prefix検証に失敗した。
|
|
104
|
+
- `ambiguous_parent`: Claude/Codexの現在親を一意に解決できない。推測でcursorを進めない。
|
|
105
|
+
|
|
106
|
+
実装は呼出直後に再計算し、以後は短いintervalで再確認する。DB transactionを待機中に保持しない。timeout境界で最後に一度再確認し、missed wakeupを防ぐ。AbortSignal / SIGINT / SIGTERMではtimerとfile handleを閉じ、成功JSONを偽装しない。
|
|
107
|
+
|
|
108
|
+
## Read wire
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"schema": "throughline.observer_read.v1",
|
|
113
|
+
"status": "delta",
|
|
114
|
+
"host": "codex",
|
|
115
|
+
"thread_sha256": "5a8f...",
|
|
116
|
+
"afterCursor": "tlc1:...",
|
|
117
|
+
"throughCursor": "tlc1:...",
|
|
118
|
+
"turns": [],
|
|
119
|
+
"page": {
|
|
120
|
+
"complete": true,
|
|
121
|
+
"nextToken": null
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`status`:
|
|
127
|
+
|
|
128
|
+
- `snapshot`: 初回orientation。最新のbounded履歴を返し、過去の省略は`historyTruncated`で明示する。
|
|
129
|
+
- `delta`: 同一threadの未読完了turn。
|
|
130
|
+
- `thread_switched`: 最新親が別threadへ移った。新threadの完了turnを先頭からpage化する。
|
|
131
|
+
- `host_switched`: 最新親がClaude/Codex間で切り替わった。新host/threadの完了turnを先頭からpage化する。
|
|
132
|
+
- `resync_required`: cursor / prefixを信頼できない。本文を通常deltaとして返さない。
|
|
133
|
+
- `projection_pending`: rolloutは完了済みだがDB freshness照合が未成立。Observerはcursorを進めず再試行する。
|
|
134
|
+
|
|
135
|
+
`throughCursor`は一回のread seriesの上限を固定する。途中で新turnが完了しても現在pageへ混ぜず、次のwaitで回収する。`page.nextToken`はafter / through / projectへ束縛したopaque tokenとする。snapshot以外の上限超過はpaginationし、黙ってdropまたは成功truncateしない。
|
|
136
|
+
|
|
137
|
+
turn本文は既存auditor projectionと同様に件数、各body、総文字数をboundedにし、各turnへcontent digestと`truncated`を付ける。truncationがある時、Observerは証拠不足の断定をしてはならない。
|
|
138
|
+
|
|
139
|
+
## Freshnessと競合
|
|
140
|
+
|
|
141
|
+
- host固有のcompleted-only pair chainをcursor正本にする。
|
|
142
|
+
- DB本文は`auditor-context`相当のproject / session / user hash / assistant hash照合を通す。
|
|
143
|
+
- Stop hook同士は並行しうるため、DBに本文があってもhost固有の最終完了証拠まではchangedにしない。
|
|
144
|
+
- waitがchangedを返した直後にDBがfreshでなければ、readは`projection_pending`を返す。
|
|
145
|
+
- Observerは全pageのreadと監査が成功した後だけ保存cursorを進める。
|
|
146
|
+
- crash、cancel、read失敗では旧cursorを維持する。
|
|
147
|
+
|
|
148
|
+
## 実装TODO
|
|
149
|
+
|
|
150
|
+
### Phase 0: Characterization
|
|
151
|
+
|
|
152
|
+
- [x] 進行中turnを含むDBとcompleted-only rolloutの差をfixture testで固定する。
|
|
153
|
+
- [x] Stop continuation後、最終`task_complete`までcursorが進まないtestを置く。
|
|
154
|
+
- 同一fixtureでDBにuser/assistant pairが先行してもcompleted chainが空のままであること、既存完了turn後の
|
|
155
|
+
Stop continuation本文が最終`task_complete`まで`unchanged`となり、完了後だけ`append`されることを固定した。
|
|
156
|
+
関連gateは9/9 green、Control `observer-feed-20260715` revision 50で親受入済み。
|
|
157
|
+
- [x] rollbackでordinalが変わってもpair hash prefixで検出できるtestを置く。
|
|
158
|
+
- [x] project配下cwdと別projectの候補分離を固定する。
|
|
159
|
+
- [x] Claude transcript、Throughline DB projection、Stop hookの順序を実hostで観測し、完了turnを進行中turnから分ける正式証拠を裁定する。
|
|
160
|
+
- Claude Code 2.1.207/Haiku 4.5/plan権限で、final assistant後にStop hooksが走ることを実hostで確認した。final assistant、process exit、mtimeを証拠にせず、Throughline Stop hookがpair capture成功後に書く製品所有receiptを採用する。正本は[ADR 0002](adr/0002-observer-claude-completion-receipt.md)。
|
|
161
|
+
- [x] Claudeのthread identity、project解決、continuation後の完了境界、再起動/resumeをfixtureと実測で固定する。
|
|
162
|
+
- headless `result/end_turn`と同じ`session_id`の`--resume`、`SessionStart:resume`を確認した。backgroundは`--print`と両立せず、`claude --bg '<task>'`がjob handleを返す。`agents --json`、`logs`、`stop`で`busy/working → idle/done → stop`を回収できた。
|
|
163
|
+
- [x] ClaudeとCodexの候補が同じprojectにある場合のhost switchと曖昧性を固定する。
|
|
164
|
+
|
|
165
|
+
### Phase 1: Core projection
|
|
166
|
+
|
|
167
|
+
- [x] `src/observer-turn-feed.mjs`へproject resolver、completed chain、opaque cursorを実装する。
|
|
168
|
+
- commit `def92f4`。Codexは自身の`task_complete`時刻だけを採用し、Claude receiptと共通のhash-only chainへ投影する。prior sourceのrollback/消失をswitchより先に検証する。
|
|
169
|
+
- [x] Claude Stop hookからbounded private completion receiptをatomic publishし、project digest、session、pair digest、sequenceを固定する。receipt失敗時はcursorを進めない。
|
|
170
|
+
- project digestごとのprivate storeへ分離し、256件の履歴上限と`history_floor`をproject単位で保持する。同一origin/pairのStop再実行はsession mergeでtargetが変わっても再採番せず、DB capture成功後・L1/L3前に同期publishする。
|
|
171
|
+
- [x] host adapter境界を設け、Claude既存parserとCodex rollout parserのcompleted chainを共通projectionへ変換する。
|
|
172
|
+
- [x] empty baseline、same-thread append、thread switch、host switch、ambiguous parent、rollback、version mismatchを実装する。
|
|
173
|
+
- [x] DB freshness照合を既存`auditor-context` helperと共有し、重複した別仕様を作らない。
|
|
174
|
+
- commit `022c0b8`。completed chainのorigin/user/assistant SHA-256をDBのcompleted pairへ順序付きで
|
|
175
|
+
全件照合し、一件でも不足・不一致なら本文ゼロの`projection_pending`とする。schema/project/I/Oは
|
|
176
|
+
hard failure、本文上限でもturn recordとdigestを保持し、raw session identityをpublic resultへ出さない。
|
|
177
|
+
- [x] Codex feedの`origin_sha256`をDB captureの`codex:<thread_id>` identityへ揃え、実DB fixtureで
|
|
178
|
+
`projection_pending`へ固定されないことを回帰化する。
|
|
179
|
+
- [x] bounded snapshot、delta pagination、page token検証を実装する。
|
|
180
|
+
- commit `7b07425`。初回through cursorで固定したlogical seriesだけをpage化し、project/exact
|
|
181
|
+
after/exact through/offset prefix digestへtokenを束縛した。snapshotは最新limit件で完了し、
|
|
182
|
+
deltaは途中の新規turnを混ぜず全件継続する。DB不足は本文・next token・through cursorを返さない。
|
|
183
|
+
関連gateは36/36 green、Control `observer-feed-20260715` revision 26でimmutable ADR 0006へfinalizeした。
|
|
184
|
+
|
|
185
|
+
### Phase 2: CLI read / wait
|
|
186
|
+
|
|
187
|
+
- [x] `observer-read`と厳格なJSON schema / exit契約を実装する。
|
|
188
|
+
- commit `e3380fa`。公開入口を`throughline observer-read --project <absolute> --json`へ固定し、
|
|
189
|
+
重複・未知引数を拒否する。既知のread状態はstdout JSON/exit 0、hard failureは固定codeの
|
|
190
|
+
stderr JSON/exit 1とし、内部path・本文・cursor・hashをerrorへ漏らさない。実bin dispatchを含む
|
|
191
|
+
関連gateは14/14 green、Control `observer-feed-20260715` revision 33でimmutable ADR 0007へfinalizeした。
|
|
192
|
+
- [x] `observer-wait`の即時changed、待機changed、timeout、deadline再確認を実装する。
|
|
193
|
+
- core commit `60c4036`。monotonic deadline、各wakeでtimeout判定より先の再計算、deadline上の
|
|
194
|
+
最終poll、abort競合、timer/listener cleanupを実装した。関連gateは15/15 green。
|
|
195
|
+
- [x] SIGINT / SIGTERM / parent disconnectで待機を明示cancelする。
|
|
196
|
+
- CLI commit `1165efd`。IPC disconnectに加え、起動時ppidの変化と`kill(pid, 0)`のESRCHをcancelへ
|
|
197
|
+
写し、EPERMは親不在扱いしない。signal listenerと親watch timerをfinallyで解放する。
|
|
198
|
+
- [x] CLI help、bin dispatch、import-safe testを追加する。
|
|
199
|
+
- 公開4状態をstdout JSON/exit 0、hard failureを固定codeのstderr JSON/exit 1へ固定した。
|
|
200
|
+
実bin dispatchを含む関連gateは12/12 green、Control `observer-feed-20260715` revision 43で
|
|
201
|
+
immutable ADR 0008へfinalizeした。
|
|
202
|
+
- [x] 65秒超live waitと3600秒設定をblack-boxで確認する。
|
|
203
|
+
- 隔離HOMEの実`throughline observer-wait`へ`--timeout-seconds 3600`を渡し、65.1秒後に
|
|
204
|
+
Claude completed receiptを投入した。65,142msで`changed`、stderr空、exit 0を一回で確認した。
|
|
205
|
+
|
|
206
|
+
### Phase 3: Integration
|
|
207
|
+
|
|
208
|
+
- [x] Observer fixtureからCLIだけを使うblack-box contract testを通す。
|
|
209
|
+
- Observer commit `45d1458`のintegration testから実`bin/throughline.mjs`だけを起動し、isolated環境で
|
|
210
|
+
empty snapshot、待機中changed、timeout、呼出前completionの即時changed、`projection_pending`を確認した。
|
|
211
|
+
実行時間2.27秒、1/1 green。Observer Control revision 49で親受入済み。
|
|
212
|
+
- [x] Claude/CodexそれぞれのObserver fixtureで65秒超live waitと3600秒設定を確認する。
|
|
213
|
+
- Observer commit `dc31c08`で、別projectのClaude receipt/Codex `task_complete`を使う2本のwaitを
|
|
214
|
+
並行起動し、一度の65.1秒待機後に両方が`changed`となるblack-box fixtureを追加した。
|
|
215
|
+
両CLI argvの`--timeout-seconds 3600`も固定した。focused gateは2/2 green、68,441.638ms。
|
|
216
|
+
- 不変の受入証拠はObserver `docs/adr/0045-o1-dual-host-live-wait-acceptance.md`とControl
|
|
217
|
+
`observer-o1-live-fixture-adr-20260715` revision 3。可変planを参照した旧receiptは使わない。
|
|
218
|
+
- [x] Claude hook、Codex capture、auditor-context、token monitorの回帰を通す。
|
|
219
|
+
- 関連6 test fileを一度だけ実行し、130/130 green、失敗・skip・cancel・todo各0、807.298ms。
|
|
220
|
+
受入は[ADR 0009](adr/0009-observer-integration-regression-and-docs.md)へ固定した。
|
|
221
|
+
- [x] README、CLAUDE.md、docs overview、CHANGELOGを実装済み挙動に同期する。
|
|
222
|
+
- commit `fb558d7`。JSON-only CLI、opaque cursor、read/wait状態、3600秒上限、host固有completed証拠、
|
|
223
|
+
DB/WAL/rollout直接監視へfallbackしない境界を同期した。Control `observer-feed-20260715` revision 60で
|
|
224
|
+
ADR 0009へfinalize済み。
|
|
225
|
+
- [x] `npm test`、`npm pack --dry-run --json`、`git diff --check`をgreenにする。
|
|
226
|
+
- fullは監査前HEAD `c5d6f2d`で661件中660件成功、失敗0、Windows限定1件skip。
|
|
227
|
+
監査後deltaはfocused 28/28で検証した。修正後packはentryCount 190、`git diff --check`も成功。
|
|
228
|
+
- [x] 独立監査でin-flight混入、thread誤選択、cursor欠落、cancel leak、privacyを反証する。
|
|
229
|
+
- 監査時点の製品判定はFAILEDとしてControl revision 67でrejectし、P1/P2を採用した。
|
|
230
|
+
- [x] P1: 複数turn backfillで全completed pairをClaude receiptへ時系列publishする。
|
|
231
|
+
commit `02a809f`、focused gate 28/28成功。
|
|
232
|
+
- [x] P2: Codex project resolverでPOSIX pathのcaseを保持する。
|
|
233
|
+
commit `88fafaf`、focused gate 28/28成功。
|
|
234
|
+
- Phase受入Decisionは[ADR 0010](adr/0010-observer-o1-phase-acceptance.md)。
|
|
235
|
+
- 監査修正Control `observer-feed-o1-audit-fixes-20260715`はbehavior-change revision 15、
|
|
236
|
+
元Control `observer-feed-20260715`はclosure revision 78でfinalizeした。lane境界は
|
|
237
|
+
[ADR 0011](adr/0011-observer-o1-control-lane-reconciliation.md)。
|
|
238
|
+
|
|
239
|
+
### Phase 4: queue 19e live defect correction
|
|
240
|
+
|
|
241
|
+
- [x] Claude async Stop hookのtranscript flush raceを閉じる。
|
|
242
|
+
- queue 19eの実Claude turnはassistant `end_turn`、Stop hook 4本・error 0、candidate `process-turn`
|
|
243
|
+
state更新まで成立したが、実行時DBはuser/assistantとも0件、receiptも0件だった。同じtranscriptを
|
|
244
|
+
turn後に読むとlatest logical groupは1件なので、shape不一致でなくfinal assistant行の可視化前に
|
|
245
|
+
一回だけbackfillしたraceである。
|
|
246
|
+
- `last_assistant_message`は本文ソースにせず、latest user groupに対応するassistantがtranscriptへ
|
|
247
|
+
永続化されたことを確定するbounded barrierにだけ使う。古い同文answer、前turn、DB本文へfallbackしない。
|
|
248
|
+
- markerがあるStopでdeadlineまで一致しなければ`HOOK_PROCESS_TURN_FAILED`として明示失敗し、
|
|
249
|
+
completionなしへ丸めない。markerを持たない旧hostだけは既存one-shot parser契約を維持する。
|
|
250
|
+
- 正本Decisionは[ADR 0012](adr/0012-claude-stop-transcript-flush-barrier.md)。
|
|
251
|
+
- [x] delayed assistant append、古い同文answer+current user-only、deadline、通常同期flushをfocused testで固定する。
|
|
252
|
+
- `node --test src/turn-processor.test.mjs`: 14/14成功。
|
|
253
|
+
- `node --test --test-name-pattern='process-turn subprocess' src/hook-entrypoints.test.mjs`: 2/2成功。
|
|
254
|
+
- [x] hook/backfill/receiptのrelated gateを一度通し、独立commit後にObserver queue 19eへ戻る。
|
|
255
|
+
- related 7ファイルは78/78成功。修理はcommit `a46b915`として独立確定した。
|
|
256
|
+
- [x] Codex Stop captureと同時の`observer-read`が一時SQLite lockをhard failureにする競合を閉じる。
|
|
257
|
+
- queue 19e実Codexで、親2turn目の`task_complete`とfeed書込みは成功したが、Observer production
|
|
258
|
+
callerの同時`observer-read`が`E_THROUGHLINE_EXEC`で終了した。直後の同じ公開readはturn 2件を返し、
|
|
259
|
+
私有driverでも同じ書込み瞬間の単発nonzeroを再現した。
|
|
260
|
+
- completed projectionのread-only接続だけにbounded SQLite busy waitを設定する。lock解消後の同じ
|
|
261
|
+
snapshotを読むか、上限超過なら従来どおりDB I/O hard failureとする。stale本文、別DB、CLI再spawnへ
|
|
262
|
+
fallbackしない。正本Decisionは[ADR 0013](adr/0013-observer-read-busy-writer-gate.md)。
|
|
263
|
+
- [x] `readCompletedPairProjection`だけに1秒のSQLite busy timeoutを設定した。別processのexclusive
|
|
264
|
+
writerを200ms後に解放するfocused testは修正前15/16、修正後16/16。Observer read/wait、
|
|
265
|
+
auditor、receipt、Codex hook/captureのrelated gateは78/78、構文・新規ADR lint・diff checkはgreen。
|
|
266
|
+
- [x] 修理済みcandidateのqueue 19e実Codex r11で、親completed-turn 2件、同じObserver generationの
|
|
267
|
+
completed cycle 2件、初回cycle後65秒超継続、pending reservation/cycle/model operation残留なしを
|
|
268
|
+
確認した。caller SIGINTはcancelled/exit 130、managed app-serverと親app-serverはterminal、projectは
|
|
269
|
+
空のまま。raw thread/turn ID、prompt、model output、credentialは保存していない。
|
|
270
|
+
|
|
271
|
+
## 受け入れ条件
|
|
272
|
+
|
|
273
|
+
1. project絶対pathだけから最新の完了済みClaude/Codex親hostとthreadを解決する。
|
|
274
|
+
2. Codexの`task_complete`またはClaudeの実証済み完了証拠より前のturnでcursorが進まない。
|
|
275
|
+
3. 呼出前に増えたturnはwaitが即時changedで返す。
|
|
276
|
+
4. 待機中に増えたturnは一回のwaitを完了させる。
|
|
277
|
+
5. timeout境界のturnを取りこぼさない。
|
|
278
|
+
6. 親thread作り直しを`thread_switched`、Claude/Codex間の移動を`host_switched`として返す。
|
|
279
|
+
7. rollback / prefix不一致を`resync_required`として返す。
|
|
280
|
+
8. DB lagを`projection_pending`として返し、stale本文をfresh扱いしない。
|
|
281
|
+
9. pagination中の新turnを固定`throughCursor`へ混ぜない。
|
|
282
|
+
10. 上限超過を黙ってdropしない。
|
|
283
|
+
11. cancel後にprocess、timer、DB handleが残らない。
|
|
284
|
+
12. ThroughlineのDB schema、既存Claude path、既存Codex captureを互換破壊しない。
|
|
285
|
+
13. Observer以外の利用者にも再利用できるJSON-only read / wait CLIになる。
|
|
286
|
+
14. 異なるhostの現在親を一意に解決できない場合は`ambiguous_parent`でfail closedにする。
|
|
287
|
+
|
|
288
|
+
## Rollback
|
|
289
|
+
|
|
290
|
+
新しいmodule、CLI dispatch、tests、docsだけを独立してrevertできる単位にする。既存DB migrationやhook shapeへ依存させない。ObserverはThroughlineの最低対応versionをverifyし、旧versionでDB直接監視へfallbackせず明示的に起動拒否する。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# BugHub runtime error store plan
|
|
2
2
|
|
|
3
|
-
Status: v0.6.
|
|
3
|
+
Status: v0.6.3 published 2026-07-14
|
|
4
4
|
|
|
5
5
|
This plan is the implementation TODO for Throughline's product-owned, local
|
|
6
6
|
runtime error projection. It implements the cross-repository contract in
|
|
@@ -61,8 +61,11 @@ existing transcript, handoff, or SQLite memory contracts.
|
|
|
61
61
|
- [x] Keep the production 3-second ACL and 5-second observer bounds unchanged,
|
|
62
62
|
but serialize Windows CI test files so parallel PowerShell fixture load
|
|
63
63
|
cannot consume those bounds before the contract under test runs.
|
|
64
|
-
- [
|
|
65
|
-
|
|
64
|
+
- [x] Require the public Node 22/24 CI matrix before publish: CI
|
|
65
|
+
`29284655280` completed 9/9 green.
|
|
66
|
+
- [ ] Record focused Windows tests, complete-suite, pack-inspection, and
|
|
67
|
+
registry-derived smoke evidence in this plan. Those individual records
|
|
68
|
+
are not part of the published-release facts currently recorded here.
|
|
66
69
|
|
|
67
70
|
## Release wave
|
|
68
71
|
|
|
@@ -71,3 +74,6 @@ existing transcript, handoff, or SQLite memory contracts.
|
|
|
71
74
|
- [x] Push the release commit and require the public CI gate to pass (`e6ce6e3`, CI `29238704750`).
|
|
72
75
|
- [x] After the owner H gate, publish `throughline@0.6.2`, create tag/release, and verify npm `latest` plus a registry-derived isolated install. Global installation is deferred to the dotagents Mac rollout wave.
|
|
73
76
|
- [x] Confirm `throughline factory-diagnostics --json` and the runtime-error snapshot from the published package, then record the public SHA and results in the changelog and canonical docs.
|
|
77
|
+
- [x] Publish `throughline@0.6.3` after the owner H gate; create tag / GitHub
|
|
78
|
+
Release and record public CI `29284655280` (9/9 green) plus npm shasum
|
|
79
|
+
`4f3fcd2598a75f026358dae7f3eb3165242b580b` (2026-07-14).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# ADR 0002: Use Throughline-owned Claude Stop receipts for completed turns
|
|
2
|
+
|
|
3
|
+
Date: 2026-07-15
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Accepted
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
Observer needs a durable, completed-only feed for both Claude and Codex parents. Claude Code 2.1.207 was characterized on macOS with an authenticated Max account using Haiku 4.5 and plan permissions.
|
|
12
|
+
|
|
13
|
+
- Headless `claude -p --output-format stream-json --verbose` returned `type=result`, `subtype=success`, `stop_reason=end_turn`, and a stable `session_id`.
|
|
14
|
+
- `--resume <session_id>` reused the same session and emitted `SessionStart:resume`.
|
|
15
|
+
- Background mode rejects `--print`; the supported form is `claude --bg '<task>'`.
|
|
16
|
+
- A background session returned job handle `6fdf0944`, moved from `busy/working` to `idle/done`, remained discoverable through `claude agents --json --all`, exposed output through `claude logs`, and accepted `claude stop`.
|
|
17
|
+
- The background UI showed Stop hooks running after the final assistant message. A final transcript message or process exit alone is not a durable completed-turn boundary.
|
|
18
|
+
- Claude `/rewind` creates a forked conversation rather than destructively rolling back the current conversation. Throughline must not add a false same-session Claude rollback surface.
|
|
19
|
+
|
|
20
|
+
The observed identifiers above are smoke evidence only. Production cursors must not embed local paths, account identifiers, prompt text, or raw session logs.
|
|
21
|
+
|
|
22
|
+
## Decision
|
|
23
|
+
|
|
24
|
+
For Claude, Throughline's own Stop hook will write a bounded, private, atomic completion receipt after the completed user/assistant pair has been captured successfully. The receipt will bind at least:
|
|
25
|
+
|
|
26
|
+
- schema version and host `claude`;
|
|
27
|
+
- canonical project identity digest;
|
|
28
|
+
- Claude session identity;
|
|
29
|
+
- completed user/assistant content digests;
|
|
30
|
+
- a host-provided completion timestamp and monotonic receipt sequence.
|
|
31
|
+
|
|
32
|
+
The Observer feed will derive its Claude completed chain from these receipts and verify the corresponding read-only DB projection before returning bodies. Transcript mtime, session index order, DB `updated_at`, final assistant presence, and headless process exit are not completion proof.
|
|
33
|
+
|
|
34
|
+
Codex continues to use rollout `task_complete`. Claude headless Worker completion uses the first valid stream-json `type=result` event; OS process exit is cleanup state, not logical completion. Background Observer supervision uses the Claude job handle and `agents/logs/stop` lifecycle, not a guessed PID-only contract.
|
|
35
|
+
|
|
36
|
+
## Consequences
|
|
37
|
+
|
|
38
|
+
- Existing Claude hooks, transcript parsing, DB schema, `/tl`, and baton behavior remain first-class and compatible.
|
|
39
|
+
- Stop receipt write failure is explicit and cannot advance the completed cursor.
|
|
40
|
+
- A captured receipt with a stale or missing DB projection produces `projection_pending`; it does not expose stale bodies.
|
|
41
|
+
- `/rewind` and `/clear` continue through the existing new-session baton and SessionStart path. No Claude equivalent of Codex `thread/rollback + thread/inject_items` is invented.
|
|
42
|
+
- Long-running Claude subprocess callers treat stream-json `type=result` as logical completion and separately clean up or recover the process/session handle.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# ADR 0003: Bind Observer cursors to host-completed pair chains
|
|
2
|
+
|
|
3
|
+
Date: 2026-07-15
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Accepted
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
Observer must follow the latest completed Claude or Codex parent for one canonical project without treating an in-flight response, file mtime, DB update, or synthetic continuation as completion. Existing Codex parsing intentionally preserves assistant-only continuations for memory rendering, including rows that have no later `task_complete`. Claude completion is now represented by the project-owned receipts accepted in ADR 0002.
|
|
12
|
+
|
|
13
|
+
The Observer-facing cursor is a local continuation token, not an authorization credential. It must be opaque to consumers, contain no raw path or body, and be checked against the current completed source chain before any body is returned.
|
|
14
|
+
|
|
15
|
+
## Decision
|
|
16
|
+
|
|
17
|
+
Throughline will normalize both hosts into a completed-pair chain with these rules:
|
|
18
|
+
|
|
19
|
+
- Claude: read the private receipt file selected by canonical project SHA-256. Each retained receipt contributes one completed pair anchor. `history_floor` is part of verification; a cursor older than the retained floor requires resync.
|
|
20
|
+
- Codex: a parsed turn is completed only when its own `task_complete` event was observed. The parser will retain that event timestamp as `completedAt`. Open turns, pending messages, and synthetic assistant-only continuations have `completedAt = null` and never advance the Observer chain.
|
|
21
|
+
- Each chain entry binds host, hashed thread/session identity, origin identity hash, normalized user hash, normalized assistant hash, and host completion timestamp. Raw project paths and bodies are excluded from cursors.
|
|
22
|
+
- A cursor binds schema version, canonical project digest, host, hashed selected thread identity, retained chain length/floor, and a rolling SHA-256 prefix digest. Throughline validates all fields against the current source chain. Encoding is an implementation detail and consumers must only store and return the bounded token.
|
|
23
|
+
- A same-thread append is valid only when the prior chain is an exact prefix. A shorter chain, changed prior pair, cursor below `history_floor`, unknown version, or project mismatch returns `resync_required`.
|
|
24
|
+
- A different selected thread returns `thread_switched`; a different selected host returns `host_switched`. A new thread does not become current until it has a completed pair.
|
|
25
|
+
- Parent candidates are ordered by host-provided completion timestamp. Ties within one host are resolved deterministically by hashed thread identity and source identity. A top timestamp tie across Claude and Codex is `ambiguous_parent`; Throughline does not guess from mtime.
|
|
26
|
+
- DB bodies remain a read-only projection. A selected source pair whose project/session/pair hashes do not match DB returns `projection_pending` and does not advance the accepted cursor.
|
|
27
|
+
|
|
28
|
+
## Consequences
|
|
29
|
+
|
|
30
|
+
- Existing memory rendering may continue to include synthetic continuations; the Observer adapter filters by explicit `completedAt` rather than changing that behavior.
|
|
31
|
+
- Rollback and retention loss are visible state transitions, not silent truncation.
|
|
32
|
+
- Empty projects can produce an empty baseline cursor and later detect the first completed pair.
|
|
33
|
+
- Cursor integrity comes from revalidation against product-owned receipts or rollout events. The token is not presented as a security boundary or cross-user capability.
|
|
34
|
+
- Page tokens must additionally bind project, after-cursor, through-cursor, and offset so new completions cannot enter an existing page series.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# ADR 0004: Observer本文はcompleted chainとDB pairの全件照合後だけ返す
|
|
2
|
+
|
|
3
|
+
**状態:** Accepted
|
|
4
|
+
**日付:** 2026-07-15
|
|
5
|
+
**対象:** Throughline Observer completed-turn feed
|
|
6
|
+
|
|
7
|
+
## 文脈
|
|
8
|
+
|
|
9
|
+
Observerのcursor正本は、Claude Stop receiptまたはCodex `task_complete`で閉じたcompleted pair
|
|
10
|
+
chainである。一方、Throughline DBの`bodies`にはStop receipt publish前のpair、receipt publishに
|
|
11
|
+
失敗したpair、Codex capture後にまだ完了していないpairが存在し得る。DBの末尾N件や
|
|
12
|
+
`sessions.updated_at`をそのまま読むと、cursorに存在しない本文を完了済みとして渡してしまう。
|
|
13
|
+
|
|
14
|
+
既存`auditor-context`はread-only DB、schema version、project境界、origin session、user/assistant
|
|
15
|
+
hashのfreshness照合と本文上限を所有する。Observer用に別のDB解釈を作らず、この低層契約を
|
|
16
|
+
共有する必要がある。
|
|
17
|
+
|
|
18
|
+
## 決定
|
|
19
|
+
|
|
20
|
+
1. `auditor-context`の低層に、completed pair chainの期待値を順序どおり照合するread-only helperを置く。
|
|
21
|
+
2. 入力する期待値は`origin_sha256`、`user_sha256`、`assistant_sha256`だけとし、各値はSHA-256の
|
|
22
|
+
厳格なlowercase hexとする。DBの`origin_session_id`はhelper内部でhash化し、外へ返さない。
|
|
23
|
+
3. helperは指定`session_id`のDB bodiesを既存と同じ規則でuser/assistant pairへ組み立て、期待chainの
|
|
24
|
+
各要素を同じ順序で一対一に照合する。同じhash組が繰り返されても、一つのDB pairを二回使わない。
|
|
25
|
+
4. session不存在、DB schema不一致、project不一致、期待pairの欠落またはhash不一致は本文を部分返却
|
|
26
|
+
しない。Observer projectionはこれらを`projection_pending`またはhard failureへ明示写像し、cursorを
|
|
27
|
+
進めない。
|
|
28
|
+
5. 本文を返すのは、要求された期待pairが全件一致した場合だけとする。各turnへ元本文のcontent digestと
|
|
29
|
+
`truncated`を付ける。本文上限でturn identityを黙ってdropせず、本文が空まで切れた場合も
|
|
30
|
+
`truncated: true`のturn recordを残す。
|
|
31
|
+
6. 選択candidateはDB queryに必要なraw target session identityをmodule内部だけで保持してよい。
|
|
32
|
+
public result、cursor、page token、error、logへraw session ID、origin ID、project path、本文を複製しない。
|
|
33
|
+
7. Claudeのsession mergeはreceiptの`target_session_id`をDB `session_id`として使い、各pairは
|
|
34
|
+
`origin_session_id`のhashで照合する。Codexは`codex:<thread_id>`をDB `session_id`として使い、同じ
|
|
35
|
+
pair hash規則を使う。
|
|
36
|
+
8. DB schemaとwrite pathは変更しない。helperは既存DBをread-onlyで開き、transactionやhandleを
|
|
37
|
+
呼出し後に保持しない。
|
|
38
|
+
|
|
39
|
+
## 状態写像
|
|
40
|
+
|
|
41
|
+
| DB/helper結果 | Observer read |
|
|
42
|
+
|---|---|
|
|
43
|
+
| 全期待pair一致 | 本文projectionを返せる |
|
|
44
|
+
| DB/session/pairがまだ無い | `projection_pending`、turns空、cursor据置 |
|
|
45
|
+
| schema不一致 | 固定code付きhard failure |
|
|
46
|
+
| project不一致 | 固定code付きhard failure |
|
|
47
|
+
| I/O/query失敗 | 固定code付きhard failure |
|
|
48
|
+
|
|
49
|
+
`projection_pending`は成功したdeltaではない。Observerはretryできるが、through cursorを保存済みcursorへ
|
|
50
|
+
昇格させてはならない。
|
|
51
|
+
|
|
52
|
+
## 受入fixture
|
|
53
|
+
|
|
54
|
+
- completed chainが1件、DBが0件なら`projection_pending`で本文ゼロ。
|
|
55
|
+
- DBに進行中またはreceipt未成立pairが余分にあっても、期待chain外の本文を返さない。
|
|
56
|
+
- Claude merge targetと複数originを、origin hashとbody hashで順序付きに照合する。
|
|
57
|
+
- Codexのturn ordinalがrollbackで変わっても、origin/pair hashが一致すれば本文を解決する。
|
|
58
|
+
- userまたはassistant hashが一文字でも違えば部分本文を返さない。
|
|
59
|
+
- project不一致、schema不一致、DB不存在を区別する。
|
|
60
|
+
- 本文上限で全turn recordを保持し、digestと`truncated`を返す。
|
|
61
|
+
|
|
62
|
+
## 非目標
|
|
63
|
+
|
|
64
|
+
- page token、snapshot/delta pagination、`throughCursor`固定
|
|
65
|
+
- `observer-read`/`observer-wait` CLI dispatch
|
|
66
|
+
- DB migration、receipt retention変更、Observer repo変更
|
|
67
|
+
|
|
68
|
+
## rollback
|
|
69
|
+
|
|
70
|
+
新しいread-only helper、Observer projection接続、testsだけを独立revertする。既存auditor-context公開API、
|
|
71
|
+
DB schema、Claude/Codex captureのwrite pathは変更しない。
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# ADR 0005: Observer read seriesを固定through cursorとopaque page tokenへ束縛する
|
|
2
|
+
|
|
3
|
+
日付: 2026-07-15
|
|
4
|
+
|
|
5
|
+
## Context
|
|
6
|
+
|
|
7
|
+
Observerは監査対象の完了turnを複数pageで読む。一回のread series中にも親threadへ新しいturnが
|
|
8
|
+
追加され得るため、各pageでlatestを読み直すと途中から別の上限が混ざり、重複・欠落・未監査cursorの
|
|
9
|
+
保存が起きる。単純なoffsetだけのpage tokenは、別project、別after cursor、別through cursorへ
|
|
10
|
+
転用できる。
|
|
11
|
+
|
|
12
|
+
この裁定はControlのDecision証拠に使うため、追記可能な
|
|
13
|
+
`docs/14_observer_completed_turn_feed_plan.md`へ契約本文を追加せず、wave専用の不変ADRとして置く。
|
|
14
|
+
|
|
15
|
+
## Decision
|
|
16
|
+
|
|
17
|
+
1. Libraryは`readObserverTurnPage`を公開し、project、`afterCursor`、`throughCursor`、
|
|
18
|
+
`pageToken`、`limit`と既存host/DB read optionsを受ける。`limit`は1以上100以下、既定10とする。
|
|
19
|
+
2. 最初のpageで`throughCursor`が無ければ、その呼出時点のcompleted-only chain上限を一度だけ固定する。
|
|
20
|
+
続きのpageは同じ`afterCursor`と`throughCursor`を必須とし、途中で完了したturnを混ぜない。
|
|
21
|
+
3. page token schemaは`throughline.observer_page.v1`とし、canonical project SHA-256、
|
|
22
|
+
exact after cursorのSHA-256(nullも固定表現でhash)、exact through cursorのSHA-256、
|
|
23
|
+
0以上のsafe integer offsetを持つ。tokenはopaque、4 KiB以下、exact schemaで検証する。
|
|
24
|
+
project path、本文、raw session/thread/origin ID、cursor本文は埋め込まない。
|
|
25
|
+
4. tokenは`pageToken`単独では使えない。呼出側が返送したproject、after、throughを再hashして
|
|
26
|
+
tokenの束縛と完全一致させる。不一致、version違い、改変、範囲外offsetは`resync_required`へ
|
|
27
|
+
丸めずhard input errorとして拒否する。
|
|
28
|
+
5. `afterCursor`と`throughCursor`はそれぞれ自身が指すsource chainのprefixとして再検証する。
|
|
29
|
+
同一thread deltaでは`after.length..through.length`、host/thread switchでは新sourceの
|
|
30
|
+
`0..through.length`だけを対象にする。afterより短いthrough、source rollback、source消失、
|
|
31
|
+
project/version不一致は`resync_required`とし本文を返さない。
|
|
32
|
+
6. 初回snapshotは最新`limit`件だけを返し、より古い完了turnがあれば`historyTruncated=true`を
|
|
33
|
+
明示してseriesを完了する。snapshotの過去分にはpage tokenを発行しない。
|
|
34
|
+
7. delta / host switch / thread switchで対象がlimitを超える場合だけpaginationする。
|
|
35
|
+
`page.complete=false`では次offsetのtoken、最終pageでは`complete=true, nextToken=null`を返す。
|
|
36
|
+
empty deltaも完了pageとして表現する。
|
|
37
|
+
8. DB projectionが一件でも不足・不一致なら`projection_pending`、`turns=[]`、next tokenなしを返す。
|
|
38
|
+
部分pageを成功として返さず、Observerは保存済みafter cursorを進めない。schema/project/I/O異常は
|
|
39
|
+
hard failureのままにする。
|
|
40
|
+
9. read wireは`throughline.observer_read.v1`、hash-only thread identity、status、after/through cursor、
|
|
41
|
+
bounded turn records、`historyTruncated`、pageだけを返す。各turnは順序を維持し、本文がboundで
|
|
42
|
+
truncateされてもturn recordとcontent digestを落とさない。
|
|
43
|
+
10. Observerは全pageの処理と監査が成功した時だけ`throughCursor`を保存する。crash、cancel、
|
|
44
|
+
hard failure、`projection_pending`、`resync_required`では旧cursorを維持する。
|
|
45
|
+
|
|
46
|
+
## Consequences
|
|
47
|
+
|
|
48
|
+
- 新しい完了turnは進行中seriesへ入らず、次のwait/readで回収される。
|
|
49
|
+
- page tokenの別project/別seriesへの転用とoffset改変をfail closedにできる。
|
|
50
|
+
- snapshotはorientation用のbounded最新履歴、deltaは欠落のない全件paginationという役割に分かれる。
|
|
51
|
+
- DB本文が遅れてもcursor正本を偽装せず、部分監査済みの状態を成功扱いしない。
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# ADR 0006: Observer page tokenのoffsetをseries prefix digestで検証する
|
|
2
|
+
|
|
3
|
+
日付: 2026-07-15
|
|
4
|
+
|
|
5
|
+
置換対象: [ADR 0005](0005-observer-read-pagination.md) Decision 3・4のoffset検証
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
ADR 0005はpage tokenをproject、after cursor、through cursor、offsetへ束縛したが、offset自身を
|
|
10
|
+
固定seriesの内容へ束縛していない。tokenをdecodeできる呼出側がoffsetだけを別の範囲内整数へ変えると、
|
|
11
|
+
schemaとcursor hashは一致したままturnを飛ばせる。page tokenは認可credentialではないが、破損・
|
|
12
|
+
取り違え・誤実装をsilent skipとして受理してはならない。
|
|
13
|
+
|
|
14
|
+
ADR 0005はControl Decisionの不変証拠なので追記修正せず、本ADRで不足契約だけを置換する。
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
1. `throughline.observer_page.v1`へ`offset_prefix_sha256`を必須追加する。
|
|
19
|
+
2. digest入力は、after/throughから導いた固定logical seriesの先頭からoffset直前までの各turnについて、
|
|
20
|
+
host、hash-only thread identity、origin/user/assistant SHA-256、completion時刻、source SHA-256を
|
|
21
|
+
canonical順で結合したものとする。offset 0はempty prefixのSHA-256を使う。
|
|
22
|
+
3. page token decode後、project/after/through hash、offsetの範囲に加え、現在再構成した固定seriesの
|
|
23
|
+
prefix digestと`offset_prefix_sha256`を完全一致で検証する。一つでも違えばhard input errorとし、
|
|
24
|
+
別offsetへの推測や最寄り境界への補正をしない。
|
|
25
|
+
4. 次page tokenは、今回返したturn数を加算した次offsetと、その位置のprefix digestからだけ生成する。
|
|
26
|
+
5. tokenはlocal continuation tokenであり認可credentialではない。攻撃者に対するMACの代用とは扱わず、
|
|
27
|
+
信頼境界を越えて受ける将来transportでは、Observer側認可とtransport integrityを別に要求する。
|
|
28
|
+
|
|
29
|
+
## Consequences
|
|
30
|
+
|
|
31
|
+
- offsetだけの破損・取り違えでturnをsilent skipできない。
|
|
32
|
+
- rollbackや固定through chainの内容変化もprefix不一致として明示拒否される。
|
|
33
|
+
- cursor contractと同じcontent-addressed検証を再利用でき、秘密鍵や永続server stateを追加しない。
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# ADR 0007: observer-read CLIはJSON-only read境界と固定error codeを持つ
|
|
2
|
+
|
|
3
|
+
日付: 2026-07-15
|
|
4
|
+
|
|
5
|
+
## Context
|
|
6
|
+
|
|
7
|
+
ObserverはThroughlineの内部moduleやDBを直接読まず、公開CLIだけを子processとして呼ぶ。Libraryの
|
|
8
|
+
`readObserverTurnPage`は既知状態を値で返す一方、引数、page token、project、DB schema/project/I/Oの
|
|
9
|
+
hard failureは例外で拒否する。CLIが例外本文、path、session、cursor、本文をstderrへ出すとprivacy境界を
|
|
10
|
+
壊し、既知状態までnon-zeroへ丸めるとObserverがretry/resyncを正しく選べない。
|
|
11
|
+
|
|
12
|
+
この契約はControlのDecision証拠に使うため、追記可能な計画書ではなく不変ADRとして置く。
|
|
13
|
+
|
|
14
|
+
## Decision
|
|
15
|
+
|
|
16
|
+
1. 公開入口は次とし、`--project`と`--json`を必須にする。`--after-cursor`、`--through-cursor`、
|
|
17
|
+
`--page-token`、`--limit`は各0回または1回だけ受理し、未知option、重複、値欠落、余剰位置引数を拒否する。
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
throughline observer-read --project <absolute-existing-directory>
|
|
21
|
+
[--after-cursor <opaque>] [--through-cursor <opaque>]
|
|
22
|
+
[--page-token <opaque>] [--limit <1..100>] --json
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
2. `--page-token`は`--after-cursor`と`--through-cursor`の両方がある場合だけ受理する。cursor/tokenの
|
|
26
|
+
decode、project canonicalization、固定series検証はCLIへ複製せず`readObserverTurnPage`へ委ねる。
|
|
27
|
+
3. `snapshot`、`delta`、`thread_switched`、`host_switched`、`resync_required`、
|
|
28
|
+
`projection_pending`、`ambiguous_parent`はすべて既知状態である。stdoutへ
|
|
29
|
+
`throughline.observer_read.v1`を単一行JSONとして一度だけ出し、stderr空、exit 0とする。
|
|
30
|
+
4. hard failureはstdoutを空に保ち、stderrへ次の固定shapeを単一行JSONとして一度だけ出し、exit 1とする。
|
|
31
|
+
error messageへ例外本文、path、body、cursor/token、hash、session/thread/origin IDを転記しない。
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{"schema":"throughline.observer_read.v1","status":"error","code":"E_OBSERVER_READ_ARGS","message":"invalid observer-read arguments"}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
5. 固定codeは次へ限定する。
|
|
38
|
+
|
|
39
|
+
| 条件 | code | message |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| CLI構文、必須/重複option、limit形式 | `E_OBSERVER_READ_ARGS` | `invalid observer-read arguments` |
|
|
42
|
+
| project/page tokenのhard input拒否 | `E_OBSERVER_READ_INPUT` | `observer read input is invalid` |
|
|
43
|
+
| DB schema不一致 | `E_OBSERVER_READ_DB_SCHEMA` | `observer read database schema is unsupported` |
|
|
44
|
+
| DB project不一致 | `E_OBSERVER_READ_DB_PROJECT` | `observer read database project does not match` |
|
|
45
|
+
| DB open/query/I/O失敗 | `E_OBSERVER_READ_DB_IO` | `observer read database could not be read` |
|
|
46
|
+
| 上記以外の内部失敗 | `E_OBSERVER_READ_INTERNAL` | `observer read failed` |
|
|
47
|
+
|
|
48
|
+
6. 公開CLIへ`--db`、`--codex-home`、receipt store path、raw session指定を追加しない。製品標準pathと
|
|
49
|
+
host固有indexをlibrary既定で解決し、testはdependency injectionまたは隔離HOME/CODEX_HOMEを使う。
|
|
50
|
+
7. `bin/throughline.mjs`はsubcommandを明示dispatchし、返却exit codeだけを`process.exitCode`へ写す。
|
|
51
|
+
import時にCLIを自動実行せず、parserと`run`はunit test可能に保つ。
|
|
52
|
+
8. cursorのversion、project、prefix、rollback不一致はLibrary既定どおり`resync_required`の既知状態で
|
|
53
|
+
exit 0とする。page tokenのversion、binding、offset/prefix不一致だけはsilent skipを防ぐhard input
|
|
54
|
+
errorであり、`E_OBSERVER_READ_INPUT`へ写す。
|
|
55
|
+
|
|
56
|
+
## Consequences
|
|
57
|
+
|
|
58
|
+
- Observerはstdoutだけを成功/既知状態wireとしてparseでき、stderrは固定codeのhard failureだけになる。
|
|
59
|
+
- DB遅延やresyncをprocess failureと誤認せず、page token改変やDB破損を成功JSONへ丸めない。
|
|
60
|
+
- 内部例外や端末固有pathがadapter境界から漏れない。
|
|
61
|
+
- waitのdeadline、poll、signal/parent disconnect契約は別ADR/Taskで扱い、read CLIへ混ぜない。
|