throughline 0.7.0 → 0.8.1
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 +50 -1
- package/README.ja.md +74 -42
- package/README.md +40 -25
- package/bin/throughline.mjs +6 -0
- package/docs/02_clear_auto_handoff_plan.md +8 -0
- package/docs/adr/0016-push-pull-recall-injection.md +105 -0
- package/package.json +1 -1
- package/rag/01-hooks/hook-stdout-10k-persisted-output.md +17 -0
- package/src/cli/factory-diagnostics.test.mjs +10 -1
- package/src/cli/recall.mjs +279 -0
- package/src/cli/recall.test.mjs +269 -0
- package/src/completed-turn-receipts.mjs +2 -1
- package/src/factory-diagnostics.mjs +3 -1
- package/src/factory-diagnostics.test.mjs +17 -0
- package/src/handoff-executor.mjs +4 -2
- package/src/resume-context.mjs +195 -82
- package/src/resume-context.test.mjs +112 -18
- package/src/runtime-error-store.mjs +2 -1
- package/src/runtime-error-store.test.mjs +1 -1
- package/src/turn-processor.test.mjs +4 -3
- package/src/windows-acl-test-helper.mjs +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,52 @@ shipped to npm but were not individually tagged on GitHub.
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **Native factory diagnostics now report the database compatibility label from
|
|
16
|
+
the canonical schema version.** Throughline schema v9 previously emitted the
|
|
17
|
+
stale `throughline.database.v8` label alongside numeric versions `9`/`9`, so
|
|
18
|
+
exact factory reporters correctly classified the installation as
|
|
19
|
+
incompatible. The label is now derived from the DB migration version and a
|
|
20
|
+
regression test requires the label, actual version, and supported version to
|
|
21
|
+
stay aligned on future schema bumps.
|
|
22
|
+
|
|
23
|
+
## [0.8.0] — 2026-07-18
|
|
24
|
+
|
|
25
|
+
### Changed (breaking behavior)
|
|
26
|
+
|
|
27
|
+
- **Injection is now push/pull, and L1 is no longer injected (ADR 0016).** The
|
|
28
|
+
budgeted resume context (9,500 chars) is rebuilt as: header +
|
|
29
|
+
current-position anchor + an always-shown retrieval-guide section as the
|
|
30
|
+
fixed part, then the **entire remaining budget is filled with L2 turns in
|
|
31
|
+
full**, newest-first, turn-atomically (a user+assistant pair goes in whole
|
|
32
|
+
or not at all — no fixed N, no fragment packing). L1 summaries are no longer
|
|
33
|
+
injected; older memory is pulled on demand instead. The guide section bakes
|
|
34
|
+
in the exact session id, ISO-millisecond boundary (strict less-than) and
|
|
35
|
+
turn counts at injection time, so the pull side never recomputes the window.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- **`throughline recall --l2|--l1` (ADR 0016).** Read-only pull commands the
|
|
40
|
+
injected guide section points at. `recall --l2 --session <id> --before
|
|
41
|
+
<ISO ms> --last <N>` returns the N turns of full L2 bodies older than the
|
|
42
|
+
boundary, in the same line grammar as the injection (including L3 inline
|
|
43
|
+
suffixes). `recall --l1 ... --skip <N>` lists every turn older than the
|
|
44
|
+
`--l2` range with its L1 summary, honestly marking unsummarized turns
|
|
45
|
+
("全 M ターン / 要約済み K") and always pointing at `throughline detail
|
|
46
|
+
<time>` for full text. The DB is opened read-only; a missing DB is an
|
|
47
|
+
explicit error and is never created or migrated.
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- **Windows ACL scripts get a 15s timeout (was 3s).** On windows-latest CI
|
|
52
|
+
runners a cold PowerShell start was measured at 3.0–3.2s, so the 3s
|
|
53
|
+
`spawnSync` cap killed the ACL apply/verify scripts of the completed-turn
|
|
54
|
+
receipt store and the runtime error store and surfaced as a flaky
|
|
55
|
+
"Windows owner-only ACL verification failed" (2 consecutive runs, including
|
|
56
|
+
a docs-only commit). The explicit hard-failure contract is unchanged; only
|
|
57
|
+
the cap was raised.
|
|
58
|
+
|
|
13
59
|
## [0.7.0] — 2026-07-17
|
|
14
60
|
|
|
15
61
|
### Changed (breaking behavior)
|
|
@@ -1063,7 +1109,10 @@ two attempts, instrument first instead of patching again.
|
|
|
1063
1109
|
|
|
1064
1110
|
---
|
|
1065
1111
|
|
|
1066
|
-
[Unreleased]: https://github.com/kitepon-rgb/Throughline/compare/v0.
|
|
1112
|
+
[Unreleased]: https://github.com/kitepon-rgb/Throughline/compare/v0.8.0...HEAD
|
|
1113
|
+
[0.8.0]: https://github.com/kitepon-rgb/Throughline/compare/v0.7.0...v0.8.0
|
|
1114
|
+
[0.7.0]: https://github.com/kitepon-rgb/Throughline/compare/v0.6.3...v0.7.0
|
|
1115
|
+
[0.6.3]: https://github.com/kitepon-rgb/Throughline/compare/v0.6.2...v0.6.3
|
|
1067
1116
|
[0.6.2]: https://github.com/kitepon-rgb/Throughline/compare/v0.6.1...v0.6.2
|
|
1068
1117
|
[0.3.22]: https://github.com/kitepon-rgb/Throughline/releases/tag/v0.3.22
|
|
1069
1118
|
[0.3.21]: https://github.com/kitepon-rgb/Throughline/compare/v0.3.19...v0.3.21
|
package/README.ja.md
CHANGED
|
@@ -29,10 +29,14 @@ throughline install # hook / Codex skill / VS Code monitor task を登録
|
|
|
29
29
|
<details>
|
|
30
30
|
<summary><b>Codex も併用する場合</b> Codex hooks も登録される — クリックで詳細</summary>
|
|
31
31
|
|
|
32
|
-
Codex
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
global install は Codex の `UserPromptSubmit` / `PostToolUse` / `Stop` hook
|
|
33
|
+
(絶対 node パス登録)と `$throughline` skill も登録する。これらの hook は
|
|
34
|
+
rollout capture と monitor state 書き込みだけを行い、**使用量閾値での
|
|
35
|
+
`$throughline` 自動注入はしない**(token-monitor は表示専用)。bare
|
|
36
|
+
`$throughline` は app-server 経由で新規 Codex thread を開始し、Throughline DB
|
|
37
|
+
の handoff memory を developer item として注入する。current-thread rollback
|
|
38
|
+
診断が要る時だけ明示的に `trim --execute --host codex` を使う。既存の
|
|
39
|
+
非 Throughline Codex hook は保持される。
|
|
36
40
|
|
|
37
41
|
</details>
|
|
38
42
|
|
|
@@ -42,7 +46,7 @@ skill も登録する。75% 自動発火は token-monitor 依存ではなく、
|
|
|
42
46
|
|---|---|---|---|---|
|
|
43
47
|
| **何をする** | ツール I/O を SQLite に退避、本文は残す | ウィンドウを全消去 | ウィンドウ全体を LLM 要約 | 新旧で要約 |
|
|
44
48
|
| **圧縮の軸** | コンテンツの **種類** (テキスト vs ツール I/O) | 無し — 全消去 | **新旧** (一律) | **新旧** (一律) |
|
|
45
|
-
| **境界後に残る記憶** | ✅
|
|
49
|
+
| **境界後に残る記憶** | ✅ 直近ターン本文そのまま (予算内ターン原子詰め) + それ以前は `recall` で pull + L3 オンデマンド | ❌ ゼロ | △ 一個の要約 (情報欠落) | △ 要約 (情報欠落) |
|
|
46
50
|
| **ツール I/O の扱い** | L3 に退避、`/sc-detail HH:MM:SS` で取り戻せる | 消える | 要約に溶けて読めない | 要約に溶ける |
|
|
47
51
|
| **コーディング用途への適合** | 高 — ツール I/O こそ重い 80% | 低 — 文脈が切れる | 中 — ただし不可逆 | 中 |
|
|
48
52
|
| **誤継承リスク** | 低 (typed `/clear` / `/tl` が前任を指名) | n/a | n/a | 高 |
|
|
@@ -73,10 +77,10 @@ Throughline 無し (50 ターン、/clear なし):
|
|
|
73
77
|
≈ 125,000 tok 合計
|
|
74
78
|
|
|
75
79
|
Throughline 有り (50 ターン → /clear → 再開):
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
ツール I/O 0 tok
|
|
79
|
-
≈ 13,000 tok — 90% 軽量
|
|
80
|
+
再開注入 ~3,000 tok ▌ (直近 L2 ターン丸ごと、≤9,500 字)
|
|
81
|
+
それ以前の記憶 0〜10,000 tok (必要な時だけ recall --l2 / --l1 で pull)
|
|
82
|
+
ツール I/O 0 tok (SQLite 退避、detail でオンデマンド取得)
|
|
83
|
+
≈ 最大 13,000 tok — 90% 軽量
|
|
80
84
|
```
|
|
81
85
|
|
|
82
86
|
MemGPT や LangChain の SummaryBufferMemory が **新旧** で圧縮するのに対し、
|
|
@@ -94,7 +98,7 @@ Throughline は加えて、トランスクリプト JSONL から実測 API 使
|
|
|
94
98
|
|
|
95
99
|
---
|
|
96
100
|
|
|
97
|
-
## 3 層メモリーモデル (schema
|
|
101
|
+
## 3 層メモリーモデル (schema v9)
|
|
98
102
|
|
|
99
103
|
```mermaid
|
|
100
104
|
flowchart LR
|
|
@@ -102,10 +106,11 @@ flowchart LR
|
|
|
102
106
|
T --> H["Stop hook"]
|
|
103
107
|
H --> L2[("L2 · bodies<br/>本文そのまま")]
|
|
104
108
|
H --> L3[("L3 · details<br/>ツール I/O · 思考")]
|
|
105
|
-
H -. "非同期<br
|
|
109
|
+
H -. "非同期<br/>要約器" .-> L1[("L1 · skeletons<br/>一行要約")]
|
|
106
110
|
|
|
107
|
-
L2 -- "
|
|
108
|
-
|
|
111
|
+
L2 -- "直近ターン丸ごと<br/>(9,500 字予算)" --> S["次セッション<br/>初回プロンプト注入"]
|
|
112
|
+
L2 -. "窓の残り<br/>throughline recall --l2" .-> S
|
|
113
|
+
L1 -. "それ以前すべて<br/>throughline recall --l1" .-> S
|
|
109
114
|
L3 -. "オンデマンド · throughline detail" .-> S
|
|
110
115
|
|
|
111
116
|
classDef l1 fill:#3aa0ff,stroke:#1a1f2e,color:#fff
|
|
@@ -118,28 +123,35 @@ flowchart LR
|
|
|
118
123
|
|
|
119
124
|
| 層 | 名称 | 保存先 | 内容 | ターンあたりコスト |
|
|
120
125
|
| --- | --- | --- | --- | --- |
|
|
121
|
-
| **L1** | スケルトン |
|
|
122
|
-
| **L2** | ボディ |
|
|
126
|
+
| **L1** | スケルトン | `recall --l1` でオンデマンド pull | ターンの一行要約(既定 backend は Codex CLI `gpt-5.6-luna`、fallback は ADR 0015 参照) | 約 10 トークン |
|
|
127
|
+
| **L2** | ボディ | 予算内は注入、残りは `recall --l2` | ユーザー本文 + アシスタント返答そのまま | 自然なフルサイズ |
|
|
123
128
|
| **L3** | ディテール | SQLite のみ | ツール I/O、システムメッセージ、画像、**拡張思考** (オンデマンド) | 重い、退避済 |
|
|
124
129
|
|
|
125
130
|
3 層は **互いに補完的かつ排他的** で、重複保存はありません。
|
|
126
131
|
拡張思考ブロックは L3 (`kind='thinking'`) に格納されるので、次セッションは
|
|
127
132
|
**前セッションの Claude が中断時に何を考えていたか** を、発話だけでなく
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
-
|
|
133
|
+
内省レベルで参照できます。思考は注入されず、`throughline detail <時刻>` で
|
|
134
|
+
いつでも取得できます。
|
|
135
|
+
|
|
136
|
+
次セッションの **初回ユーザープロンプト** 時(二相ハンドオフ、ADR 0014)、
|
|
137
|
+
Throughline は SQLite からコンテキストを再構築し、プレーンテキストとして
|
|
138
|
+
注入します(push/pull 設計、ADR 0016):
|
|
139
|
+
|
|
140
|
+
- **直近ターン** を L2 (`bodies`) のフル本文として注入 — 新しい順に
|
|
141
|
+
ターン丸ごと(user + assistant 原子)、約 9,500 字の予算に入るだけ
|
|
142
|
+
- **L1 (`skeletons`) の一行要約は注入しません** — 代わりに、そのまま実行
|
|
143
|
+
できる `throughline recall --l2|--l1` コマンド入りの案内セクションを注入し、
|
|
144
|
+
古い記憶は必要になった時だけ pull します
|
|
136
145
|
- L3 は SQLite に残り、`/sc-detail <時刻>` でオンデマンド取得
|
|
137
146
|
|
|
138
147
|
L1 要約は遅延実行で、20 ターン未満で終わるセッションでは外部要約器を呼ばず、
|
|
139
|
-
|
|
140
|
-
`
|
|
141
|
-
Claude
|
|
142
|
-
|
|
148
|
+
短いタスクの要約コストはゼロです。要約は **削減割合**(既定 1/5、
|
|
149
|
+
`THROUGHLINE_L1_RATIO` で変更可。不正値は明示エラー)を目標にします。
|
|
150
|
+
Claude-primary 経路の backend 順は codex-sidecar(`summarize-l1` preset 明示
|
|
151
|
+
設定時)→ **Codex CLI**(既定 `gpt-5.6-luna`@`low`、実測評価で選定 — ADR 0015。
|
|
152
|
+
`THROUGHLINE_L1_MODEL` / `THROUGHLINE_L1_EFFORT` で変更可)→ **Claude Haiku 4.5**
|
|
153
|
+
(`claude -p` サブプロセス、Claude Max のログイン認証流用)で、API キーは
|
|
154
|
+
不要です。各段の失敗理由は記録されます。
|
|
143
155
|
|
|
144
156
|
3 層 (L1/L2/L3) の書き込みパスは schema v5 から動作しています。
|
|
145
157
|
`/sc-detail HH:MM:SS` はユーザー / アシスタント本文 (L2) と、そのターンで
|
|
@@ -158,13 +170,14 @@ flowchart LR
|
|
|
158
170
|
U["ユーザーが入力<br/>/clear または /tl"] -->|UserPromptSubmit| W["writeBaton<br/>(session_id + TTL 1h)"]
|
|
159
171
|
W --> B[("handoff_batons<br/>SQLite")]
|
|
160
172
|
M["VS Code メニュー<br/>clear"] -->|UserPromptSubmit に届かない| X["baton 無し"]
|
|
161
|
-
NS["次の SessionStart"] -->
|
|
173
|
+
NS["次の SessionStart<br/>(intent 登録のみ)"] --> FP["初回ユーザープロンプト<br/>(実セッションの証明)"]
|
|
174
|
+
FP --> C{"baton<br/>あり?"}
|
|
162
175
|
B -.-> C
|
|
163
176
|
X -.-> C
|
|
164
177
|
C -->|あり| P1["baton path<br/>(主経路)<br/>指名された前任を merge"]
|
|
165
|
-
C -->|無し / source='clear'| P2["auto path<br/>(補助)<br
|
|
178
|
+
C -->|無し / source='clear'| P2["auto path<br/>(補助)<br/>前任は SessionStart 時点で凍結"]
|
|
166
179
|
C -->|無し / source!='clear'| P3["新規セッション<br/>merge 無し"]
|
|
167
|
-
P1 --> INJ["
|
|
180
|
+
P1 --> INJ["L2 ターン + recall 案内を注入<br/>(予算 ≤ 9,500 字)"]
|
|
168
181
|
P2 --> INJ
|
|
169
182
|
|
|
170
183
|
classDef primary fill:#7c5cff,stroke:#1a1f2e,color:#fff
|
|
@@ -178,16 +191,26 @@ flowchart LR
|
|
|
178
191
|
### baton path (primary): typed `/clear` または `/tl`
|
|
179
192
|
|
|
180
193
|
ユーザーが prompt に `/clear` または `/tl` を打つと、UserPromptSubmit hook が
|
|
181
|
-
**そのセッションの** `session_id` を `handoff_batons`
|
|
182
|
-
|
|
194
|
+
**そのセッションの** `session_id` を `handoff_batons` に書きます。次の新セッション
|
|
195
|
+
は **初回ユーザープロンプト時** に baton を消費し(適格性: セッション誕生が baton
|
|
196
|
+
書き込みから TTL 1 時間以内)、その前任を確定的に merge します。
|
|
183
197
|
複数ウィンドウで「最新更新セッション」と「今 `/clear` したセッション」が違っても、
|
|
184
198
|
指名された前任だけを引き継ぎます。
|
|
185
199
|
|
|
200
|
+
なぜ SessionStart でなく初回プロンプトか: Claude Code は同一 project に数百 ms の
|
|
201
|
+
間隔で複数の SessionStart を発火させることがあり、その一部は transcript を一切
|
|
202
|
+
作らない**幽霊セッション**になります。SessionStart 時点では実体と幽霊を判別
|
|
203
|
+
できないため、baton を SessionStart で消費すると幽霊が記憶を飲み込み、実セッション
|
|
204
|
+
が空で始まる事故が起きます。幽霊はプロンプトを発火しないので、消費を初回
|
|
205
|
+
プロンプトへ遅延させればこの事故は構造的に起きません(二相ハンドオフ、ADR 0014)。
|
|
206
|
+
|
|
186
207
|
### auto path (fallback): `source='clear'`
|
|
187
208
|
|
|
188
209
|
baton が無く、SessionStart の `source='clear'` が届いた場合だけ、同 project の
|
|
189
|
-
最新 Claude predecessor
|
|
190
|
-
|
|
210
|
+
最新 Claude predecessor を **SessionStart 時点で** 解決・凍結し(transcript の
|
|
211
|
+
無い幽霊は候補から除外)、merge + 注入は初回プロンプト時に行います。これは
|
|
212
|
+
VS Code 拡張メニューなど、typed `/clear` が UserPromptSubmit hook に届かない
|
|
213
|
+
経路のための補助です。
|
|
191
214
|
|
|
192
215
|
`THROUGHLINE_DISABLE_AUTO_HANDOFF=1` はこの fallback path だけを OFF にします。
|
|
193
216
|
typed `/clear` と `/tl` はユーザーの明示意思なので、この env に関係なく baton を
|
|
@@ -201,11 +224,18 @@ fallback: baton 無し + source='clear' → latest predecessor を merge
|
|
|
201
224
|
|
|
202
225
|
### 注入されるもの
|
|
203
226
|
|
|
204
|
-
両経路で同じ curated memory
|
|
227
|
+
両経路で同じ curated memory が注入されます(push/pull 設計、ADR 0016):
|
|
205
228
|
|
|
206
|
-
-
|
|
207
|
-
|
|
208
|
-
-
|
|
229
|
+
- **「現在地」アンカー** (v0.4.12〜) — 最新のユーザー指示と最新の assistant turn を
|
|
230
|
+
ヘッダ直下に再掲(各 600 字で切り詰め)
|
|
231
|
+
- **pull 案内セクション**(無条件表示)— そのまま実行できる `throughline recall`
|
|
232
|
+
コマンド。session id・ISO ms 境界・件数は注入時に焼き込み済み
|
|
233
|
+
- L2 verbatim — 約 9,500 字の注入予算に入るだけ、新しい turn からターン丸ごと
|
|
234
|
+
詰める(典型 7〜8 turn、軽い会話ならもっと多い)。**L1 サマリーは注入しません**
|
|
235
|
+
— 20 turn 窓の残りは `recall --l2` で全文、それより古い全 turn は `recall --l1`
|
|
236
|
+
で取得(要約済みは L1 行、未要約は `throughline detail` への誘導付きで明示)
|
|
237
|
+
- L3 references (`throughline detail <時刻>` で引き出すコマンド、各 L2 行末尾に
|
|
238
|
+
inline 付記。本文は SQLite に残置)
|
|
209
239
|
|
|
210
240
|
注入は **「中断されたタスクの再開」** として再フレーミングされます。L2 verbatim に
|
|
211
241
|
最終 assistant turn (= 次に何をしようとしていたか) が含まれるため、別途 memo /
|
|
@@ -231,14 +261,14 @@ slash command / transcript / baton / resume behavior を置き換えるもので
|
|
|
231
261
|
adapter / projection として追加されます。
|
|
232
262
|
|
|
233
263
|
現時点で core Throughline が外部モデルを呼ぶのは L2→L1 要約だけです。
|
|
234
|
-
|
|
235
|
-
|
|
264
|
+
backend 順は codex-sidecar(`summarize-l1` preset 明示設定時)→ Codex CLI
|
|
265
|
+
(既定 `gpt-5.6-luna`)→ Claude Haiku です(ADR 0015)。
|
|
236
266
|
|
|
237
267
|
Codex 側 trim (= same-thread context trim) は `throughline trim --execute --host codex`
|
|
238
268
|
で発火します。Codex の bare `$throughline` skill もこの scripted rollback + DB
|
|
239
269
|
memory inject を直接実行します。Claude 側は `/clear` での auto path 引継ぎが本線になったため、
|
|
240
270
|
`/tl-trim` slash command は v0.4.0 で廃止されました。current-work framing は
|
|
241
|
-
|
|
271
|
+
再開注入の Reading Contract / Continuation Instruction で同じ意図を
|
|
242
272
|
継承しています。
|
|
243
273
|
|
|
244
274
|
</details>
|
|
@@ -285,6 +315,7 @@ Throughline state をまだ書いていない現在セッションも表示で
|
|
|
285
315
|
| `throughline monitor` | マルチセッション監視を起動 |
|
|
286
316
|
| `throughline monitor --diag` | TTY/columns/env 診断ダンプ (描画バグ切り分け用) |
|
|
287
317
|
| `throughline detail <時刻>` | あるターンの L2 本文と L3 ツール I/O を取得 (Claude が使う) |
|
|
318
|
+
| `throughline recall --l2\|--l1 --session <id> --before <ISO> ...` | 注入の案内セクションが指す古い記憶を pull (read-only、正確なコマンドは注入に焼き込み済み) |
|
|
288
319
|
| `throughline doctor` | Node バージョン、hook 登録状況、DB、PATH をチェック |
|
|
289
320
|
| `throughline doctor --trim --host claude` | trim boundary と手動手順を診断 |
|
|
290
321
|
| `throughline handoff-preview --session <id>` | Codex 向け `throughline_handoff` JSON projection を表示 |
|
|
@@ -313,7 +344,8 @@ Throughline state をまだ書いていない現在セッションも表示で
|
|
|
313
344
|
|
|
314
345
|
- **Node.js 22.5 以上** (組み込み `node:sqlite` モジュール使用、ネイティブビルド不要)
|
|
315
346
|
- **Claude Code** (`SessionStart`, `Stop`, `UserPromptSubmit` hooks 対応版)
|
|
316
|
-
- **
|
|
347
|
+
- **Codex CLI ログイン**(既定の L1 要約 backend、`gpt-5.6-luna`)または
|
|
348
|
+
**Claude Max サブスクリプション**(`claude -p` 経由の Haiku fallback)— どちらも API キー不要
|
|
317
349
|
- 対応 OS: **Windows / macOS / Linux**
|
|
318
350
|
|
|
319
351
|
ランタイム依存 **ゼロ**。npm パッケージは純 `.mjs` ファイルのみで構成されています。
|
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ guarded `trim --execute --host codex` surface.
|
|
|
54
54
|
|---|---|---|---|---|
|
|
55
55
|
| **What it does** | retire tool I/O to SQLite, keep text in-context | wipe the whole window | LLM-summarize the whole window | recency-based summarize |
|
|
56
56
|
| **Compression axis** | content **type** (text vs tool I/O) | none — full wipe | **recency** (uniform) | **recency** (uniform) |
|
|
57
|
-
| **Memory after the boundary** | ✅ recent
|
|
57
|
+
| **Memory after the boundary** | ✅ recent turns verbatim (whole, budget-packed) + everything older via `recall` pull + L3 on demand | ❌ zero | △ lossy single summary | △ lossy summary |
|
|
58
58
|
| **Tool I/O handling** | retired to L3, retrievable by `/sc-detail HH:MM:SS` | gone | folded into summary, unreadable | folded into summary |
|
|
59
59
|
| **Coding-assistant fit** | high — tool I/O is the heavy 80% | low — you lose the thread | medium — but irreversible | medium |
|
|
60
60
|
| **Auto-inheritance risk** | low (typed `/clear` / `/tl` names the predecessor) | n/a | n/a | high |
|
|
@@ -86,10 +86,10 @@ Without Throughline (50 turns, no /clear):
|
|
|
86
86
|
≈ 125,000 tok total
|
|
87
87
|
|
|
88
88
|
With Throughline (50 turns → /clear → resume):
|
|
89
|
-
recent
|
|
90
|
-
older
|
|
91
|
-
tool I/O 0 tok
|
|
92
|
-
≈ 13,000 tok
|
|
89
|
+
resume injection ~3,000 tok ▌ (recent L2 turns, whole, ≤ 9,500 chars)
|
|
90
|
+
older memory 0–10,000 tok (pulled only when needed: recall --l2 / --l1)
|
|
91
|
+
tool I/O 0 tok (retired to SQLite, on-demand via detail)
|
|
92
|
+
≈ 13,000 tok worst case — 90% lighter
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
Throughline separates conversation content by **type, not time**: human-readable
|
|
@@ -117,10 +117,11 @@ flowchart LR
|
|
|
117
117
|
T --> H["Stop hook"]
|
|
118
118
|
H --> L2[("L2 · bodies<br/>verbatim text")]
|
|
119
119
|
H --> L3[("L3 · details<br/>tool I/O · thinking")]
|
|
120
|
-
H -. "async<br/>
|
|
120
|
+
H -. "async<br/>summarizer" .-> L1[("L1 · skeletons<br/>one-liners")]
|
|
121
121
|
|
|
122
|
-
L2 -- "recent
|
|
123
|
-
|
|
122
|
+
L2 -- "recent turns, whole<br/>(9,500-char budget)" --> S["Next session's first prompt<br/>injection"]
|
|
123
|
+
L2 -. "rest of the window<br/>throughline recall --l2" .-> S
|
|
124
|
+
L1 -. "everything older<br/>throughline recall --l1" .-> S
|
|
124
125
|
L3 -. "on demand · throughline detail" .-> S
|
|
125
126
|
|
|
126
127
|
classDef l1 fill:#3aa0ff,stroke:#1a1f2e,color:#fff
|
|
@@ -133,22 +134,25 @@ flowchart LR
|
|
|
133
134
|
|
|
134
135
|
| Layer | Name | Where it lives | Content | Cost per turn |
|
|
135
136
|
| ----- | ---------- | --------------------- | --------------------------------------------------------------------- | ------------- |
|
|
136
|
-
| **L1** | Skeleton |
|
|
137
|
-
| **L2** | Body | injected
|
|
137
|
+
| **L1** | Skeleton | pulled on demand (`recall --l1`) | one-line summary of the turn (default backend: Codex CLI `gpt-5.6-luna`, fallbacks per ADR 0015) | ~10 tok |
|
|
138
|
+
| **L2** | Body | injected while the budget lasts; rest via `recall --l2` | user text + assistant reply, verbatim | full natural |
|
|
138
139
|
| **L3** | Detail | SQLite only | tool I/O, system messages, images, **extended thinking** (on-demand) | heavy, retired |
|
|
139
140
|
|
|
140
141
|
The layers are **complementary and disjoint** — nothing is duplicated across
|
|
141
142
|
them. Extended thinking blocks are stored at L3 (`kind='thinking'`) so the
|
|
142
143
|
next session can see *what the previous Claude was thinking* at the moment it
|
|
143
|
-
was interrupted, not just what it said aloud.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
- The **most recent
|
|
151
|
-
-
|
|
144
|
+
was interrupted, not just what it said aloud. Thinking is never injected —
|
|
145
|
+
it stays retrievable via `throughline detail <time>`.
|
|
146
|
+
|
|
147
|
+
At the **first user prompt** of the next session (two-phase handoff, ADR
|
|
148
|
+
0014), Throughline rebuilds the context from SQLite and injects it as plain
|
|
149
|
+
text (push/pull design, ADR 0016):
|
|
150
|
+
|
|
151
|
+
- The **most recent turns** are injected as full L2 (`bodies`) text — packed
|
|
152
|
+
whole-turn, newest-first, as many as fit the ~9,500-char budget
|
|
153
|
+
- **L1 (`skeletons`) one-liners are not injected** — a guidance section with
|
|
154
|
+
ready-to-run `throughline recall --l2|--l1` commands is injected instead,
|
|
155
|
+
so older memory is pulled only when needed
|
|
152
156
|
- L3 stays in SQLite and is retrieved on demand via `/sc-detail <time>`
|
|
153
157
|
|
|
154
158
|
L1 summaries are generated lazily: for sessions that stay under 20 turns, no
|
|
@@ -189,7 +193,7 @@ flowchart LR
|
|
|
189
193
|
C -->|yes| P1["baton path<br/>(primary)<br/>merge that exact predecessor"]
|
|
190
194
|
C -->|no, source='clear'| P2["auto path<br/>(fallback)<br/>predecessor frozen at SessionStart"]
|
|
191
195
|
C -->|no, source!='clear'| P3["fresh session<br/>no merge"]
|
|
192
|
-
P1 --> INJ["inject
|
|
196
|
+
P1 --> INJ["inject L2 turns + recall guidance<br/>(budgeted ≤ 9,500 chars)"]
|
|
193
197
|
P2 --> INJ
|
|
194
198
|
|
|
195
199
|
classDef primary fill:#7c5cff,stroke:#1a1f2e,color:#fff
|
|
@@ -245,15 +249,23 @@ this fallback. **The env var only affects the fallback**; typed `/clear` and
|
|
|
245
249
|
|
|
246
250
|
### What gets injected
|
|
247
251
|
|
|
248
|
-
Both paths inject the **same** curated memory:
|
|
252
|
+
Both paths inject the **same** curated memory (push/pull design, ADR 0016):
|
|
249
253
|
|
|
250
254
|
- A **"現在地 (latest exchange)"** anchor (added in v0.4.12) re-surfaces the
|
|
251
255
|
most recent user directive and the most recent assistant turn directly under
|
|
252
256
|
the header, each truncated to 600 characters
|
|
253
|
-
-
|
|
254
|
-
|
|
257
|
+
- A **pull guidance section** (always present) with ready-to-run
|
|
258
|
+
`throughline recall` commands — session id, an ISO-ms boundary, and turn
|
|
259
|
+
counts are baked in at injection time
|
|
260
|
+
- L2 verbatim: as many of the most recent turns as fit the ~9,500-char
|
|
261
|
+
injection budget, packed whole-turn (typically 7–8 turns; more for light
|
|
262
|
+
conversations). **L1 summaries are not injected** — the rest of the
|
|
263
|
+
20-turn window is retrieved verbatim via `throughline recall --l2`, and
|
|
264
|
+
everything older via `throughline recall --l1` (summarized turns show
|
|
265
|
+
their L1 line; unsummarized ones are listed explicitly with a
|
|
266
|
+
`throughline detail` pointer)
|
|
255
267
|
- L3 references (`throughline detail <time>` retrieval commands, attached
|
|
256
|
-
inline to each
|
|
268
|
+
inline to each L2 row; bodies stay in SQLite)
|
|
257
269
|
|
|
258
270
|
The injection is reframed as **"resuming an interrupted task"** rather than
|
|
259
271
|
"reading past logs". The L2 verbatim already contains the last assistant
|
|
@@ -754,6 +766,7 @@ aggregate は collection が既定OFFで、canonical dotagents config の
|
|
|
754
766
|
| `throughline monitor [--all] [--session <id>]` | Run the multi-session token monitor |
|
|
755
767
|
| `throughline monitor --diag` | Dump TTY/columns/env diagnostics (for debugging monitor render bugs) |
|
|
756
768
|
| `throughline detail <time>` | Retrieve L2 body text and L3 tool I/O for a turn (see below) |
|
|
769
|
+
| `throughline recall --l2\|--l1 --session <id> --before <ISO> ...` | Pull older memory referenced by the injection's guidance section (read-only; the exact command is baked into each injection) |
|
|
757
770
|
| `throughline observer-read --project <absolute-directory> --json` | Read one completed-turn Observer page through the JSON-only public boundary |
|
|
758
771
|
| `throughline observer-wait --project <absolute-directory> --after-cursor <opaque> [--timeout-seconds 3600] --json` | Wait up to 3600 seconds for a completed-turn Observer cursor change |
|
|
759
772
|
| `throughline doctor` | Check Node version, hook registration, DB writability, PATH |
|
|
@@ -886,7 +899,9 @@ throughline auditor-context --session claude-session-id --project "$PWD" \
|
|
|
886
899
|
- **Node.js >= 22.13** (for the stable, flag-free built-in `node:sqlite` module — no native build
|
|
887
900
|
required, no `npm install` of SQLite bindings)
|
|
888
901
|
- **Claude Code** with hooks support (`SessionStart`, `Stop`)
|
|
889
|
-
- **
|
|
902
|
+
- **Codex CLI login** (default L1 summarization backend, `gpt-5.6-luna`) or a
|
|
903
|
+
**Claude Max subscription** (Haiku fallback via `claude -p`) — no API key
|
|
904
|
+
either way
|
|
890
905
|
- Works on **Windows, macOS, Linux**
|
|
891
906
|
|
|
892
907
|
Throughline has **zero runtime dependencies**. The published tarball is just
|
package/bin/throughline.mjs
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* throughline process-turn # Stop hook (Claude Code から呼ばれる)
|
|
10
10
|
* throughline session-start # SessionStart hook (Claude Code から呼ばれる)
|
|
11
11
|
* throughline detail <時刻> # L2+L3 詳細取得 (Claude が Bash 経由で呼ぶ想定)
|
|
12
|
+
* throughline recall --l2|--l1 # 注入案内から辿る pull 用 read-only 記憶取得
|
|
12
13
|
* throughline handoff-preview # Codex-facing throughline_handoff JSON preview
|
|
13
14
|
* throughline auditor-context --json # Read-only bounded auditor context JSON
|
|
14
15
|
* throughline factory-diagnostics --json # Native factory read-only readiness JSON
|
|
@@ -66,6 +67,11 @@ switch (cmd) {
|
|
|
66
67
|
case 'detail':
|
|
67
68
|
(await import('../src/sc-detail.mjs')).run(rest);
|
|
68
69
|
break;
|
|
70
|
+
case 'recall': {
|
|
71
|
+
const exitCode = (await import('../src/cli/recall.mjs')).run(rest);
|
|
72
|
+
process.exitCode = exitCode;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
69
75
|
case 'handoff-preview':
|
|
70
76
|
await (await import('../src/cli/handoff-preview.mjs')).run(rest);
|
|
71
77
|
break;
|
|
@@ -17,6 +17,14 @@ A 案 (= /clear で自動引継ぎ + /tl は逃げ道として残す + /tl-trim
|
|
|
17
17
|
> `THROUGHLINE_DISABLE_AUTO_HANDOFF=1` は **fallback path のみに作用** する
|
|
18
18
|
> ようになった (typed `/clear` / `/tl` には効かない)。
|
|
19
19
|
|
|
20
|
+
> **2026-07-18 (ADR 0016) update**: 注入の中身を push/pull 二段に再設計した。
|
|
21
|
+
> push (9,500 字) はヘッダ + 現在地アンカー + 案内セクション + **L2 をターン原子で
|
|
22
|
+
> 入るだけ全文**(L1 は注入しない)。窓 20 ターンの残りは `throughline recall --l2`、
|
|
23
|
+
> それより古い全ターンは `recall --l1`(要約 or 未要約明示)で pull する。範囲・境界
|
|
24
|
+
> (ISO ms)・件数・session は注入時に案内コマンドへ焼き込み、recall 側は窓を再計算
|
|
25
|
+
> しない。正典は [ADR 0016](adr/0016-push-pull-recall-injection.md)。本書内の
|
|
26
|
+
> 「L1 + L2 を注入する」旧記述はこの update で読み替えること。
|
|
27
|
+
|
|
20
28
|
---
|
|
21
29
|
|
|
22
30
|
## 1. 確定した事実 (実機検証済み)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# ADR 0016: 注入の push/pull 再設計 — 現在地 + 入るだけ L2 の push と recall CLI による pull
|
|
2
|
+
|
|
3
|
+
- Status: accepted (2026-07-18 オーナー裁定)
|
|
4
|
+
- 関連: [ADR 0014](0014-two-phase-handoff-ghost-baton.md)(9,500 字注入予算)、
|
|
5
|
+
[ADR 0015](0015-l1-summarizer-model-effort-ratio.md)(L1 要約体制)
|
|
6
|
+
|
|
7
|
+
## 問題
|
|
8
|
+
|
|
9
|
+
9,500 字の注入予算(hook stdout ~10k で `<persisted-output>` file 化、ADR 0014)の中で、
|
|
10
|
+
旧 `buildBudgetedResumeContext` は **L1 を先に詰めてから L2 を詰めて**いた。実測
|
|
11
|
+
(このMacのDB、L2 1 ターン中央値 ~800 字・平均 ~2,750 字)では L2 は 5〜10 ターンしか
|
|
12
|
+
入らず、しかも L1 生成は `L2_WINDOW = 20` より古いターンにしか走らない(Stop 毎 1 件の
|
|
13
|
+
遅延生成)ため、「予算落ちした 5〜20 ターン前」が **L2 も注入されず L1 も未生成**の
|
|
14
|
+
記憶空白帯になっていた。省略告知は時刻列挙のみで、取り出しは 1 ターンずつ
|
|
15
|
+
`throughline detail` を叩くしかなかった。
|
|
16
|
+
|
|
17
|
+
## 検討して不採用にした案
|
|
18
|
+
|
|
19
|
+
1. **固定 N ターン + per-role 切り詰めで L2 を保証し、残りに L1 充填**:
|
|
20
|
+
L1 生成閾値を N に連動させる案。pull 設計に移った時点で、注入 L1 が pull の L2×残り
|
|
21
|
+
と同じターンを二重に運ぶ矛盾が出て破棄(会話での敵対的整理)。固定 N 自体も
|
|
22
|
+
「軽い会話で予算を設計的に遊ばせる」ため破棄。
|
|
23
|
+
2. **multi-hook による 10k 突破**: 実測で**可能**と確認した(下記「実測」)が、hook の
|
|
24
|
+
構造的想定(1 本 = 1 context string)に無い使い方であり、attachment 順序も非決定の
|
|
25
|
+
ため不採用。
|
|
26
|
+
3. **全文ログのファイル書き出し + Read 誘導**: DB が正本なのに派生ファイルの寿命管理が
|
|
27
|
+
発生する。過去ターンのレコードは不変なので、スナップショット性は DB 直参照でも
|
|
28
|
+
担保できる。CLI 経由の DB 直参照に統一して不採用。
|
|
29
|
+
4. **生 SQL をモデルに案内**: schema 講義で注入が太り、origin フィルタ等の誤クエリ事故面
|
|
30
|
+
が開く。`detail` と同じ「DB 直参照だがモデルにはコマンド 1 発」の流儀で不採用。
|
|
31
|
+
|
|
32
|
+
## 決定
|
|
33
|
+
|
|
34
|
+
push は「現在地」に徹し、過去は pull に出す。
|
|
35
|
+
|
|
36
|
+
- **push(注入、9,500 字内)**: ヘッダ + 現在地アンカー + 案内セクション
|
|
37
|
+
(固定部として最優先予約・無条件表示)+ 残り全予算に **L2 を新しい順で
|
|
38
|
+
丸ごと入るターンだけ全文**(ターン単位の原子。固定 N なし・断片詰めなし・
|
|
39
|
+
ターン境界の自然な端数は許容)。**L1 は注入しない**。
|
|
40
|
+
最新ターンが単体で予算超過する場合だけ切り詰めて入れる(従来規則の維持)。
|
|
41
|
+
- **pull(新 CLI `throughline recall` = read-only DB 直参照)**:
|
|
42
|
+
- `recall --l2 --session <id> --before <ISO8601 ms> --last <N>` —
|
|
43
|
+
境界より古いターンを新しい側から N 件、L2 全文(注入と同じ行文法 + L3 suffix)
|
|
44
|
+
- `recall --l1 --session <id> --before <ISO8601 ms> --skip <N>` —
|
|
45
|
+
--l2 の担当分を飛ばした先の全ターン一覧。**L1 要約があれば要約、無ければ
|
|
46
|
+
「未要約」と明示**して detail 誘導。冒頭に「全 M ターン / 要約済み K」を正直に表示
|
|
47
|
+
- 一点掘りは従来どおり `throughline detail <時刻>`(L2+L3)
|
|
48
|
+
- **`L2_WINDOW = 20` は据え置き**。20 の意味が「push(入るだけ)+ pull(残り)」の
|
|
49
|
+
合計窓に再定義されるだけで、L1 要約ペース・Codex 側・schema への変更なし。
|
|
50
|
+
|
|
51
|
+
### 間抜け防止 — 機械用境界の完全焼き込み(refuter 敵対的検証 2026-07-18)
|
|
52
|
+
|
|
53
|
+
実装前に refuter による敵対的検証を通し、real 指摘 6 件を全て設計へ反映した:
|
|
54
|
+
|
|
55
|
+
| 指摘 | 対処 |
|
|
56
|
+
|---|---|
|
|
57
|
+
| HH:MM:SS 境界は「当日」解決(sc-detail の `timeToUnixRange`)で深夜跨ぎに壊れる | `--before` は ISO 8601 完全日時(ms 精度)。表示用 HH:MM:SS と機械用境界を分離 |
|
|
58
|
+
| 秒切り捨てで同秒行の境界包含が未定義 | 境界は strict less-than の ms 比較で規定 |
|
|
59
|
+
| 20 ターン窓はクエリ時再計算のため、新セッションのターン追記で窓がスライドし古い側が黙って欠落 | `--last <残り件数>` も注入時に焼き込み、recall 側の窓再計算を全廃 |
|
|
60
|
+
| L1 は遅延生成でバックログがあり「全 N ターンの要約」が虚偽になる。未要約ターンがどの取っ手からも見えない | `--l1` は全ターン一覧(要約 or 未要約明示)。件数は「全 M / 要約済み K」形式 |
|
|
61
|
+
| 既定 session 解決(cwd 系)は Codex 併走・複数ウィンドウで非決定 | `--session` も焼き込み。recall は既定解決を持たない |
|
|
62
|
+
| `getDb()` は mkdir + read-write open + migration を行い read-only 契約に反する | recall は `DatabaseSync(path, { readOnly: true })` + 存在チェック。DB を作成しない |
|
|
63
|
+
|
|
64
|
+
併せて、予算詰めを行(role)単位からターン単位の原子に変更した(行単位だと同一ターンの
|
|
65
|
+
assistant 行だけ入り user 行が pull 側に半身で現れ、境界の算術が濁るため)。
|
|
66
|
+
|
|
67
|
+
## 実測
|
|
68
|
+
|
|
69
|
+
### 新設計のレンダリング(2026-07-18、実 DB 191 ターンセッション)
|
|
70
|
+
|
|
71
|
+
- totalChars 9,158 / 9,500、L2 9 ターン注入、残り 11 ターン、窓外 171 ターン
|
|
72
|
+
(要約済み 124 / 未要約 47)
|
|
73
|
+
- 焼き込まれた案内コマンドをそのまま実行して、`--l2` が境界ぴったりから 11 ターン
|
|
74
|
+
(33k 字)、`--l1` が全 171 ターン一覧(118k 字、`--last` で部分取得可)を返すことを確認
|
|
75
|
+
|
|
76
|
+
### 実機 E2E(2026-07-18、global 0.8.0 / baton path)
|
|
77
|
+
|
|
78
|
+
- `/tl` → 新セッションの初回プロンプトで新形式注入が発火(47 ターン引き継ぎ)。
|
|
79
|
+
注入は L1 非注入・現在地アンカー・L2 ターン原子詰め(11 ターン全文)・案内セクション
|
|
80
|
+
焼き込みで、設計どおりの形を目視確認
|
|
81
|
+
- 焼き込まれた `recall --l2 --session <id> --before <ISO ms> --last 11` を新セッション側から
|
|
82
|
+
そのまま実行し、境界より古い 11 ターン(11:02:27〜11:38:57)が古い順・全文・L3 suffix
|
|
83
|
+
付きで返り、注入分の最古ターン(11:48:57)と連続・重複欠落なしを確認
|
|
84
|
+
- 焼き込まれた `recall --l1 ... --skip 11` が窓外の全 9 ターンを「全 9 / 要約済み 7」の
|
|
85
|
+
正直表示で返し、未要約 2 件は `throughline detail <時刻>` への誘導付きで明示された
|
|
86
|
+
- これをもって v0.8.0 の残タスク(新注入形式の実機 E2E)は完了
|
|
87
|
+
|
|
88
|
+
### multi-hook 10k 突破の実測(2026-07-18、Claude Code 2.1.211 / 不採用)
|
|
89
|
+
|
|
90
|
+
- 同一 UserPromptSubmit に hook を 3 本登録 → それぞれ独立の `hook_success` attachment
|
|
91
|
+
になり 9,000 字 × 3 = 27,000 字が全部モデル可視 inline
|
|
92
|
+
- 1 本だけ 12k にすると**その 1 本だけ**が `<persisted-output>` 化(隣の 9k は無傷)
|
|
93
|
+
= 10k 判定は per context string
|
|
94
|
+
- 5 本 × 9k = 45,000 字でも全部可視。合算上限は 45k まで観測されず
|
|
95
|
+
- **attachment の並び順は登録順と一致しない**(並列実行のため非決定)
|
|
96
|
+
- 詳細は [rag/01-hooks/hook-stdout-10k-persisted-output.md](../../rag/01-hooks/hook-stdout-10k-persisted-output.md)
|
|
97
|
+
|
|
98
|
+
## 帰結
|
|
99
|
+
|
|
100
|
+
- 「5〜20 ターン前の空白帯」は消滅: 窓内の非注入分は `recall --l2` が verbatim で、
|
|
101
|
+
窓外は `recall --l1` が全ターン(未要約含む)で必ず到達可能
|
|
102
|
+
- push は会話密度に自動適応(中央値 7〜8 ターン、軽い会話なら 10 ターン超、重くて 2〜3)
|
|
103
|
+
- コンテキスト衛生は維持: pull はモデルが必要と判断した時だけ発生する
|
|
104
|
+
- `handoff-executor` の injection stats は
|
|
105
|
+
`injected_l2_turns / remaining_l2_turns / older_turns / older_summarized` に更新
|
package/package.json
CHANGED
|
@@ -40,6 +40,23 @@ v2.1.195 より前に 10k 超を emit した実績が手元に無いため、「
|
|
|
40
40
|
ヘッダ + アンカー常時全文、L1 → L2 を新しい側から詰め、省略は注入文へ明示)。
|
|
41
41
|
詳細は [ADR 0014](../../docs/adr/0014-two-phase-handoff-ghost-baton.md)。
|
|
42
42
|
|
|
43
|
+
## 追記: 10k 判定は per context string — multi-hook で突破可能(2026-07-18 実測、不採用)
|
|
44
|
+
|
|
45
|
+
同一イベントに複数 hook を登録した場合の挙動を実測した(Claude Code 2.1.211、
|
|
46
|
+
一時 project + 3〜5 本の UserPromptSubmit tracer hook + `claude -p` Haiku):
|
|
47
|
+
|
|
48
|
+
- 各 hook stdout は**独立の `hook_success` attachment** になり、10k 判定も per string。
|
|
49
|
+
3 本 × 9,000 字 = 27,000 字、5 本 × 9,000 字 = 45,000 字が全部モデル可視 inline
|
|
50
|
+
- 1 本だけ 12k にすると**その 1 本だけ**が `<persisted-output>` 化し、隣の 9k は無傷
|
|
51
|
+
- hooks reference の「10,000 characters per context string」「複数 hook の
|
|
52
|
+
additionalContext は all of the values が届く」の文言と整合
|
|
53
|
+
- **罠**: attachment の並び順は登録順と一致しない(hook 並列実行のため非決定)。
|
|
54
|
+
multi-part 注入に使うなら各 part に自己記述ヘッダが必須
|
|
55
|
+
|
|
56
|
+
Throughline では「hook の構造的想定(1 本 = 1 context string)に無い使い方」として
|
|
57
|
+
**不採用**(オーナー裁定 2026-07-18)。注入は 9,500 字 push + `throughline recall` pull の
|
|
58
|
+
二段構成にした([ADR 0016](../../docs/adr/0016-push-pull-recall-injection.md))。
|
|
59
|
+
|
|
43
60
|
## 検証手順(再現用)
|
|
44
61
|
|
|
45
62
|
1. 一時 project に `.claude/settings.json` で UserPromptSubmit hook を登録し、
|