trajectories 0.0.0 → 0.1.2

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.
Files changed (205) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +201 -0
  3. package/README.md +288 -2
  4. package/dist/cli.js +30210 -0
  5. package/dist/convert.d.ts +31 -0
  6. package/dist/convert.js +431 -0
  7. package/dist/deepagents_checkpoint.py +362 -0
  8. package/dist/index.d.ts +7 -0
  9. package/dist/index.js +8402 -0
  10. package/dist/log-baseline.d.ts +26 -0
  11. package/dist/log-baseline.js +104 -0
  12. package/dist/log-content.d.ts +16 -0
  13. package/dist/log-content.js +80 -0
  14. package/dist/log-io.d.ts +10 -0
  15. package/dist/log-io.js +121 -0
  16. package/dist/log-read.d.ts +24 -0
  17. package/dist/log-read.js +117 -0
  18. package/dist/log-reconcile.d.ts +5 -0
  19. package/dist/log-reconcile.js +38 -0
  20. package/dist/log-records.d.ts +35 -0
  21. package/dist/log-records.js +85 -0
  22. package/dist/log-recovery.d.ts +18 -0
  23. package/dist/log-recovery.js +123 -0
  24. package/dist/log-stage.d.ts +14 -0
  25. package/dist/log-stage.js +138 -0
  26. package/dist/log-state.d.ts +89 -0
  27. package/dist/log-state.js +92 -0
  28. package/dist/log-switch.d.ts +3 -0
  29. package/dist/log-switch.js +98 -0
  30. package/dist/log-types.d.ts +129 -0
  31. package/dist/log-types.js +8 -0
  32. package/dist/log.d.ts +8 -0
  33. package/dist/log.js +62 -0
  34. package/dist/migrate.d.ts +29 -0
  35. package/dist/migrate.js +173 -0
  36. package/dist/normalization-runtime.d.ts +1 -0
  37. package/dist/normalization-runtime.js +3150 -0
  38. package/dist/portable.d.ts +9 -0
  39. package/dist/portable.js +26 -0
  40. package/dist/readers/canonical.d.ts +13 -0
  41. package/dist/readers/canonical.js +54 -0
  42. package/dist/readers/cline.d.ts +10 -0
  43. package/dist/readers/cline.js +107 -0
  44. package/dist/readers/cursor.d.ts +20 -0
  45. package/dist/readers/cursor.js +374 -0
  46. package/dist/readers/deepagents.d.ts +41 -0
  47. package/dist/readers/deepagents.js +163 -0
  48. package/dist/readers/fx.d.ts +11 -0
  49. package/dist/readers/fx.js +573 -0
  50. package/dist/readers/github-copilot.d.ts +8 -0
  51. package/dist/readers/github-copilot.js +216 -0
  52. package/dist/readers/grok-build.d.ts +12 -0
  53. package/dist/readers/grok-build.js +391 -0
  54. package/dist/readers/guards.d.ts +4 -0
  55. package/dist/readers/guards.js +121 -0
  56. package/dist/readers/pi-compaction.d.ts +7 -0
  57. package/dist/readers/pi-compaction.js +112 -0
  58. package/dist/readers/pi-context.d.ts +13 -0
  59. package/dist/readers/pi-context.js +64 -0
  60. package/dist/store.d.ts +25 -0
  61. package/dist/store.js +218 -0
  62. package/dist/types.d.ts +91 -0
  63. package/dist/types.js +1 -0
  64. package/dist/writers/claude-code.d.ts +5 -0
  65. package/dist/writers/claude-code.js +51 -0
  66. package/dist/writers/cline.d.ts +3 -0
  67. package/dist/writers/cline.js +77 -0
  68. package/dist/writers/codex.d.ts +3 -0
  69. package/dist/writers/codex.js +32 -0
  70. package/dist/writers/cursor.d.ts +3 -0
  71. package/dist/writers/cursor.js +197 -0
  72. package/dist/writers/deepagents.d.ts +3 -0
  73. package/dist/writers/deepagents.js +116 -0
  74. package/dist/writers/entropy.d.ts +5 -0
  75. package/dist/writers/entropy.js +33 -0
  76. package/dist/writers/fx.d.ts +3 -0
  77. package/dist/writers/fx.js +135 -0
  78. package/dist/writers/github-copilot.d.ts +3 -0
  79. package/dist/writers/github-copilot.js +71 -0
  80. package/dist/writers/grok-build.d.ts +3 -0
  81. package/dist/writers/grok-build.js +43 -0
  82. package/dist/writers/helpers.d.ts +17 -0
  83. package/dist/writers/helpers.js +75 -0
  84. package/dist/writers/omp.d.ts +3 -0
  85. package/dist/writers/omp.js +7 -0
  86. package/dist/writers/opencode.d.ts +3 -0
  87. package/dist/writers/opencode.js +65 -0
  88. package/dist/writers/pi.d.ts +3 -0
  89. package/dist/writers/pi.js +45 -0
  90. package/docs/cline-native-format.md +43 -0
  91. package/docs/copilot-native-format.md +78 -0
  92. package/docs/cursor-native-format.md +103 -0
  93. package/docs/deepagents-native-format.md +98 -0
  94. package/docs/evidence-api.md +170 -0
  95. package/docs/fx-native-format.md +87 -0
  96. package/docs/harness-parity-bootstrap.md +78 -0
  97. package/docs/harness-support-parity.md +107 -0
  98. package/docs/inspirations.md +103 -0
  99. package/docs/ledger-migration.md +141 -0
  100. package/docs/pi-compaction-context.md +75 -0
  101. package/docs/pi-provider-context.md +76 -0
  102. package/docs/research/2026-10-06-huihua-and-oar.md +176 -0
  103. package/docs/research/ledger/README.md +96 -0
  104. package/docs/research/ledger/confer/2026-09-13-round-10/README.md +61 -0
  105. package/docs/research/ledger/confer/2026-09-13-round-2/cass.md +72 -0
  106. package/docs/research/ledger/confer/2026-09-13-round-3/README.md +70 -0
  107. package/docs/research/ledger/confer/2026-09-13-round-5/README.md +61 -0
  108. package/docs/research/ledger/confer/2026-09-13-round-6/README.md +68 -0
  109. package/docs/research/ledger/confer/2026-09-13-round-7/README.md +64 -0
  110. package/docs/research/ledger/confer/2026-09-13-round-9/README.md +59 -0
  111. package/docs/research/ledger/confer/2026-09-14-wake/wake.md +146 -0
  112. package/docs/research/ledger/docs/inspirations.md +320 -0
  113. package/docs/research/ledger/docs/pr2-pr6-integration.md +53 -0
  114. package/docs/research/ledger/docs/research/2026-09-07-confer-inspirations.md +163 -0
  115. package/docs/research/ledger/docs/research/2026-09-07-letta-continuous-service.md +104 -0
  116. package/docs/research/ledger/docs/research/2026-09-08-claude-history-retrieval.md +117 -0
  117. package/docs/research/ledger/docs/research/2026-09-09-agentsview-native-discovery.md +87 -0
  118. package/docs/research/ledger/docs/research/2026-09-09-daemon-observation.md +231 -0
  119. package/docs/research/ledger/docs/research/2026-09-09-discovery-pagination.md +114 -0
  120. package/docs/research/ledger/docs/research/2026-09-09-harness-transcript-contract.md +145 -0
  121. package/docs/research/ledger/docs/research/2026-09-09-native-history-discovery.md +180 -0
  122. package/docs/research/ledger/docs/research/2026-09-09-native-history-implementation.md +137 -0
  123. package/docs/research/ledger/docs/research/2026-09-09-other-inspirations.md +279 -0
  124. package/docs/research/ledger/docs/research/2026-09-09-trajectory-and-letta-code.md +128 -0
  125. package/docs/research/ledger/docs/research/2026-09-13-diagnostics-coverage.md +116 -0
  126. package/docs/research/ledger/docs/research/2026-09-13-fts-rebuild.md +83 -0
  127. package/docs/research/ledger/docs/research/2026-09-13-read-path-split.md +74 -0
  128. package/docs/research/ledger/docs/research/2026-09-13-runtime-rollout.md +102 -0
  129. package/docs/research/ledger/docs/research/2026-09-13-scan-outcomes.md +80 -0
  130. package/docs/research/ledger/docs/research/2026-09-13-search-quality.md +105 -0
  131. package/docs/research/ledger/docs/research/2026-09-13-source-coverage.md +64 -0
  132. package/docs/research/ledger/docs/research/2026-09-13-sync-wall-budget.md +82 -0
  133. package/docs/research/ledger/docs/research/2026-09-13-typed-read-gate.md +81 -0
  134. package/docs/research/ledger/docs/research/2026-09-13-writer-lease.md +84 -0
  135. package/docs/research/ledger/docs/research/2026-09-16-c1-copy-fallback.md +227 -0
  136. package/docs/research/ledger/docs/research/2026-09-16-c10-kimi.md +414 -0
  137. package/docs/research/ledger/docs/research/2026-09-16-c2-derive-counter.md +216 -0
  138. package/docs/research/ledger/docs/research/2026-09-16-c3-echo-filter.md +305 -0
  139. package/docs/research/ledger/docs/research/2026-09-16-c4-title-index.md +349 -0
  140. package/docs/research/ledger/docs/research/2026-09-16-c5-trigram.md +268 -0
  141. package/docs/research/ledger/docs/research/2026-09-16-c6-recency-prior.md +438 -0
  142. package/docs/research/ledger/docs/research/2026-09-16-c7-tombstones.md +467 -0
  143. package/docs/research/ledger/docs/research/2026-09-16-c8-remote-truth.md +390 -0
  144. package/docs/research/ledger/docs/research/2026-09-16-c9-grok.md +380 -0
  145. package/docs/research/ledger/docs/research/2026-09-16-wake-port-program.md +38 -0
  146. package/docs/research/ledger/docs/research/2026-09-17-cursor-blobstore.md +157 -0
  147. package/docs/research/ledger/manifest.json +540 -0
  148. package/legacy/NOTICE.md +50 -0
  149. package/legacy/bin/ledger-core.ts +7 -0
  150. package/legacy/bin/ledger.ts +7 -0
  151. package/legacy/bin/traj-bridge.ts +7 -0
  152. package/legacy/scripts/import-claude-history.ts +130 -0
  153. package/legacy/scripts/native-history-smoke.ts +52 -0
  154. package/legacy/scripts/observe-daemon.ts +570 -0
  155. package/legacy/scripts/retitle-sessions.md +19 -0
  156. package/legacy/scripts/verify-runtime-rollout.ts +151 -0
  157. package/legacy/src/canonical.ts +108 -0
  158. package/legacy/src/cli.ts +1293 -0
  159. package/legacy/src/db.ts +2254 -0
  160. package/legacy/src/diagnostics.ts +87 -0
  161. package/legacy/src/discovery.ts +56 -0
  162. package/legacy/src/embeddings.ts +382 -0
  163. package/legacy/src/filter.ts +602 -0
  164. package/legacy/src/gemini_embeddings.ts +103 -0
  165. package/legacy/src/identity.ts +9 -0
  166. package/legacy/src/index.ts +10 -0
  167. package/legacy/src/ingest.ts +791 -0
  168. package/legacy/src/managed.ts +178 -0
  169. package/legacy/src/native/common.ts +68 -0
  170. package/legacy/src/native/cursor.ts +647 -0
  171. package/legacy/src/native/grok.ts +540 -0
  172. package/legacy/src/native/kimi.ts +706 -0
  173. package/legacy/src/native/opencode.ts +470 -0
  174. package/legacy/src/native/sqlite-ro.ts +281 -0
  175. package/legacy/src/native.ts +97 -0
  176. package/legacy/src/normalizer.ts +457 -0
  177. package/legacy/src/parentage.ts +562 -0
  178. package/legacy/src/pressure.ts +162 -0
  179. package/legacy/src/private-storage.ts +74 -0
  180. package/legacy/src/reader.ts +1255 -0
  181. package/legacy/src/scanner.ts +591 -0
  182. package/legacy/src/schema-gate.ts +263 -0
  183. package/legacy/src/schema.ts +829 -0
  184. package/legacy/src/semantic-top-k.ts +71 -0
  185. package/legacy/src/session-hash.ts +49 -0
  186. package/legacy/src/store-status.ts +218 -0
  187. package/legacy/src/sync-preparation.ts +45 -0
  188. package/legacy/src/sync-truth.ts +843 -0
  189. package/legacy/src/sync.ts +1233 -0
  190. package/legacy/src/types.ts +436 -0
  191. package/legacy/src/vocab.txt +30522 -0
  192. package/legacy/src/watch.ts +673 -0
  193. package/legacy/src/writer-lease.ts +310 -0
  194. package/ops/README.md +150 -0
  195. package/ops/install-cli.sh +63 -0
  196. package/ops/install-native.sh +35 -0
  197. package/ops/install-release.sh +49 -0
  198. package/ops/linux/install-systemd-user.sh +103 -0
  199. package/ops/macos/install-launchd.sh +72 -0
  200. package/ops/sync-settings.sh +37 -0
  201. package/package.json +21 -5
  202. package/skills/trajectories/SKILL.md +138 -0
  203. package/skills/trajectories/scripts/trajectories_recent.sh +26 -0
  204. package/skills/trajectories/scripts/trajectories_search.sh +26 -0
  205. package/index.js +0 -1
