@cleocode/core 2026.10.6 → 2026.10.7

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 (242) hide show
  1. package/dist/build-provenance.json +2 -2
  2. package/dist/cloud/http.d.ts +8 -0
  3. package/dist/cloud/http.d.ts.map +1 -1
  4. package/dist/cloud/http.js +11 -0
  5. package/dist/cloud/http.js.map +1 -1
  6. package/dist/cloud/journal.d.ts +6 -0
  7. package/dist/cloud/journal.d.ts.map +1 -1
  8. package/dist/cloud/journal.js +1 -1
  9. package/dist/cloud/journal.js.map +1 -1
  10. package/dist/cloud/nexus-cloud-status.d.ts.map +1 -1
  11. package/dist/cloud/nexus-cloud-status.js +3 -1
  12. package/dist/cloud/nexus-cloud-status.js.map +1 -1
  13. package/dist/cloud/nexus-vault.d.ts +86 -1
  14. package/dist/cloud/nexus-vault.d.ts.map +1 -1
  15. package/dist/cloud/nexus-vault.js +875 -98
  16. package/dist/cloud/nexus-vault.js.map +1 -1
  17. package/dist/docs/doctor.d.ts +183 -0
  18. package/dist/docs/doctor.d.ts.map +1 -0
  19. package/dist/docs/doctor.js +537 -0
  20. package/dist/docs/doctor.js.map +1 -0
  21. package/dist/doctor/system-health.d.ts +189 -0
  22. package/dist/doctor/system-health.d.ts.map +1 -0
  23. package/dist/doctor/system-health.js +783 -0
  24. package/dist/doctor/system-health.js.map +1 -0
  25. package/dist/git/hooks-install.d.ts +17 -34
  26. package/dist/git/hooks-install.d.ts.map +1 -1
  27. package/dist/git/hooks-install.js +173 -70
  28. package/dist/git/hooks-install.js.map +1 -1
  29. package/dist/harness/ask-enforce.d.ts +43 -0
  30. package/dist/harness/ask-enforce.d.ts.map +1 -0
  31. package/dist/harness/ask-enforce.js +165 -0
  32. package/dist/harness/ask-enforce.js.map +1 -0
  33. package/dist/hooks.d.ts +8 -30
  34. package/dist/hooks.d.ts.map +1 -1
  35. package/dist/hooks.js +36 -116
  36. package/dist/hooks.js.map +1 -1
  37. package/dist/init.d.ts +2 -1
  38. package/dist/init.d.ts.map +1 -1
  39. package/dist/init.js +5 -14
  40. package/dist/init.js.map +1 -1
  41. package/dist/injection.d.ts +2 -2
  42. package/dist/injection.d.ts.map +1 -1
  43. package/dist/injection.js +108 -10
  44. package/dist/injection.js.map +1 -1
  45. package/dist/internal.d.ts +1 -1
  46. package/dist/internal.d.ts.map +1 -1
  47. package/dist/internal.js +1 -1
  48. package/dist/internal.js.map +1 -1
  49. package/dist/issue/create.d.ts +4 -0
  50. package/dist/issue/create.d.ts.map +1 -1
  51. package/dist/issue/create.js +12 -0
  52. package/dist/issue/create.js.map +1 -1
  53. package/dist/issue/index.d.ts +1 -1
  54. package/dist/issue/index.d.ts.map +1 -1
  55. package/dist/issue/index.js +1 -1
  56. package/dist/issue/index.js.map +1 -1
  57. package/dist/orchestrate/spawn-ops.d.ts.map +1 -1
  58. package/dist/orchestrate/spawn-ops.js +8 -2
  59. package/dist/orchestrate/spawn-ops.js.map +1 -1
  60. package/dist/orchestration/lean-change.d.ts +15 -0
  61. package/dist/orchestration/lean-change.d.ts.map +1 -0
  62. package/dist/orchestration/lean-change.js +17 -0
  63. package/dist/orchestration/lean-change.js.map +1 -0
  64. package/dist/orchestration/spawn-prompt.d.ts.map +1 -1
  65. package/dist/orchestration/spawn-prompt.js +6 -4
  66. package/dist/orchestration/spawn-prompt.js.map +1 -1
  67. package/dist/release/ci-evidence.d.ts +59 -0
  68. package/dist/release/ci-evidence.d.ts.map +1 -1
  69. package/dist/release/ci-evidence.js +150 -5
  70. package/dist/release/ci-evidence.js.map +1 -1
  71. package/dist/release/plan.d.ts.map +1 -1
  72. package/dist/release/plan.js +11 -2
  73. package/dist/release/plan.js.map +1 -1
  74. package/dist/release/pr-evidence.d.ts +15 -0
  75. package/dist/release/pr-evidence.d.ts.map +1 -1
  76. package/dist/release/pr-evidence.js +42 -1
  77. package/dist/release/pr-evidence.js.map +1 -1
  78. package/dist/release/reconcile.js +2 -2
  79. package/dist/release/reconcile.js.map +1 -1
  80. package/dist/release/verify-provenance.js +2 -2
  81. package/dist/release/verify-provenance.js.map +1 -1
  82. package/dist/resources/admission-ledger.d.ts +8 -0
  83. package/dist/resources/admission-ledger.d.ts.map +1 -1
  84. package/dist/resources/admission-ledger.js +5 -0
  85. package/dist/resources/admission-ledger.js.map +1 -1
  86. package/dist/resources/claim-keepalive.d.ts +34 -0
  87. package/dist/resources/claim-keepalive.d.ts.map +1 -0
  88. package/dist/resources/claim-keepalive.js +49 -0
  89. package/dist/resources/claim-keepalive.js.map +1 -0
  90. package/dist/resources/governor.d.ts.map +1 -1
  91. package/dist/resources/governor.js +3 -0
  92. package/dist/resources/governor.js.map +1 -1
  93. package/dist/scaffold/ensure-config.js +1 -1
  94. package/dist/scaffold/ensure-config.js.map +1 -1
  95. package/dist/store/attachment-store.d.ts.map +1 -1
  96. package/dist/store/attachment-store.js +2 -1
  97. package/dist/store/attachment-store.js.map +1 -1
  98. package/dist/store/restore-marker.d.ts +50 -7
  99. package/dist/store/restore-marker.d.ts.map +1 -1
  100. package/dist/store/restore-marker.js +77 -25
  101. package/dist/store/restore-marker.js.map +1 -1
  102. package/dist/store/row-identity-registry.d.ts +6 -4
  103. package/dist/store/row-identity-registry.d.ts.map +1 -1
  104. package/dist/store/row-identity-registry.js +156 -33
  105. package/dist/store/row-identity-registry.js.map +1 -1
  106. package/dist/store/schema/cleo-project/provenance-orphans.d.ts +16 -0
  107. package/dist/store/schema/cleo-project/provenance-orphans.d.ts.map +1 -1
  108. package/dist/store/schema/cleo-project/provenance-orphans.js +4 -1
  109. package/dist/store/schema/cleo-project/provenance-orphans.js.map +1 -1
  110. package/dist/store/schema/cleo-shared/brain.d.ts +240 -0
  111. package/dist/store/schema/cleo-shared/brain.d.ts.map +1 -1
  112. package/dist/store/schema/cleo-shared/brain.js +69 -0
  113. package/dist/store/schema/cleo-shared/brain.js.map +1 -1
  114. package/dist/store/schema/tasks.d.ts +1 -1
  115. package/dist/store/schema/tasks.d.ts.map +1 -1
  116. package/dist/store/schema/tasks.js +9 -1
  117. package/dist/store/schema/tasks.js.map +1 -1
  118. package/dist/store/sync/apply/applier.d.ts.map +1 -1
  119. package/dist/store/sync/apply/applier.js +85 -3
  120. package/dist/store/sync/apply/applier.js.map +1 -1
  121. package/dist/store/sync/apply/parent-delete.d.ts.map +1 -1
  122. package/dist/store/sync/apply/parent-delete.js +4 -0
  123. package/dist/store/sync/apply/parent-delete.js.map +1 -1
  124. package/dist/store/sync/capture.d.ts +8 -0
  125. package/dist/store/sync/capture.d.ts.map +1 -1
  126. package/dist/store/sync/capture.js +20 -0
  127. package/dist/store/sync/capture.js.map +1 -1
  128. package/dist/store/sync/clock-store.d.ts +21 -0
  129. package/dist/store/sync/clock-store.d.ts.map +1 -1
  130. package/dist/store/sync/clock-store.js +47 -0
  131. package/dist/store/sync/clock-store.js.map +1 -1
  132. package/dist/store/sync/conflicts.d.ts +13 -0
  133. package/dist/store/sync/conflicts.d.ts.map +1 -1
  134. package/dist/store/sync/conflicts.js +22 -0
  135. package/dist/store/sync/conflicts.js.map +1 -1
  136. package/dist/store/sync/genesis.d.ts +113 -12
  137. package/dist/store/sync/genesis.d.ts.map +1 -1
  138. package/dist/store/sync/genesis.js +325 -27
  139. package/dist/store/sync/genesis.js.map +1 -1
  140. package/dist/store/sync/merge/types.d.ts +8 -2
  141. package/dist/store/sync/merge/types.d.ts.map +1 -1
  142. package/dist/store/sync/pull.d.ts +192 -0
  143. package/dist/store/sync/pull.d.ts.map +1 -0
  144. package/dist/store/sync/pull.js +429 -0
  145. package/dist/store/sync/pull.js.map +1 -0
  146. package/dist/store/sync/push.d.ts +110 -0
  147. package/dist/store/sync/push.d.ts.map +1 -0
  148. package/dist/store/sync/push.js +172 -0
  149. package/dist/store/sync/push.js.map +1 -0
  150. package/dist/store/sync/replay-pin.d.ts +70 -0
  151. package/dist/store/sync/replay-pin.d.ts.map +1 -0
  152. package/dist/store/sync/replay-pin.js +172 -0
  153. package/dist/store/sync/replay-pin.js.map +1 -0
  154. package/dist/store/sync/sealer.d.ts.map +1 -1
  155. package/dist/store/sync/sealer.js +6 -2
  156. package/dist/store/sync/sealer.js.map +1 -1
  157. package/dist/store/sync/timestamp-canon.d.ts.map +1 -1
  158. package/dist/store/sync/timestamp-canon.js +15 -0
  159. package/dist/store/sync/timestamp-canon.js.map +1 -1
  160. package/dist/store/table-classification.d.ts.map +1 -1
  161. package/dist/store/table-classification.js +51 -15
  162. package/dist/store/table-classification.js.map +1 -1
  163. package/dist/store/tasks-schema.d.ts +1 -1
  164. package/dist/store/tasks-schema.d.ts.map +1 -1
  165. package/dist/store/tasks-schema.js +1 -1
  166. package/dist/store/tasks-schema.js.map +1 -1
  167. package/dist/store/twin-collapse.d.ts.map +1 -1
  168. package/dist/store/twin-collapse.js +5 -0
  169. package/dist/store/twin-collapse.js.map +1 -1
  170. package/dist/store/vault-manifest.d.ts +7 -0
  171. package/dist/store/vault-manifest.d.ts.map +1 -1
  172. package/dist/store/vault-manifest.js +4 -0
  173. package/dist/store/vault-manifest.js.map +1 -1
  174. package/dist/tasks/ac-table.d.ts +17 -0
  175. package/dist/tasks/ac-table.d.ts.map +1 -1
  176. package/dist/tasks/ac-table.js +49 -0
  177. package/dist/tasks/ac-table.js.map +1 -1
  178. package/dist/tasks/affected-scope.d.ts +52 -3
  179. package/dist/tasks/affected-scope.d.ts.map +1 -1
  180. package/dist/tasks/affected-scope.js +94 -3
  181. package/dist/tasks/affected-scope.js.map +1 -1
  182. package/dist/tasks/change-set.d.ts.map +1 -1
  183. package/dist/tasks/change-set.js +9 -5
  184. package/dist/tasks/change-set.js.map +1 -1
  185. package/dist/tasks/complete-ci.d.ts +2 -0
  186. package/dist/tasks/complete-ci.d.ts.map +1 -1
  187. package/dist/tasks/complete-ci.js +26 -7
  188. package/dist/tasks/complete-ci.js.map +1 -1
  189. package/dist/tasks/complete.d.ts.map +1 -1
  190. package/dist/tasks/complete.js +10 -0
  191. package/dist/tasks/complete.js.map +1 -1
  192. package/dist/tasks/done-plan.d.ts.map +1 -1
  193. package/dist/tasks/done-plan.js +63 -6
  194. package/dist/tasks/done-plan.js.map +1 -1
  195. package/dist/tasks/evidence.d.ts +3 -0
  196. package/dist/tasks/evidence.d.ts.map +1 -1
  197. package/dist/tasks/evidence.js +106 -2
  198. package/dist/tasks/evidence.js.map +1 -1
  199. package/dist/tasks/gate-preview.d.ts +19 -1
  200. package/dist/tasks/gate-preview.d.ts.map +1 -1
  201. package/dist/tasks/gate-preview.js +44 -2
  202. package/dist/tasks/gate-preview.js.map +1 -1
  203. package/dist/tasks/gate-runner.d.ts +16 -0
  204. package/dist/tasks/gate-runner.d.ts.map +1 -1
  205. package/dist/tasks/gate-runner.js +25 -2
  206. package/dist/tasks/gate-runner.js.map +1 -1
  207. package/dist/tasks/qa-run-binding.d.ts +88 -0
  208. package/dist/tasks/qa-run-binding.d.ts.map +1 -0
  209. package/dist/tasks/qa-run-binding.js +188 -0
  210. package/dist/tasks/qa-run-binding.js.map +1 -0
  211. package/dist/tasks/req.d.ts +50 -0
  212. package/dist/tasks/req.d.ts.map +1 -1
  213. package/dist/tasks/req.js +99 -1
  214. package/dist/tasks/req.js.map +1 -1
  215. package/dist/tasks/test-run-binding.d.ts +29 -0
  216. package/dist/tasks/test-run-binding.d.ts.map +1 -1
  217. package/dist/tasks/test-run-binding.js +47 -23
  218. package/dist/tasks/test-run-binding.js.map +1 -1
  219. package/dist/tasks/tool-resolver.d.ts +9 -0
  220. package/dist/tasks/tool-resolver.d.ts.map +1 -1
  221. package/dist/tasks/tool-resolver.js +14 -3
  222. package/dist/tasks/tool-resolver.js.map +1 -1
  223. package/dist/tasks/tool-semaphore.d.ts.map +1 -1
  224. package/dist/tasks/tool-semaphore.js +3 -0
  225. package/dist/tasks/tool-semaphore.js.map +1 -1
  226. package/dist/upgrade.d.ts.map +1 -1
  227. package/dist/upgrade.js +10 -1
  228. package/dist/upgrade.js.map +1 -1
  229. package/dist/validation/engine-ops.d.ts.map +1 -1
  230. package/dist/validation/engine-ops.js +14 -3
  231. package/dist/validation/engine-ops.js.map +1 -1
  232. package/migrations/drizzle-cleo-global/20261010130000_t12895-brain-natural-uids/migration.sql +28 -0
  233. package/migrations/drizzle-cleo-global/20261010140000_t12896-brain-autoinc-uids/migration.sql +54 -0
  234. package/migrations/drizzle-cleo-project/20261010130000_t12895-brain-natural-uids/migration.sql +36 -0
  235. package/migrations/drizzle-cleo-project/20261010140000_t12896-brain-autoinc-uids/migration.sql +58 -0
  236. package/migrations/sync-journal/20261008120000_t12343-pull-cursor/migration.sql +28 -0
  237. package/migrations/sync-journal/20261009120000_t13318-seen-floor/migration.sql +41 -0
  238. package/package.json +18 -18
  239. package/templates/CLEO-INJECTION.md +5 -3
  240. package/templates/CLEO-REFERENCE.md +1 -1
  241. package/templates/git-hooks/legacy-hashes.json +20 -0
  242. package/templates/git-hooks/pre-commit +1 -0
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Push one stream's journal (journal spec §2.8 "Persist before push",
3
+ * "Upload", §2.9, §1.3 "Local clock ahead"; T12343 S4-2).
4
+ *
5
+ * {@link pushStream} runs, in order:
6
+ * 1. the preconditions: `sync.push` on, the stream cut and its genesis
7
+ * checkpoint stored (`genesis_pending` clear), so no segment ever
8
+ * precedes the checkpoint it extends;
9
+ * 2. the local-clock check: the server's date is recorded for the sealer's
10
+ * clamp (`sync.server_clock`, read by `sealWallCeiling`); when this
11
+ * device's clock runs more than `MAX_DRIFT` ahead of it, push pauses
12
+ * (persisted as `sync.clock_ahead` for `cleo doctor`), so future-stamped
13
+ * HLCs do not spread; local writes are never refused;
14
+ * 3. the rollback check: when the server already holds a later segment from
15
+ * this replica than the store has persisted, the store is behind (a
16
+ * restore or a copy) and must rebind (§1.5, §2.9); nothing is sent;
17
+ * 4. seal everything pending, then pack and persist segments
18
+ * ({@link buildSegment}), advancing the device registry's high-water mark
19
+ * after each persist (the store ahead of the registry is the safe
20
+ * direction);
21
+ * 5. upload the persisted segments, lowest `replicaSeq` first, one at a time,
22
+ * always the exact persisted bytes; a duplicate counts as stored.
23
+ *
24
+ * The upload goes through a {@link SegmentUploader} port: the journal client
25
+ * in production (`cloud/nexus-vault.ts`), a fake in tests.
26
+ *
27
+ * @task T12343
28
+ * @module store/sync/push
29
+ */
30
+ import type { DatabaseSync } from 'node:sqlite';
31
+ import type { TableScope } from '@cleocode/contracts';
32
+ import type { ReplicaRegistry } from './replica-registry.js';
33
+ import { type BuildSegmentOptions, type PersistedSegment, type SegmentSealer } from './segments.js';
34
+ /** `MAX_DRIFT` (§1.3, Q6): how far this device's clock may run ahead of the server's. */
35
+ export declare const MAX_DRIFT_MS: number;
36
+ /** `_sync_meta` key set while push is paused for a clock ahead of the server's (`cleo doctor`). */
37
+ export declare const CLOCK_AHEAD_KEY = "sync.clock_ahead";
38
+ /** Uploads one persisted segment; resolves with the server's seq, `duplicate` when it was stored already. */
39
+ export type SegmentUploader = (segment: PersistedSegment) => Promise<{
40
+ readonly seq: number;
41
+ readonly duplicate: boolean;
42
+ }>;
43
+ /** Options for {@link pushStream}. */
44
+ export interface PushStreamOptions {
45
+ readonly scope: TableScope;
46
+ readonly stream: string;
47
+ /** This store's bound replica (the stream knows it by the same id). */
48
+ readonly replica: string;
49
+ /** The merge key's project, or null on a home stream. */
50
+ readonly project: string | null;
51
+ readonly sealer: SegmentSealer;
52
+ /** Signs each packed transaction for the stream ({@link BuildSegmentOptions.signTxn}). */
53
+ readonly signTxn: BuildSegmentOptions['signTxn'];
54
+ readonly upload: SegmentUploader;
55
+ /**
56
+ * The server's clock minus this device's, measured when its latest response
57
+ * arrived (negative: this device runs ahead), or null when unknown.
58
+ */
59
+ readonly serverOffsetMs: number | null;
60
+ /** The server's last stored `replicaSeq` for this replica on the stream, or null when unknown or none. */
61
+ readonly serverLastReplicaSeq: number | null;
62
+ /** The device registry, whose high-water mark advances after each persist. */
63
+ readonly registry?: ReplicaRegistry;
64
+ /** Wall clock. @defaultValue Date.now */
65
+ readonly now?: () => number;
66
+ readonly env?: NodeJS.ProcessEnv;
67
+ /** Seal and push although the flags are unreleased (tests and staging only). Never set from user input. */
68
+ readonly allowUnreleased?: boolean;
69
+ /** Override {@link MAX_DRIFT_MS} (tests). */
70
+ readonly maxDriftMs?: number;
71
+ }
72
+ /**
73
+ * Why {@link pushStream} sent nothing, as a kind callers branch on (the
74
+ * message in `refused` is for people and may be reworded):
75
+ * - `schema-missing`: the sync schema is not installed;
76
+ * - `push-off`: `sync.push` is off;
77
+ * - `no-genesis`: this store has no genesis cut on the stream;
78
+ * - `genesis-pending`: the genesis checkpoint is not stored yet;
79
+ * - `store-behind`: the server holds a later segment of this replica.
80
+ */
81
+ export type PushRefusal = 'schema-missing' | 'push-off' | 'no-genesis' | 'genesis-pending' | 'store-behind';
82
+ /** What {@link pushStream} did. */
83
+ export interface PushStreamReport {
84
+ readonly stream: string;
85
+ /** Why nothing was sent, or null. */
86
+ readonly refused: string | null;
87
+ /** The kind of `refused`, or null when nothing was refused. */
88
+ readonly refusedKind: PushRefusal | null;
89
+ /** Push paused because this device's clock is ahead of the server's. */
90
+ readonly clockAhead: boolean;
91
+ /** Transactions sealed, segments packed and persisted, segments the server stored (`duplicates` of them retries). */
92
+ readonly sealed: number;
93
+ readonly built: number;
94
+ readonly pushed: number;
95
+ readonly duplicates: number;
96
+ /** The server seq of the last segment stored, or null. */
97
+ readonly lastServerSeq: number | null;
98
+ }
99
+ /**
100
+ * Seal, persist and upload one stream's pending journal (module docs).
101
+ * Must run outside a transaction. An upload failure propagates after the
102
+ * segments before it were recorded; the next run resends from the lowest
103
+ * unpushed `replicaSeq`, with the same bytes.
104
+ *
105
+ * @param db - The store.
106
+ * @param o - {@link PushStreamOptions}.
107
+ * @returns What was sealed, persisted and stored, or why nothing was sent.
108
+ */
109
+ export declare function pushStream(db: DatabaseSync, o: PushStreamOptions): Promise<PushStreamReport>;
110
+ //# sourceMappingURL=push.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../src/store/sync/push.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAKtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7D,OAAO,EACL,KAAK,mBAAmB,EAGxB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAEnB,MAAM,eAAe,CAAC;AAEvB,yFAAyF;AACzF,eAAO,MAAM,YAAY,QAAgB,CAAC;AAK1C,mGAAmG;AACnG,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAElD,6GAA6G;AAC7G,MAAM,MAAM,eAAe,GAAG,CAC5B,OAAO,EAAE,gBAAgB,KACtB,OAAO,CAAC;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAEpE,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,0FAA0F;IAC1F,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,0GAA0G;IAC1G,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;IACpC,yCAAyC;IACzC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,2GAA2G;IAC3G,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,6CAA6C;IAC7C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GACnB,gBAAgB,GAChB,UAAU,GACV,YAAY,GACZ,iBAAiB,GACjB,cAAc,CAAC;AAEnB,mCAAmC;AACnC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IACzC,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,qHAAqH;IACrH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AA4BD;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,EAAE,EAAE,YAAY,EAChB,CAAC,EAAE,iBAAiB,GACnB,OAAO,CAAC,gBAAgB,CAAC,CAmG3B"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Push one stream's journal (journal spec §2.8 "Persist before push",
3
+ * "Upload", §2.9, §1.3 "Local clock ahead"; T12343 S4-2).
4
+ *
5
+ * {@link pushStream} runs, in order:
6
+ * 1. the preconditions: `sync.push` on, the stream cut and its genesis
7
+ * checkpoint stored (`genesis_pending` clear), so no segment ever
8
+ * precedes the checkpoint it extends;
9
+ * 2. the local-clock check: the server's date is recorded for the sealer's
10
+ * clamp (`sync.server_clock`, read by `sealWallCeiling`); when this
11
+ * device's clock runs more than `MAX_DRIFT` ahead of it, push pauses
12
+ * (persisted as `sync.clock_ahead` for `cleo doctor`), so future-stamped
13
+ * HLCs do not spread; local writes are never refused;
14
+ * 3. the rollback check: when the server already holds a later segment from
15
+ * this replica than the store has persisted, the store is behind (a
16
+ * restore or a copy) and must rebind (§1.5, §2.9); nothing is sent;
17
+ * 4. seal everything pending, then pack and persist segments
18
+ * ({@link buildSegment}), advancing the device registry's high-water mark
19
+ * after each persist (the store ahead of the registry is the safe
20
+ * direction);
21
+ * 5. upload the persisted segments, lowest `replicaSeq` first, one at a time,
22
+ * always the exact persisted bytes; a duplicate counts as stored.
23
+ *
24
+ * The upload goes through a {@link SegmentUploader} port: the journal client
25
+ * in production (`cloud/nexus-vault.ts`), a fake in tests.
26
+ *
27
+ * @task T12343
28
+ * @module store/sync/push
29
+ */
30
+ import { SERVER_CLOCK_KEY } from './clock-store.js';
31
+ import { isSyncFlagOn } from './flags.js';
32
+ import { genesisCutOf, genesisPending } from './genesis.js';
33
+ import { storeHwm } from './replica.js';
34
+ import { hasTable } from './schema.js';
35
+ import { sealPending } from './sealer.js';
36
+ import { buildSegment, markSegmentPushed, unpushedSegments, } from './segments.js';
37
+ /** `MAX_DRIFT` (§1.3, Q6): how far this device's clock may run ahead of the server's. */
38
+ export const MAX_DRIFT_MS = 5 * 60 * 1000;
39
+ /** Sealing passes per push before it sends what is persisted (the rest waits for the next run). */
40
+ const SEAL_PASSES = 8;
41
+ /** `_sync_meta` key set while push is paused for a clock ahead of the server's (`cleo doctor`). */
42
+ export const CLOCK_AHEAD_KEY = 'sync.clock_ahead';
43
+ const empty = (stream, fields) => ({
44
+ stream,
45
+ refused: null,
46
+ refusedKind: null,
47
+ clockAhead: false,
48
+ sealed: 0,
49
+ built: 0,
50
+ pushed: 0,
51
+ duplicates: 0,
52
+ lastServerSeq: null,
53
+ ...fields,
54
+ });
55
+ /** Set or clear {@link CLOCK_AHEAD_KEY}. */
56
+ /** Set (or, with null, clear) one of push's `_sync_meta` keys. */
57
+ function setPushMeta(db, key, value, atIso) {
58
+ if (value === null) {
59
+ db.prepare('DELETE FROM _sync_meta WHERE key = ?').run(key);
60
+ return;
61
+ }
62
+ db.prepare('INSERT INTO _sync_meta (key, value, updated_at) VALUES (?, ?, ?) ' +
63
+ 'ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at').run(key, value, atIso);
64
+ }
65
+ /**
66
+ * Seal, persist and upload one stream's pending journal (module docs).
67
+ * Must run outside a transaction. An upload failure propagates after the
68
+ * segments before it were recorded; the next run resends from the lowest
69
+ * unpushed `replicaSeq`, with the same bytes.
70
+ *
71
+ * @param db - The store.
72
+ * @param o - {@link PushStreamOptions}.
73
+ * @returns What was sealed, persisted and stored, or why nothing was sent.
74
+ */
75
+ export async function pushStream(db, o) {
76
+ const env = o.env ?? process.env;
77
+ const now = o.now ?? Date.now;
78
+ if (!hasTable(db, '_sync_segment'))
79
+ return empty(o.stream, {
80
+ refused: 'sync schema not installed',
81
+ refusedKind: 'schema-missing',
82
+ });
83
+ if (!isSyncFlagOn(db, 'sync.push', env))
84
+ return empty(o.stream, { refused: 'sync.push is off', refusedKind: 'push-off' });
85
+ if (genesisCutOf(db, o.stream) === undefined) {
86
+ return empty(o.stream, {
87
+ refused: `${o.stream} has no genesis cut on this store: run \`cleo sync enable push\``,
88
+ refusedKind: 'no-genesis',
89
+ });
90
+ }
91
+ if (genesisPending(db, o.stream)) {
92
+ return empty(o.stream, {
93
+ refused: `${o.stream}'s genesis checkpoint is not stored yet: run \`cleo sync enable push\` again`,
94
+ refusedKind: 'genesis-pending',
95
+ });
96
+ }
97
+ const nowMs = now();
98
+ const atIso = new Date(nowMs).toISOString();
99
+ // §1.3: the observed server date bounds what the sealer stamps
100
+ // (sealWallCeiling), and a clock running ahead of it pauses push, never writes.
101
+ if (o.serverOffsetMs !== null) {
102
+ setPushMeta(db, SERVER_CLOCK_KEY, JSON.stringify({ offsetMs: o.serverOffsetMs, atMs: nowMs }), atIso);
103
+ }
104
+ const drift = o.serverOffsetMs === null ? 0 : -o.serverOffsetMs;
105
+ if (drift > (o.maxDriftMs ?? MAX_DRIFT_MS)) {
106
+ setPushMeta(db, CLOCK_AHEAD_KEY, JSON.stringify({ driftMs: drift }), atIso);
107
+ return empty(o.stream, { clockAhead: true });
108
+ }
109
+ setPushMeta(db, CLOCK_AHEAD_KEY, null, atIso);
110
+ // §1.5 / §2.9: the server is authoritative. A later segment there than this
111
+ // store ever persisted means the store was restored or copied behind it.
112
+ const local = storeHwm(db, o.replica)[o.stream] ?? null;
113
+ if (o.serverLastReplicaSeq !== null && (local === null || o.serverLastReplicaSeq > local)) {
114
+ return empty(o.stream, {
115
+ refused: `the server holds replicaSeq ${o.serverLastReplicaSeq} of replica ${o.replica} on ${o.stream}, but this store persisted only ${local ?? 'none'}: the store is behind (restored or copied) and must rebind to a new replica (T12753)`,
116
+ refusedKind: 'store-behind',
117
+ });
118
+ }
119
+ let sealed = 0;
120
+ // Bounded: steady concurrent writers never keep the push from sending; the
121
+ // next run seals the rest.
122
+ for (let pass = 0; pass < SEAL_PASSES; pass++) {
123
+ const r = sealPending(db, {
124
+ scope: o.scope,
125
+ replica: o.replica,
126
+ now,
127
+ env,
128
+ ...(o.allowUnreleased ? { allowUnreleased: true } : {}),
129
+ });
130
+ sealed += r.txns;
131
+ if (r.refused !== null || r.captures === 0 || r.pending.length > 0)
132
+ break;
133
+ }
134
+ let built = 0;
135
+ for (;;) {
136
+ const seg = buildSegment(db, {
137
+ stream: o.stream,
138
+ replica: o.replica,
139
+ scope: o.scope,
140
+ project: o.project,
141
+ sealer: o.sealer,
142
+ signTxn: o.signTxn,
143
+ nowIso: new Date(now()).toISOString(),
144
+ });
145
+ if (seg === null)
146
+ break;
147
+ built += 1;
148
+ // §2.8: the registry follows the store; a crash before this leaves the
149
+ // store ahead of it, the safe direction.
150
+ if (o.registry?.get(o.replica))
151
+ o.registry.advanceHwm(o.replica, o.stream, seg.replicaSeq);
152
+ }
153
+ let pushed = 0;
154
+ let duplicates = 0;
155
+ let lastServerSeq = null;
156
+ for (const seg of unpushedSegments(db, o.stream, o.replica)) {
157
+ const stored = await o.upload(seg);
158
+ markSegmentPushed(db, {
159
+ stream: o.stream,
160
+ replica: o.replica,
161
+ replicaSeq: seg.replicaSeq,
162
+ serverSeq: stored.seq,
163
+ nowIso: new Date(now()).toISOString(),
164
+ });
165
+ pushed += 1;
166
+ if (stored.duplicate)
167
+ duplicates += 1;
168
+ lastServerSeq = stored.seq;
169
+ }
170
+ return empty(o.stream, { sealed, built, pushed, duplicates, lastServerSeq });
171
+ }
172
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../../src/store/sync/push.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The replay pin of a checkpoint (journal spec §2.11 §7; NEW-9, R5-8,
3
+ * T12797): what an endorsing device must replay under to recompute the
4
+ * checkpoint's tallies.
5
+ *
6
+ * - `journal`: sha256 over the store's WHOLE ordered migration journal,
7
+ * `(created_at, name, hash)` per applied migration, in drizzle's order
8
+ * `(created_at, name)`. A single head is not enough: stamped and drifted
9
+ * stores differ below it.
10
+ * - `triggerSetHash`: sha256 over the normalized DDL of the store's OWNED
11
+ * triggers ({@link OWNED_TRIGGERS}: the guard and side-effect triggers a
12
+ * replay runs under), sorted by name. Device-local maintenance triggers
13
+ * (the twin-collapse docs freeze, whose message names the build that froze
14
+ * that store, and the legacy track triggers) never touch a replayed table,
15
+ * so they stay out: two devices with the same replay semantics agree
16
+ * (T13298). Normalization is literal-safe ({@link normalizeTriggerDdl}).
17
+ * - `transitions`: the schema rise points inside the checkpoint's window;
18
+ * the caller computes them from the segments it replayed (empty for a
19
+ * genesis checkpoint, which has no window to replay).
20
+ *
21
+ * The server cannot check the two hashes; another device recomputes them
22
+ * from its own store and requires a match before it endorses.
23
+ *
24
+ * @task T12343
25
+ * @module store/sync/replay-pin
26
+ */
27
+ import type { DatabaseSync } from 'node:sqlite';
28
+ import type { ReplayPin } from '@cleocode/contracts/cloud';
29
+ /**
30
+ * Normalize trigger DDL for hashing, literal-safely (§2.11 §7): comments are
31
+ * dropped; outside string literals and quoted identifiers, whitespace runs
32
+ * collapse to one space (and vanish next to punctuation) and words are
33
+ * lower-cased; a string literal is kept byte for byte (`'Done'` stays
34
+ * `'Done'`); every quoted identifier (`"x"`, `` `x` ``, `[x]`) is written
35
+ * `"x"`, and a bare identifier stays bare (SQLite treats both alike for the
36
+ * names a trigger uses, so the two spellings of one trigger hash alike only
37
+ * when quoting is unified this way).
38
+ *
39
+ * @param sql - The trigger's `CREATE TRIGGER` text.
40
+ * @returns The normalized text.
41
+ */
42
+ export declare function normalizeTriggerDdl(sql: string): string;
43
+ /**
44
+ * sha256 of the store's whole ordered migration journal (§2.11 §7): every
45
+ * `__drizzle_migrations` row as `[created_at, name, hash]`, ordered by
46
+ * `(created_at, name)`. A store without the journal hashes an empty list.
47
+ *
48
+ * @param db - The store.
49
+ * @returns The hex digest.
50
+ */
51
+ export declare function migrationJournalHash(db: DatabaseSync): string;
52
+ /**
53
+ * sha256 over the normalized DDL of every owned trigger present in the store
54
+ * ({@link OWNED_TRIGGERS}), sorted by name (§2.11 §7; T13298). Capture
55
+ * triggers (generated per build) and device-local maintenance triggers are
56
+ * excluded: they never run in a replay.
57
+ *
58
+ * @param db - The store.
59
+ * @returns The hex digest.
60
+ */
61
+ export declare function triggerSetHash(db: DatabaseSync): string;
62
+ /**
63
+ * A checkpoint's replay pin, from the store it snapshots (§2.11 §7).
64
+ *
65
+ * @param db - The store (or the snapshot of it the checkpoint carries).
66
+ * @param transitions - The window's schema rise points (empty for genesis).
67
+ * @returns The pin.
68
+ */
69
+ export declare function replayPinOf(db: DatabaseSync, transitions?: ReplayPin['transitions']): ReplayPin;
70
+ //# sourceMappingURL=replay-pin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-pin.d.ts","sourceRoot":"","sources":["../../../src/store/sync/replay-pin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAO3D;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAgEvD;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,YAAY,GAAG,MAAM,CAa7D;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,YAAY,GAAG,MAAM,CAOvD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,YAAY,EAChB,WAAW,GAAE,SAAS,CAAC,aAAa,CAAM,GACzC,SAAS,CAMX"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * The replay pin of a checkpoint (journal spec §2.11 §7; NEW-9, R5-8,
3
+ * T12797): what an endorsing device must replay under to recompute the
4
+ * checkpoint's tallies.
5
+ *
6
+ * - `journal`: sha256 over the store's WHOLE ordered migration journal,
7
+ * `(created_at, name, hash)` per applied migration, in drizzle's order
8
+ * `(created_at, name)`. A single head is not enough: stamped and drifted
9
+ * stores differ below it.
10
+ * - `triggerSetHash`: sha256 over the normalized DDL of the store's OWNED
11
+ * triggers ({@link OWNED_TRIGGERS}: the guard and side-effect triggers a
12
+ * replay runs under), sorted by name. Device-local maintenance triggers
13
+ * (the twin-collapse docs freeze, whose message names the build that froze
14
+ * that store, and the legacy track triggers) never touch a replayed table,
15
+ * so they stay out: two devices with the same replay semantics agree
16
+ * (T13298). Normalization is literal-safe ({@link normalizeTriggerDdl}).
17
+ * - `transitions`: the schema rise points inside the checkpoint's window;
18
+ * the caller computes them from the segments it replayed (empty for a
19
+ * genesis checkpoint, which has no window to replay).
20
+ *
21
+ * The server cannot check the two hashes; another device recomputes them
22
+ * from its own store and requires a match before it endorses.
23
+ *
24
+ * @task T12343
25
+ * @module store/sync/replay-pin
26
+ */
27
+ import { createHash } from 'node:crypto';
28
+ import { hasTable } from './schema.js';
29
+ import { canonicalJson } from './sealer-values.js';
30
+ import { OWNED_TRIGGERS } from './trigger-classes.js';
31
+ const sha256Hex = (s) => createHash('sha256').update(s, 'utf8').digest('hex');
32
+ /**
33
+ * Normalize trigger DDL for hashing, literal-safely (§2.11 §7): comments are
34
+ * dropped; outside string literals and quoted identifiers, whitespace runs
35
+ * collapse to one space (and vanish next to punctuation) and words are
36
+ * lower-cased; a string literal is kept byte for byte (`'Done'` stays
37
+ * `'Done'`); every quoted identifier (`"x"`, `` `x` ``, `[x]`) is written
38
+ * `"x"`, and a bare identifier stays bare (SQLite treats both alike for the
39
+ * names a trigger uses, so the two spellings of one trigger hash alike only
40
+ * when quoting is unified this way).
41
+ *
42
+ * @param sql - The trigger's `CREATE TRIGGER` text.
43
+ * @returns The normalized text.
44
+ */
45
+ export function normalizeTriggerDdl(sql) {
46
+ const out = [];
47
+ let i = 0;
48
+ let space = false;
49
+ const push = (tok, word) => {
50
+ const prev = out[out.length - 1];
51
+ // One space between two word-like tokens; none around punctuation.
52
+ if (space && prev !== undefined && word && /[\w"'\]`]$/.test(prev))
53
+ out.push(' ');
54
+ out.push(tok);
55
+ space = false;
56
+ };
57
+ while (i < sql.length) {
58
+ const c = sql[i];
59
+ if (/\s/.test(c)) {
60
+ space = true;
61
+ i += 1;
62
+ }
63
+ else if (sql.startsWith('--', i)) {
64
+ const end = sql.indexOf('\n', i);
65
+ i = end === -1 ? sql.length : end;
66
+ space = true;
67
+ }
68
+ else if (sql.startsWith('/*', i)) {
69
+ const end = sql.indexOf('*/', i + 2);
70
+ i = end === -1 ? sql.length : end + 2;
71
+ space = true;
72
+ }
73
+ else if (c === "'") {
74
+ let j = i + 1;
75
+ for (;;) {
76
+ if (j >= sql.length)
77
+ break;
78
+ if (sql[j] === "'") {
79
+ if (sql[j + 1] === "'")
80
+ j += 2;
81
+ else
82
+ break;
83
+ }
84
+ else
85
+ j += 1;
86
+ }
87
+ push(sql.slice(i, j + 1), true);
88
+ i = j + 1;
89
+ }
90
+ else if (c === '"' || c === '`' || c === '[') {
91
+ const close = c === '[' ? ']' : c;
92
+ let j = i + 1;
93
+ let name = '';
94
+ for (;;) {
95
+ if (j >= sql.length)
96
+ break;
97
+ if (sql[j] === close) {
98
+ if (close !== ']' && sql[j + 1] === close) {
99
+ name += close;
100
+ j += 2;
101
+ }
102
+ else
103
+ break;
104
+ }
105
+ else {
106
+ name += sql[j];
107
+ j += 1;
108
+ }
109
+ }
110
+ push(`"${name.replaceAll('"', '""')}"`, true);
111
+ i = j + 1;
112
+ }
113
+ else if (/[\w$]/.test(c)) {
114
+ let j = i;
115
+ while (j < sql.length && /[\w$]/.test(sql[j]))
116
+ j += 1;
117
+ push(sql.slice(i, j).toLowerCase(), true);
118
+ i = j;
119
+ }
120
+ else {
121
+ push(c, false);
122
+ i += 1;
123
+ }
124
+ }
125
+ return out.join('').replace(/;$/, '');
126
+ }
127
+ /**
128
+ * sha256 of the store's whole ordered migration journal (§2.11 §7): every
129
+ * `__drizzle_migrations` row as `[created_at, name, hash]`, ordered by
130
+ * `(created_at, name)`. A store without the journal hashes an empty list.
131
+ *
132
+ * @param db - The store.
133
+ * @returns The hex digest.
134
+ */
135
+ export function migrationJournalHash(db) {
136
+ const rows = hasTable(db, '__drizzle_migrations')
137
+ ? db
138
+ .prepare('SELECT created_at, name, hash FROM main."__drizzle_migrations" ORDER BY created_at, name')
139
+ .all()
140
+ : [];
141
+ return sha256Hex(canonicalJson(rows.map((r) => [r.created_at === null ? null : Number(r.created_at), r.name, r.hash])));
142
+ }
143
+ /**
144
+ * sha256 over the normalized DDL of every owned trigger present in the store
145
+ * ({@link OWNED_TRIGGERS}), sorted by name (§2.11 §7; T13298). Capture
146
+ * triggers (generated per build) and device-local maintenance triggers are
147
+ * excluded: they never run in a replay.
148
+ *
149
+ * @param db - The store.
150
+ * @returns The hex digest.
151
+ */
152
+ export function triggerSetHash(db) {
153
+ const rows = db
154
+ .prepare("SELECT name, sql FROM main.sqlite_master WHERE type = 'trigger' ORDER BY name")
155
+ .all().filter((r) => Object.hasOwn(OWNED_TRIGGERS, r.name) && r.sql !== null);
156
+ return sha256Hex(canonicalJson(rows.map((r) => [r.name, normalizeTriggerDdl(r.sql)])));
157
+ }
158
+ /**
159
+ * A checkpoint's replay pin, from the store it snapshots (§2.11 §7).
160
+ *
161
+ * @param db - The store (or the snapshot of it the checkpoint carries).
162
+ * @param transitions - The window's schema rise points (empty for genesis).
163
+ * @returns The pin.
164
+ */
165
+ export function replayPinOf(db, transitions = []) {
166
+ return {
167
+ journal: migrationJournalHash(db),
168
+ triggerSetHash: triggerSetHash(db),
169
+ transitions: [...transitions],
170
+ };
171
+ }
172
+ //# sourceMappingURL=replay-pin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-pin.js","sourceRoot":"","sources":["../../../src/store/sync/replay-pin.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"sealer.d.ts","sourceRoot":"","sources":["../../../src/store/sync/sealer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sealer.d.ts","sourceRoot":"","sources":["../../../src/store/sync/sealer.ts"],"names":[],"mappings":"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"}
@@ -50,7 +50,7 @@ import { naturalRowUid } from '../row-identity.js';
50
50
  import { BIRTH_FP_COLUMN, rowIdentitySpec, UID_COLUMN } from '../row-identity-registry.js';
51
51
  import { loadFrameIntents, subtractApplyIntents } from './apply-intent.js';
52
52
  import { captureTableDef, chunkedObject, enc, SECRET_MARKER, syncSetTables, } from './capture.js';
53
- import { tickClock, withImmediateTransaction } from './clock-store.js';
53
+ import { sealWallCeiling, tickClock, withImmediateTransaction } from './clock-store.js';
54
54
  import { actorOpOf, clearFieldLeaves, localFrontierUpdates, localLeaves, recordFieldLeaves, setFieldFrontiers, } from './field-leave.js';
55
55
  import { isSyncFlagOn, legacyStrandRemedy, legacyStrandsCached, UNRELEASED_FLAGS, } from './flags.js';
56
56
  import { mergeGroupsOf } from './merge/rules.js';
@@ -887,6 +887,10 @@ function sealInTransaction(db, opts, budget, now) {
887
887
  const started = performance.now();
888
888
  const maxMs = opts.maxMs ?? 200;
889
889
  const replica = opts.replica;
890
+ // §1.3: a capture stamped while this clock ran ahead seals no later than
891
+ // the server's date plus MAX_DRIFT, once a recent server date is known.
892
+ const ceiling = sealWallCeiling(db, now());
893
+ const wall = (ms) => (ceiling === null ? ms : Math.min(ms, ceiling));
890
894
  // 1. Read a batch. A frame cut by the budget is completed only when it is
891
895
  // the batch's first group (it must seal whole); otherwise it is left for
892
896
  // the next batch, so the extension stays bounded (T13032).
@@ -1118,7 +1122,7 @@ function sealInTransaction(db, opts, budget, now) {
1118
1122
  ...rest,
1119
1123
  seq,
1120
1124
  last,
1121
- h: tickClock(db, replica, at(op)?.at_ms ?? now()),
1125
+ h: tickClock(db, replica, wall(at(op)?.at_ms ?? now())),
1122
1126
  };
1123
1127
  });
1124
1128
  const txnHlc = sealedOps.reduce((m, o) => (o.h > m ? o.h : m), sealedOps[0]?.h ?? '');