@@ -0,0 +1,231 @@
1
+ # Integrated daemon observation
2
+
3
+ Status: operational retest passed at source revision `6bd64b7`; the supervised
4
+ daemon remains running. Failed attempts are retained. Corpus coverage and raw
5
+ archival retention are not complete.
6
+
7
+ ## Question and acceptance
8
+
9
+ Can the integrated Ledger watcher preserve the existing index, make new native
10
+ records searchable under launchd, and continue bounded backfill without loading
11
+ an entire large JSONL transcript?
12
+
13
+ Acceptance requires a schema-6 migration with no missing existing record hashes,
14
+ the recovered Claude prompt slice still searchable, a supervised foreground
15
+ process, automatic import and append of a labeled synthetic canary, and continued
16
+ cursor progress. A successful process start or populated database alone is not
17
+ acceptance. This trial does not establish complete corpus coverage, raw archival
18
+ retention, exactly-once effects, or steady-state fleet capacity.
19
+
20
+ ## Frozen protocol
21
+
22
+ - Target: this Mac's stable Ledger checkout at merged revision
23
+ `56b51e2b76d8d37f749bb114f5b73f1e40164cf9`, Bun 1.4.0,
24
+ installed Trajectory 0.3.0. Both PRs are merged before launch.
25
+ - Host: Apple Silicon macOS, 10 CPU cores, 24 GiB physical memory. Other desktop
26
+ work continues; this is an operational observation, not an isolated benchmark.
27
+ - Database: create a SQLite backup of the existing checkout index, preserving the
28
+ original. Migrate only the candidate, verify identity-adjusted record hashes,
29
+ then install the candidate at the service's dedicated data path.
30
+ - Workload: native-source discovery and bounded catch-up, plus a clearly labeled
31
+ synthetic Pi JSONL canary with three sequential append observations. No private
32
+ transcript bodies, titles, credentials, or environment dumps in reports.
33
+ - Service settings: lexical ingestion, 60-second reconciliation, 25 candidate
34
+ sessions per source per tick, explicit discovery limit 1,000,000 per source,
35
+ 1 MiB target normalization chunks, adaptive individual records up to 64 MiB.
36
+ The record-input ceiling is not a peak-RSS guarantee.
37
+ - Observe the explicit process tree and read-only aggregate DB state at one-second
38
+ cadence for at least 180 seconds. Record baseline, sampled peak, and end RSS,
39
+ cumulative CPU and sampled CPU demand, wall time, query responsiveness, cursor
40
+ progress, failures, and supervision state. Keep unavailable I/O measurements
41
+ explicit rather than reporting zero. The service remains running afterward.
42
+ - Repetition: one operational service run, three canary appends. Do not infer
43
+ stable percentiles or capacity from this sample.
44
+ - Model/token accounting: no model or embedding generation in the daemon trial;
45
+ model tokens and cache counters are not applicable. Agent engineering work is
46
+ outside the sampled daemon process tree.
47
+ - Preserve private raw observation artifacts and hashes locally; publish only a
48
+ compact verdict and sanitized evidence. Validate the trial record with the
49
+ personal evaluation-engineering checker and disclose missing measurements.
50
+
51
+ ## Exploratory metadata
52
+
53
+ Before installation, the original schema-v3 index contains 190 sessions and
54
+ 68,301 canonical records: 2 Claude sessions and 188 Cursor sessions. One Claude
55
+ session is the separately recovered prompt-history slice. These counts do not
56
+ establish complete coverage.
57
+
58
+ A paginated metadata-only discovery returned 14,493 candidate sessions:
59
+ 6,227 Claude, 3,406 Codex, 187 Pi, 1 Letta Code, 137 Droid, 2,705 Cursor,
60
+ and 1,830 OpenCode. Files can change while discovery runs; these are candidate
61
+ listings, not successfully normalized sessions or an immutable corpus manifest.
62
+ An earlier over-limit probe was invalid because upstream page size is capped at
63
+ 1,000; the scanner now paginates instead of reporting those sources as empty.
64
+
65
+ Byte framing of six large files, without decoding transcript values, found a
66
+ roughly 750 MB Claude file and Codex files up to 419 MB. The largest observed
67
+ individual JSONL row was 4,184,489 bytes. This motivated separating the 1 MiB
68
+ batch target from the individual-record ceiling.
69
+
70
+ ## Results
71
+
72
+ ### Pre-observation startup attempt
73
+
74
+ The schema-6 candidate retained all 190 sessions and all 68,301 original record
75
+ hashes after source-prefix adjustment. The recovered prompt slice retains 78
76
+ records and the scoped `benchflow` query returns its historical hit. Integrity
77
+ and foreign-key checks pass. The original schema-3 database remains unchanged.
78
+
79
+ The first launchd attempt used the checkout below `~/Documents/Codex/`. It
80
+ stalled before opening SQLite: a one-second process sample remained in
81
+ `getcwd -> open`, and the matching macOS TCC event requested
82
+ `kTCCServiceSystemPolicyDocumentsFolder` for Bun. This is a failed startup, not
83
+ an ingestion run. The service was unloaded; no OS security policy was changed.
84
+
85
+ For the observation, the same merged revision was cloned to
86
+ `~/workspace/personal/ledger`, dependencies installed from the frozen lockfile,
87
+ and the service reinstalled from that stable checkout. The dedicated database
88
+ and rollback copy are outside Documents. This path correction is fixed before
89
+ the ingestion observation begins. Service logs were set to mode 0600; their
90
+ parent directory is 0700. The installer's shell umask alone does not govern
91
+ files created by launchd.
92
+
93
+ ### Ingestion observation
94
+
95
+ The first full observation completed its 180.120-second collection window with
96
+ 175 samples, zero collection errors, and zero query timeouts. **Ingestion
97
+ acceptance failed:** sessions remained 190, records 68,301, and persisted cursors
98
+ zero. The synthetic import returned zero hits after 90.199 seconds (90 polls,
99
+ zero query errors); subsequent appends were not attempted.
100
+
101
+ Attach-time RSS was 69,058,560 bytes, sampled peak 82,165,760 bytes, and end RSS
102
+ 74,088,448 bytes. The observed process tree consumed at least 132.15 CPU-seconds;
103
+ peak sampled demand was 1.0015 cores. This is wasted startup work, not measured
104
+ ingestion throughput. Per-process I/O and post-teardown memory were not measured.
105
+
106
+ The watcher recursively subscribes to each directory and also manually walks
107
+ its descendants, including broad agent homes containing plugins and worktrees.
108
+ All of this precedes the first reconcile. A process sample during the failed
109
+ window shows deep repeated filesystem recursion. The process was unloaded and
110
+ its exit verified after collection. The original DB and failed-run artifacts
111
+ remain intact.
112
+
113
+ ### Revised-run protocol
114
+
115
+ Retain the same migration, search, import, three-append, and cursor-progress
116
+ acceptance criteria. Before another run, narrow automatic subscriptions to
117
+ actual transcript stores, retain shallow parent watches for store creation,
118
+ and use manual per-directory watches only as a platform fallback. Keep all
119
+ sources and periodic reconciliation. Test those properties before deployment;
120
+ record the committed revised source SHA before launch. Repeat the 180-second
121
+ collection on the same dedicated DB and a new labeled canary. Preserve the
122
+ failed attempt rather than replacing it with the retest. This is a single
123
+ operational retest, not a controlled throughput or capacity comparison.
124
+
125
+ The retest's watcher change is committed at `750f7b8`, with installer changes at
126
+ `952f9078b3fe188d9e916915d9e772ae50fe0709`. Before deployment, the combined core
127
+ suite passed 182 tests and 1,087 assertions across 19 files; the observer suite
128
+ passed 10 tests and 79 assertions. Typecheck and whitespace checks passed.
129
+
130
+ Independent review then found quadratic ancestor lookup in the unsupported
131
+ recursive-watch fallback. A 512-directory fixture measured 131,842 candidate
132
+ visits; the recursive-only registry reduces that counter to zero. The final
133
+ deployment is `6bd64b717e646b0d62d31eb833f3dff0736e4456`, with 183 passing core
134
+ tests, 1,097 assertions across 19 files, and passing typecheck. The same 10-test
135
+ observer gate remains green. This final revision was captured before launch,
136
+ along with source-tree, runtime binary, watcher, lockfile, and protocol hashes.
137
+
138
+ ### Retest results
139
+
140
+ The 180.186-second run completed with 175 process samples and 33 read-only DB
141
+ samples, zero collection errors, and zero query timeouts. The installed service
142
+ started once and remained the same supervised process throughout.
143
+
144
+ | Measurement | Observed result |
145
+ | --- | --- |
146
+ | Indexed sessions | 267 -> 752; increase 485 |
147
+ | Canonical records / FTS rows | 71,745 -> 123,896; increase 52,151, equal at each endpoint |
148
+ | Cursor bytes ingested | 32,656,746 -> 404,526,886; progress 371,870,140 bytes |
149
+ | Synthetic initial import | Searchable after 10.024 seconds |
150
+ | Three sequential appends | Searchable after 12.032, 9.021, and 3.007 seconds |
151
+ | Attach / sampled peak / end RSS | 249,462,784 / 347,127,808 / 289,652,736 bytes |
152
+ | Observed CPU-seconds / peak sampled cores | 130.26 / 1.0173 |
153
+ | Aggregate DB subprocess elapsed range | 89.141-566.350 ms; includes process startup |
154
+ | Failed cursors at window end | 61; not hidden as successful coverage |
155
+
156
+ Canary visibility is polled at one-second intervals under ongoing native
157
+ backfill; these are observed end-to-end latencies, not latency percentiles.
158
+ Cursor byte totals are logical progress, not disk-I/O counters. The two native
159
+ canary files are explicitly synthetic and remain labeled in the Pi store.
160
+ The sampled RSS peak is about 331 MiB, not a hard memory bound.
161
+
162
+ A later coherent read transaction found 1,523 sessions, 190,501 records and the
163
+ same number of FTS rows, zero foreign-key violations, and two staged replacement
164
+ sessions. All 78 recovered Claude prompts remained present; the scoped
165
+ `benchflow` historical query still returned its hit (5.656 ms for that probe).
166
+ This post-window snapshot demonstrates continued progress, not an extension of
167
+ the frozen measurement window or complete archival preservation.
168
+
169
+ The actual CLI was also checked after the window using `query benchflow` with
170
+ `--source claude-code --limit 20 --robot --json` and the dedicated DB. Its
171
+ structured response contained nine hits, including the recovered history slice,
172
+ with exit zero and no stderr. The fully checked end-to-end invocation took
173
+ 4.645 seconds under continuing backfill; do not confuse that with the isolated
174
+ SQL-query duration above or infer stable latency from this probe.
175
+
176
+ The service uses the committed PR7 implementation from
177
+ `~/workspace/personal/ledger` and `~/.local/share/ledger/ledger.sqlite`. It remains
178
+ installed under user launchd label `com.archeism.ledger`. PR2 and PR6 are already
179
+ merged; this operational follow-up is kept as its own PR.
180
+
181
+ ### Residual failures
182
+
183
+ Observed failed receipts included 33 small Cursor `/projects/` JSONL files
184
+ totaling 5,490 bytes, additional Cursor JSONL files, and 25 OpenCode JSON files
185
+ totaling 958,856 bytes. No failed-file payloads were opened for this triage.
186
+ Their metadata cannot establish whether they are malformed, metadata-only,
187
+ configuration, or unsupported transcript exports. Zero checkpoint bytes does
188
+ not mean zero file reads. The successful run observed ingestion from Claude,
189
+ Codex, Cursor, Pi, Droid, and Letta Code; it does not establish all-source recall.
190
+
191
+ Source inspection confirms two remaining issues. Custom discovery primarily
192
+ filters by extension, while OpenCode normalization expects a whole
193
+ `{info, messages}` export. Also, non-JSONL failures currently omit the cache
194
+ receipt fields needed to skip an unchanged failure. A newest-first budget can
195
+ therefore keep retrying the same 25 files before older valid exports. This is a
196
+ confirmed scheduling mechanism, not proof that valid older OpenCode exports
197
+ exist in this corpus. Cursor JSONL failures already have fingerprint-bound
198
+ negative caching.
199
+
200
+ Follow-up work: tighten export eligibility, retain controlled failure reasons,
201
+ distinguish deterministic failures from transient retries, and separate fresh
202
+ work from retry scheduling. A regression should place 25 newer invalid JSON
203
+ fixtures ahead of one older valid export, then change a failed fixture into a
204
+ valid export and require recovery. These changes are not claimed as implemented
205
+ by this PR. See `scanner.ts:194`, `ingest.ts:122`, and `watch.ts:443` at the final
206
+ deployment revision.
207
+
208
+ ## Remaining optimization
209
+
210
+ The [bounded pagination probe](2026-09-09-discovery-pagination.md) confirms that
211
+ Trajectory 0.3.0 inventories a source again for every page. Two pages over 1,002
212
+ synthetic files caused 2,004 stat operations and four directory reads. This is
213
+ not evidence that pagination caused the measured startup stall. There is no
214
+ public single-inventory API in the installed package. Keep full discovery
215
+ coverage and the published normalizer; request an upstream inventory boundary
216
+ before replacing this with private imports or copied discovery code.
217
+
218
+ Both failed-attempt evaluation records validate structurally and remain
219
+ `complete: false`: per-process I/O and post-teardown memory were not collected,
220
+ and the TCC startup attempt lacks a full resource trace. Original artifacts and
221
+ checksums are private under `.local/integrate-7wV4FS/evaluation/` in the retained
222
+ development checkout. No private transcript contents are published here.
223
+
224
+ The third record, `evaluation/attempt-03-operational-retest/run.json`, also
225
+ validates with no errors and `complete: false` for the same unmeasured I/O and
226
+ teardown fields. Its operational rubric passes. Six negative controls reject
227
+ changed hashes, fabricated zero teardown memory, absent record progress, a
228
+ missing append, missing historical evidence, and a service restart. The frozen
229
+ protocol is recovered from Git revision `6bd64b7` and matches its prelaunch
230
+ hash; this document's subsequently appended results are not substituted for
231
+ the trial input. All prior archive files remain unchanged.
@@ -0,0 +1,114 @@
1
+ # Discovery pagination: bounded enumeration evidence
2
+
3
+ 2026-09-09. **Measured future optimization, not an implemented change.** Retain
4
+ published Trajectory normalization and complete discovery; do not replace its
5
+ discovery layer or depend on private imports to optimize pagination.
6
+
7
+ ## Pin and public boundary
8
+
9
+ - Installed and [lockfile-resolved](https://github.com/archeism/ledger/blob/43081aef54c23b4bd017b6c0e11fc42a9a1cb381/packages/core/bun.lock) package:
10
+ `@letta-ai/trajectory@0.3.0`. Inspected upstream pin:
11
+ [`21ae92d10579499b183d93929c85da0b91233167`](https://github.com/letta-ai/trajectory/tree/21ae92d10579499b183d93929c85da0b91233167).
12
+ - The only public discovery function is `listTrajectories`. There is no
13
+ `listSourceSessions`, inventory snapshot API, or public adapter lister.
14
+ [Entrypoint](https://github.com/letta-ai/trajectory/blob/21ae92d10579499b183d93929c85da0b91233167/src/index.ts#L181-L186).
15
+ - Package subpaths expose only `.`, `./schema`, `./schema/canonical`, and
16
+ `./package.json`. Resolving `dist/listing.js` or `dist/adapters/omp/list.js`
17
+ through the package name returned `ERR_PACKAGE_PATH_NOT_EXPORTED` in Node.
18
+ An internal function's `export` declaration is not a public package contract.
19
+ [Export map](https://github.com/letta-ai/trajectory/blob/21ae92d10579499b183d93929c85da0b91233167/package.json#L17-L25).
20
+ - Every page calls the source lister again before pagination; page size is capped
21
+ at 1,000. The cursor retains identity/position, not an inventory snapshot.
22
+ [Dispatch and pagination](https://github.com/letta-ai/trajectory/blob/21ae92d10579499b183d93929c85da0b91233167/src/listing.ts#L74-L153).
23
+
24
+ ## Observation and limits
25
+
26
+ Node `v24.18.1`, macOS arm64; 1,002 synthetic empty JSONL files in one temporary
27
+ project, separately listed as Pi and OMP. Pass-through instrumentation counts
28
+ filesystem API calls under the fixture root, not kernel syscalls or physical I/O.
29
+
30
+ | Source (each independently) | Cumulative items | `statSync` | `readdirSync` | `readFileSync` |
31
+ | --- | ---: | ---: | ---: | ---: |
32
+ | Pi / OMP, page 1 | 1,000 | 1,002 | 2 | 0 |
33
+ | Pi / OMP, pages 1 + 2 | 1,002 | 2,004 | 4 | 0 |
34
+
35
+ Thus two pages repeat inventory metadata work. There were no fixture-content
36
+ reads: these listers discover metadata without normalizing the empty files.
37
+ Bun `1.4.0` did not expose named filesystem imports to this instrumentation;
38
+ its zero counters were rejected, not treated as zero I/O. Counts above are from
39
+ Node, not a Bun timing comparison.
40
+
41
+ Ledger's audited `5d629ec3058485327a84c963f8c73997e46acbc1` scanner accumulated
42
+ all pages before the watcher applied its import budget. This is a source-backed
43
+ delay mechanism, **not a measured cause of the reported 90/130-second daemon
44
+ observations, nor a throughput, memory, latency, or capacity result**. No native
45
+ corpus, real database, model, embedding, or daemon was exercised by this probe.
46
+
47
+ ## Deferred public API proposal
48
+
49
+ An upstream public full-inventory function, for example
50
+ `listSourceSessions({ source, root }) -> Promise<TrajectoryListing[]>`, could
51
+ reuse the existing listers once. This is a proposed name/contract, not an API
52
+ available in `0.3.0`. Ledger could then enumerate children for **all OMP parents**
53
+ before sorting and applying its final combined budget. No parser is copied and
54
+ published canonical normalization remains unchanged. Until such a supported API
55
+ exists, retain current complete pagination rather than lower coverage limits,
56
+ deep-import internals, or add a large duplicate discovery implementation.
57
+ No upstream issue or change has been submitted or authorized by this note.
58
+
59
+ ## Reproduce
60
+
61
+ From the repository root with dependencies already installed and Node 24.18.1:
62
+ the command imports only public package entrypoints, creates 1,002 temporary empty
63
+ files, asserts counts/coverage, and removes the fixture in `finally`.
64
+
65
+ ```sh
66
+ cd packages/core
67
+ node --input-type=module <<'JS'
68
+ import fs from "node:fs";
69
+ import { createRequire, syncBuiltinESMExports } from "node:module";
70
+ import { tmpdir } from "node:os";
71
+ import { join } from "node:path";
72
+ import assert from "node:assert/strict";
73
+ const require = createRequire(import.meta.url);
74
+ assert.equal(require("@letta-ai/trajectory/package.json").version, "0.3.0");
75
+ const root = fs.mkdtempSync(join(tmpdir(), "ledger-listing-count-"));
76
+ const project = join(root, "sessions", "project");
77
+ const original = { statSync: fs.statSync, readdirSync: fs.readdirSync,
78
+ readFileSync: fs.readFileSync };
79
+ let counts = [0, 0, 0]; // stat, directory enumeration, content read
80
+ try {
81
+ fs.mkdirSync(project, { recursive: true });
82
+ for (let i = 0; i < 1002; i++)
83
+ fs.writeFileSync(join(project, `session-${String(i).padStart(4, "0")}.jsonl`), "");
84
+ Object.keys(original).forEach((method, index) => {
85
+ fs[method] = function (...args) {
86
+ if (String(args[0]).startsWith(root + "/")) counts[index]++;
87
+ return Reflect.apply(original[method], fs, args);
88
+ };
89
+ });
90
+ syncBuiltinESMExports();
91
+ const api = await import("@letta-ai/trajectory");
92
+ assert.deepEqual(Object.keys(api).filter(k => /^list/.test(k)), ["listTrajectories"]);
93
+ for (const source of ["pi", "omp"]) {
94
+ counts = [0, 0, 0];
95
+ const first = await api.listTrajectories({ source, root, limit: 1000 });
96
+ assert.equal(first.items.length, 1000);
97
+ assert.ok(first.nextCursor);
98
+ assert.deepEqual(counts, [1002, 2, 0]);
99
+ console.log(JSON.stringify({ source, pages: 1, items: 1000, counts }));
100
+ const second = await api.listTrajectories({ source, root, limit: 1000,
101
+ cursor: first.nextCursor });
102
+ assert.equal(second.items.length, 2);
103
+ assert.equal(second.nextCursor, undefined);
104
+ assert.equal(new Set([...first.items, ...second.items].map(x => x.path)).size, 1002);
105
+ assert.deepEqual(counts, [2004, 4, 0]);
106
+ console.log(JSON.stringify({ source, pages: 2, items: 1002, counts }));
107
+ }
108
+ } finally {
109
+ Object.assign(fs, original);
110
+ syncBuiltinESMExports();
111
+ fs.rmSync(root, { recursive: true, force: true });
112
+ }
113
+ JS
114
+ ```
@@ -0,0 +1,145 @@
1
+ # Trajectory discovery and AI SDK Harnesses transcript contracts
2
+
3
+ ## Answer
4
+
5
+ **Trajectory parses Cursor and OpenCode exports but does not discover their local
6
+ stores.** This was verified against installed and latest published `0.3.0`, current
7
+ upstream source, and direct public-API probes below. Ledger's broad custom file
8
+ scanner is not functionality inherited from Trajectory.
9
+
10
+ **Vercel unifies live turn input/output and session lifecycle, not historical
11
+ transcript ingestion or a canonical archive.** Applications can use common AI SDK
12
+ messages and streams across harnesses, but each native runtime still owns its
13
+ conversation history. Passing old messages to `HarnessAgent` does not import that
14
+ history: it selects the latest user message, with separate handling for pending
15
+ tool continuations. [History contract](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/content/docs/03-ai-sdk-harnesses/02-harness-agent.mdx#L188-L201),
16
+ [implementation](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness/src/agent/harness-agent.ts#L833-L885).
17
+
18
+ ## Pins and scope
19
+
20
+ - Refreshed GitHub `main` on 2026-09-09 at 01:41 UTC:
21
+ [`45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563`](https://github.com/vercel/ai/commit/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563).
22
+ This supersedes the earlier `46cea633cf7286386eb4e70f9c04413c57723d35` research pin.
23
+ - Public npm metadata reported `@ai-sdk/harness` **1.0.104**, Claude Code
24
+ **1.0.108**, and Codex **1.0.106**. Their release tags all resolved to
25
+ [`db450a22f14c21fe734f0592bcde191a4515bc1c`](https://github.com/vercel/ai/commit/db450a22f14c21fe734f0592bcde191a4515bc1c).
26
+ The collected release-to-HEAD comparison showed no changes under those three
27
+ package directories. Thus the examined package contracts also exist in
28
+ release-tag source, not only unreleased main. Built tarballs were not audited.
29
+ [Harness metadata](https://registry.npmjs.org/@ai-sdk/harness/1.0.104),
30
+ [Claude metadata](https://registry.npmjs.org/@ai-sdk/harness-claude-code/1.0.108),
31
+ [Codex metadata](https://registry.npmjs.org/@ai-sdk/harness-codex/1.0.106).
32
+ - Official source docs still label harness packages **experimental**, despite
33
+ published 1.0 versions. [Status](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/content/docs/03-ai-sdk-harnesses/01-overview.mdx#L25-L36).
34
+ - Bounded inspection: public GitHub tree, exported core/session interfaces,
35
+ eleven adapter entrypoints, official source docs, and decisive Claude/Codex
36
+ implementation and test paths. Tests were read, not run. No local upstream
37
+ clone, installation, model execution, daemon, database, or personal transcript
38
+ access. Findings below describe these inspected surfaces, not every native
39
+ SDK capability or deployed documentation version.
40
+
41
+ ## Trajectory: parsing is separate from discovery
42
+
43
+ On 2026-09-09, the installed package and npm's `latest` metadata both reported
44
+ `@letta-ai/trajectory` **0.3.0**. The registry's `gitHead` and a live GitHub `main`
45
+ lookup agreed on
46
+ [`21ae92d10579499b183d93929c85da0b91233167`](https://github.com/letta-ai/trajectory/commit/21ae92d10579499b183d93929c85da0b91233167).
47
+ [Published metadata](https://registry.npmjs.org/@letta-ai/trajectory/0.3.0).
48
+
49
+ | Source | Supported normalization input | Local-store listing |
50
+ | --- | --- | --- |
51
+ | Cursor | Captured role/message content-block JSONL, documented from SWE-chat; callers supply the group identity for canonical normalization. | Intentionally unavailable: this capture is not a documented Cursor local-store format. [Adapter contract](https://github.com/letta-ai/trajectory/blob/21ae92d10579499b183d93929c85da0b91233167/src/adapters/cursor/README.md#L1-L25). |
52
+ | OpenCode | An assembled whole-session JSON document with `info` and `messages`, whose entries contain message metadata and parts. | Intentionally unavailable: the caller provides the assembled export. [Adapter contract](https://github.com/letta-ai/trajectory/blob/21ae92d10579499b183d93929c85da0b91233167/src/adapters/opencode/README.md#L1-L34). |
53
+
54
+ The public lister distinguishes known normalization-only sources from unknown
55
+ sources, returning `listing_unavailable` for both Cursor and OpenCode.
56
+ [Dispatch and classification](https://github.com/letta-ai/trajectory/blob/21ae92d10579499b183d93929c85da0b91233167/src/listing.ts#L56-L110).
57
+
58
+ Bounded local verification used the installed package, no private inputs, and no
59
+ database or model calls:
60
+
61
+ | Probe | Cursor | OpenCode |
62
+ | --- | --- | --- |
63
+ | `listTrajectories({ source, limit: 1 })` | `NormalizationError`: `listing_unavailable` | `NormalizationError`: `listing_unavailable` |
64
+ | `normalizeTranscript` on a synthetic user/assistant exchange in the documented format | Three records: meta, user, assistant; one `timestamps_synthesized` diagnostic | Three records: meta, user, assistant; zero diagnostics |
65
+
66
+ Ledger therefore does need a caller-side discovery/export step for these sources.
67
+ The current custom scanner selects by directory, depth and filename extension,
68
+ not by a complete export-eligibility check. Its false candidates are a Ledger
69
+ discovery issue; swapping the call to `listTrajectories()` would instead make
70
+ both sources unavailable. [Ledger scanner](https://github.com/archeism/ledger/blob/43081aef54c23b4bd017b6c0e11fc42a9a1cb381/packages/core/src/scanner.ts).
71
+ No ingestion behavior or dependency was changed by this research.
72
+
73
+ ## What Vercel makes common
74
+
75
+ | Surface | Actual contract |
76
+ | --- | --- |
77
+ | Application history | `UIMessage` has `id`, role, optional metadata, and typed `parts`. This is an application/UI representation, not an assertion that all native execution evidence is included. [Shape](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/content/docs/07-reference/01-ai-sdk-core/31-ui-message.mdx#L50-L75). |
78
+ | Turn input | AI SDK accepts `ModelMessage[]`, but the adapter receives only `string \| UserModelMessage` for a fresh turn. Prior history stays native. A regression explicitly verifies older user/assistant turns are discarded. [Adapter input](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness/src/v1/harness-v1-prompt.ts#L1-L11), [test](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness/src/agent/harness-agent.test.ts#L3139-L3166). |
79
+ | Adapter events | `HarnessV1StreamPart` standardizes text/reasoning, tool input/call/approval/result, step/turn finish, usage, errors, file changes, compaction, and a raw escape hatch. It reuses LanguageModelV4 primitives; it is not itself `UIMessage`. [Union](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness/src/v1/harness-v1-stream-part.ts#L15-L137), [type-compatibility tests](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness/src/v1/harness-v1-stream-part.test-d.ts#L25-L95). |
80
+ | Consumer output | Adapter events become AI SDK `TextStreamPart`s; `harnessMetadata` becomes `providerMetadata`. The documented UI route uses `convertToModelMessages`, `agent.stream`, then `toUIMessageStream({ stream: result.stream })`. [Translation](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness/src/agent/internal/translate-stream-part.ts#L5-L27), [UI route](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/content/docs/03-ai-sdk-harnesses/07-ui.mdx#L153-L190). |
81
+ | Result messages | `responseMessages` is constructed from the current result's accumulated content as assistant/tool model messages. It is not a session-history getter. [Construction](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness/src/agent/internal/harness-stream-text-result.ts#L397-L405), [conversion](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness/src/agent/internal/harness-stream-text-result.ts#L472-L482). |
82
+
83
+ ## Session continuity is not transcript access
84
+
85
+ `createSession({ sessionId, resumeFrom })` restores a known session;
86
+ `continueFrom` restores an unfinished turn. `detach()` and `stop()` return opaque
87
+ resume state; `destroy()` ends without resumability. The envelope identifies
88
+ `harnessId`, specification version, adapter-defined JSON `data`, and optional
89
+ continuation/tool state. That is a common lifecycle envelope, not a portable
90
+ cross-provider conversation. [Creation API](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness/src/agent/harness-agent.ts#L284-L340),
91
+ [state types](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness/src/v1/harness-v1-lifecycle-state.ts#L59-L119).
92
+
93
+ The complete common `HarnessV1Session` interface exposes prompt/continue,
94
+ compaction, suspend, detach, stop, and destroy. **It has no session listing,
95
+ history retrieval, transcript export, or historical import method.** The inspected
96
+ public adapter entrypoints likewise expose adapter factories/settings/types,
97
+ not a cross-harness history service. This is a scoped API finding, not a claim
98
+ that Claude's or Codex's own SDKs cannot inspect their native histories.
99
+ [Session interface](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness/src/v1/harness-v1-session.ts#L152-L258),
100
+ [Claude exports](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness-claude-code/src/index.ts#L1-L15),
101
+ [Codex exports](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness-codex/src/index.ts#L1-L14).
102
+
103
+ Claude resumes the exact observed native conversation ID through the SDK's
104
+ `resume` option, falling back to cwd-based continuation only without an exact
105
+ ID. Codex retains a native `threadId` and calls `codex.resumeThread`.
106
+ Neither path enumerates old host-local transcripts and converts them into a
107
+ common historical dataset. [Claude resume](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness-claude-code/src/bridge/index.ts#L511-L528),
108
+ [Codex state](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness-codex/src/codex-harness.ts#L175-L188),
109
+ [Codex resume](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness-codex/src/bridge/index.ts#L205-L218).
110
+
111
+ Storage remains application-owned: the official session-store example keeps
112
+ opaque resume state in a map and explicitly requires durable storage in
113
+ production. It is not a retained-source archive or historical discovery/import
114
+ service. This is a stronger distinction than merely observing that bridge event
115
+ replay is not archival commitment. [Storage boundary](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/content/docs/03-ai-sdk-harnesses/07-ui.mdx#L97-L145).
116
+
117
+ ## Fidelity limits
118
+
119
+ - **Claude:** messages with `parent_tool_use_id` are excluded from the parent
120
+ text/tool projection. The regression asserts no child-agent events leak into
121
+ that stream. A captured parent stream therefore cannot reconstruct the full
122
+ child execution. [Filter](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness-claude-code/src/bridge/create-emit-stream-event.ts#L213-L218),
123
+ [test](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness-claude-code/src/bridge/create-emit-stream-event.test.ts#L470-L522).
124
+ - **Codex:** completed file changes become operation/path events; MCP results
125
+ prefer `structured_content` over `content` rather than preserving the native
126
+ result envelope. This is selective normalization, not a reversible native
127
+ transcript encoding. [Projection](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness-codex/src/bridge/create-emit-stream-event.ts#L226-L271).
128
+ - **Raw is not automatic preservation:** the common union permits
129
+ `{ type: 'raw', rawValue }` and the translator forwards it, but the inspected
130
+ Claude/Codex native loops feed their selective emitters without also emitting
131
+ each original native event as `raw`. The escape hatch does not recover events
132
+ those adapters never emit. [Raw forwarding](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness/src/agent/internal/translate-stream-part.ts#L252-L257),
133
+ [Claude loop](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness-claude-code/src/bridge/index.ts#L582-L600),
134
+ [Codex loop](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness-codex/src/bridge/index.ts#L234-L264).
135
+ - **SDK projection adds synthetic events:** file changes and compaction become
136
+ dynamic, provider-executed tool-call/result pairs with generated IDs. These
137
+ make UI rendering uniform; they are not original native tool calls or stable
138
+ source-record identities. [Translation](https://github.com/vercel/ai/blob/45f2b6a5bc06bdb40a847ea961f8d7ca7cd86563/packages/harness/src/agent/internal/translate-stream-part.ts#L165-L247).
139
+
140
+ **Ledger implication (inference):** HarnessAgent is useful as a live integration
141
+ boundary. Its normalized streams and opaque lifecycle state do not replace
142
+ historical source discovery, native-source retention, provenance/identity, or a
143
+ durable canonical archive. An application may persist UI messages for its own
144
+ chat history, but that does not establish complete cross-harness execution
145
+ evidence. No new implementation or adoption decision is made here.