@cleocode/core 2026.9.25 → 2026.9.26

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 (208) hide show
  1. package/dist/admin/import-tasks.d.ts.map +1 -1
  2. package/dist/admin/import-tasks.js +5 -2
  3. package/dist/admin/import-tasks.js.map +1 -1
  4. package/dist/admin/import.d.ts.map +1 -1
  5. package/dist/admin/import.js +7 -2
  6. package/dist/admin/import.js.map +1 -1
  7. package/dist/build-provenance.json +2 -2
  8. package/dist/cloud/index.d.ts +1 -0
  9. package/dist/cloud/index.d.ts.map +1 -1
  10. package/dist/cloud/index.js +1 -0
  11. package/dist/cloud/index.js.map +1 -1
  12. package/dist/cloud/nexus-auth.d.ts +26 -1
  13. package/dist/cloud/nexus-auth.d.ts.map +1 -1
  14. package/dist/cloud/nexus-auth.js +61 -32
  15. package/dist/cloud/nexus-auth.js.map +1 -1
  16. package/dist/cloud/nexus-credentials.js +2 -2
  17. package/dist/cloud/nexus-credentials.js.map +1 -1
  18. package/dist/cloud/nexus-device.d.ts +809 -0
  19. package/dist/cloud/nexus-device.d.ts.map +1 -0
  20. package/dist/cloud/nexus-device.js +1680 -0
  21. package/dist/cloud/nexus-device.js.map +1 -0
  22. package/dist/cloud/nexus-enrol.d.ts +210 -0
  23. package/dist/cloud/nexus-enrol.d.ts.map +1 -0
  24. package/dist/cloud/nexus-enrol.js +1023 -0
  25. package/dist/cloud/nexus-enrol.js.map +1 -0
  26. package/dist/cloud/nexus-link.d.ts +3 -0
  27. package/dist/cloud/nexus-link.d.ts.map +1 -1
  28. package/dist/cloud/nexus-link.js +38 -4
  29. package/dist/cloud/nexus-link.js.map +1 -1
  30. package/dist/cloud/signing.d.ts +20 -0
  31. package/dist/cloud/signing.d.ts.map +1 -1
  32. package/dist/cloud/signing.js +14 -0
  33. package/dist/cloud/signing.js.map +1 -1
  34. package/dist/crypto/credentials.d.ts +61 -0
  35. package/dist/crypto/credentials.d.ts.map +1 -1
  36. package/dist/crypto/credentials.js +154 -58
  37. package/dist/crypto/credentials.js.map +1 -1
  38. package/dist/docs/export-document.js +830 -190
  39. package/dist/docs/export-document.js.map +3 -3
  40. package/dist/doctor/dependency-cycles.d.ts +41 -0
  41. package/dist/doctor/dependency-cycles.d.ts.map +1 -0
  42. package/dist/doctor/dependency-cycles.js +39 -0
  43. package/dist/doctor/dependency-cycles.js.map +1 -0
  44. package/dist/doctor/index.d.ts +2 -0
  45. package/dist/doctor/index.d.ts.map +1 -1
  46. package/dist/doctor/index.js +1 -0
  47. package/dist/doctor/index.js.map +1 -1
  48. package/dist/doctor/row-identity.d.ts +11 -0
  49. package/dist/doctor/row-identity.d.ts.map +1 -1
  50. package/dist/doctor/row-identity.js +67 -9
  51. package/dist/doctor/row-identity.js.map +1 -1
  52. package/dist/error-catalog.d.ts.map +1 -1
  53. package/dist/error-catalog.js +4 -0
  54. package/dist/error-catalog.js.map +1 -1
  55. package/dist/error-registry.d.ts.map +1 -1
  56. package/dist/error-registry.js +8 -0
  57. package/dist/error-registry.js.map +1 -1
  58. package/dist/llm/plugin-facade.js +857 -217
  59. package/dist/llm/plugin-facade.js.map +3 -3
  60. package/dist/nexus/workspace.d.ts.map +1 -1
  61. package/dist/nexus/workspace.js +54 -4
  62. package/dist/nexus/workspace.js.map +1 -1
  63. package/dist/orchestrate/lifecycle-ops.js +1 -1
  64. package/dist/orchestrate/lifecycle-ops.js.map +1 -1
  65. package/dist/orchestrate/pivot.d.ts.map +1 -1
  66. package/dist/orchestrate/pivot.js +4 -2
  67. package/dist/orchestrate/pivot.js.map +1 -1
  68. package/dist/orchestrate/query-ops.d.ts.map +1 -1
  69. package/dist/orchestrate/query-ops.js +3 -3
  70. package/dist/orchestrate/query-ops.js.map +1 -1
  71. package/dist/orchestrate/spawn-ops.js +1 -1
  72. package/dist/orchestrate/spawn-ops.js.map +1 -1
  73. package/dist/orchestration/index.js +1 -1
  74. package/dist/orchestration/index.js.map +1 -1
  75. package/dist/orchestration/lead-rollup.d.ts.map +1 -1
  76. package/dist/orchestration/lead-rollup.js +3 -3
  77. package/dist/orchestration/lead-rollup.js.map +1 -1
  78. package/dist/orchestration/parallel.js +1 -1
  79. package/dist/orchestration/parallel.js.map +1 -1
  80. package/dist/orchestration/wave-topic.d.ts +4 -2
  81. package/dist/orchestration/wave-topic.d.ts.map +1 -1
  82. package/dist/orchestration/wave-topic.js +6 -4
  83. package/dist/orchestration/wave-topic.js.map +1 -1
  84. package/dist/orchestration/waves.d.ts +2 -1
  85. package/dist/orchestration/waves.d.ts.map +1 -1
  86. package/dist/orchestration/waves.js +4 -3
  87. package/dist/orchestration/waves.js.map +1 -1
  88. package/dist/phases/index.d.ts.map +1 -1
  89. package/dist/phases/index.js +4 -2
  90. package/dist/phases/index.js.map +1 -1
  91. package/dist/selfimprove/fix-gen.js +856 -216
  92. package/dist/selfimprove/fix-gen.js.map +3 -3
  93. package/dist/session/engine-ops.d.ts.map +1 -1
  94. package/dist/session/engine-ops.js +77 -49
  95. package/dist/session/engine-ops.js.map +1 -1
  96. package/dist/sessions/focus-state-store.d.ts +69 -23
  97. package/dist/sessions/focus-state-store.d.ts.map +1 -1
  98. package/dist/sessions/focus-state-store.js +113 -27
  99. package/dist/sessions/focus-state-store.js.map +1 -1
  100. package/dist/sessions/handoff.js +1 -1
  101. package/dist/sessions/handoff.js.map +1 -1
  102. package/dist/sessions/index.d.ts.map +1 -1
  103. package/dist/sessions/index.js +24 -13
  104. package/dist/sessions/index.js.map +1 -1
  105. package/dist/sessions/terminal-identity.d.ts +165 -17
  106. package/dist/sessions/terminal-identity.d.ts.map +1 -1
  107. package/dist/sessions/terminal-identity.js +341 -32
  108. package/dist/sessions/terminal-identity.js.map +1 -1
  109. package/dist/skills/orchestrator/startup.js +2 -2
  110. package/dist/skills/orchestrator/startup.js.map +1 -1
  111. package/dist/snapshot/index.d.ts +4 -0
  112. package/dist/snapshot/index.d.ts.map +1 -1
  113. package/dist/snapshot/index.js +50 -3
  114. package/dist/snapshot/index.js.map +1 -1
  115. package/dist/store/db-helpers.d.ts +15 -2
  116. package/dist/store/db-helpers.d.ts.map +1 -1
  117. package/dist/store/db-helpers.js +84 -3
  118. package/dist/store/db-helpers.js.map +1 -1
  119. package/dist/store/dependency-cycles.d.ts +164 -0
  120. package/dist/store/dependency-cycles.d.ts.map +1 -0
  121. package/dist/store/dependency-cycles.js +360 -0
  122. package/dist/store/dependency-cycles.js.map +1 -0
  123. package/dist/store/display-id-alias.d.ts +93 -37
  124. package/dist/store/display-id-alias.d.ts.map +1 -1
  125. package/dist/store/display-id-alias.js +590 -115
  126. package/dist/store/display-id-alias.js.map +1 -1
  127. package/dist/store/dual-scope-db.d.ts.map +1 -1
  128. package/dist/store/dual-scope-db.js +34 -7
  129. package/dist/store/dual-scope-db.js.map +1 -1
  130. package/dist/store/exodus/migrate.d.ts.map +1 -1
  131. package/dist/store/exodus/migrate.js +8 -1
  132. package/dist/store/exodus/migrate.js.map +1 -1
  133. package/dist/store/global-salt.d.ts +35 -0
  134. package/dist/store/global-salt.d.ts.map +1 -1
  135. package/dist/store/global-salt.js +136 -16
  136. package/dist/store/global-salt.js.map +1 -1
  137. package/dist/store/migration-sqlite.d.ts.map +1 -1
  138. package/dist/store/migration-sqlite.js +19 -11
  139. package/dist/store/migration-sqlite.js.map +1 -1
  140. package/dist/store/portable-bundle-scan.d.ts.map +1 -1
  141. package/dist/store/portable-bundle-scan.js +6 -0
  142. package/dist/store/portable-bundle-scan.js.map +1 -1
  143. package/dist/store/row-identity-registry.d.ts.map +1 -1
  144. package/dist/store/row-identity-registry.js +11 -3
  145. package/dist/store/row-identity-registry.js.map +1 -1
  146. package/dist/store/row-identity.d.ts +91 -1
  147. package/dist/store/row-identity.d.ts.map +1 -1
  148. package/dist/store/row-identity.js +276 -7
  149. package/dist/store/row-identity.js.map +1 -1
  150. package/dist/store/safety-data-accessor.d.ts +3 -2
  151. package/dist/store/safety-data-accessor.d.ts.map +1 -1
  152. package/dist/store/safety-data-accessor.js +5 -2
  153. package/dist/store/safety-data-accessor.js.map +1 -1
  154. package/dist/store/session-store.d.ts +63 -3
  155. package/dist/store/session-store.d.ts.map +1 -1
  156. package/dist/store/session-store.js +121 -8
  157. package/dist/store/session-store.js.map +1 -1
  158. package/dist/store/split-brain-import.d.ts.map +1 -1
  159. package/dist/store/split-brain-import.js +36 -0
  160. package/dist/store/split-brain-import.js.map +1 -1
  161. package/dist/store/sqlite-data-accessor.d.ts +50 -3
  162. package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
  163. package/dist/store/sqlite-data-accessor.js +246 -13
  164. package/dist/store/sqlite-data-accessor.js.map +1 -1
  165. package/dist/store/tasks-sqlite.d.ts.map +1 -1
  166. package/dist/store/tasks-sqlite.js +14 -2
  167. package/dist/store/tasks-sqlite.js.map +1 -1
  168. package/dist/store/umbrella-data-accessor.d.ts +3 -2
  169. package/dist/store/umbrella-data-accessor.d.ts.map +1 -1
  170. package/dist/store/umbrella-data-accessor.js +5 -2
  171. package/dist/store/umbrella-data-accessor.js.map +1 -1
  172. package/dist/system/health.d.ts +2 -0
  173. package/dist/system/health.d.ts.map +1 -1
  174. package/dist/system/health.js +54 -0
  175. package/dist/system/health.js.map +1 -1
  176. package/dist/task-work/index.d.ts.map +1 -1
  177. package/dist/task-work/index.js +9 -6
  178. package/dist/task-work/index.js.map +1 -1
  179. package/dist/tasks/analyze.d.ts.map +1 -1
  180. package/dist/tasks/analyze.js +3 -2
  181. package/dist/tasks/analyze.js.map +1 -1
  182. package/dist/tasks/evidence.d.ts +19 -1
  183. package/dist/tasks/evidence.d.ts.map +1 -1
  184. package/dist/tasks/evidence.js +79 -18
  185. package/dist/tasks/evidence.js.map +1 -1
  186. package/dist/tasks/infer-add-params.js +1 -1
  187. package/dist/tasks/infer-add-params.js.map +1 -1
  188. package/dist/tasks/task-context.d.ts.map +1 -1
  189. package/dist/tasks/task-context.js +3 -1
  190. package/dist/tasks/task-context.js.map +1 -1
  191. package/dist/tasks/task-import.d.ts.map +1 -1
  192. package/dist/tasks/task-import.js +11 -3
  193. package/dist/tasks/task-import.js.map +1 -1
  194. package/dist/tasks/task-next.d.ts +5 -1
  195. package/dist/tasks/task-next.d.ts.map +1 -1
  196. package/dist/tasks/task-next.js +7 -2
  197. package/dist/tasks/task-next.js.map +1 -1
  198. package/dist/validation/engine-ops.d.ts.map +1 -1
  199. package/dist/validation/engine-ops.js +3 -2
  200. package/dist/validation/engine-ops.js.map +1 -1
  201. package/dist/workgraph/scaffold-apply.d.ts.map +1 -1
  202. package/dist/workgraph/scaffold-apply.js +14 -5
  203. package/dist/workgraph/scaffold-apply.js.map +1 -1
  204. package/migrations/drizzle-cleo-project/20260930160000_t12886-dependency-cycle-guard/migration.sql +76 -0
  205. package/migrations/drizzle-cleo-project/20260930160000_t12886-dependency-cycle-guard/revert.sql +7 -0
  206. package/package.json +13 -13
  207. package/templates/CLEO-INJECTION.md +2 -2
  208. package/templates/CLEO-REFERENCE.md +1 -1
@@ -325,7 +325,28 @@ export declare function installRowUidTriggers(db: DatabaseSync, scope: TableScop
325
325
  * {@link ROW_IDENTITY_RECIPE_KEY} by the first fill; bumped by any change to a
326
326
  * recipe, a frozen content or birth-fact list, or the encoding.
327
327
  */
328
- export declare const ROW_IDENTITY_RECIPE = "cleo/row-identity/v1";
328
+ export declare const ROW_IDENTITY_RECIPE = "cleo/row-identity/v2";
329
+ /**
330
+ * The recipe 9.25 shipped (v1). v2 changed only the birth facts of AC history
331
+ * and evidence bindings (@refFp:ac_uid and ac_text_hash → none / ac_id,
332
+ * T12802); a store filled
333
+ * with v1 has those two tables' fingerprints re-derived (see
334
+ * {@link v1ReleaseBirthFp}).
335
+ */
336
+ export declare const ROW_IDENTITY_RECIPE_V1 = "cleo/row-identity/v1";
337
+ /**
338
+ * The v1 fingerprint of an AC-history or binding row, to recognise a value
339
+ * the 9.25 recipe derived: v1 hashed the criterion's fingerprint
340
+ * (`@refFp:ac_uid`), or `ref:none` / `ref:missing`. Other tables: `null`
341
+ * (their v1 and v2 recipes are the same).
342
+ *
343
+ * @param db - Connection on the project `cleo.db`.
344
+ * @param table - Declared minted table.
345
+ * @param row - The stored row.
346
+ * @returns The v1 value, or `null`.
347
+ * @task T12802
348
+ */
349
+ export declare function v1ReleaseBirthFp(db: DatabaseSync, table: string, row: Readonly<Record<string, UidInput>>): string | null;
329
350
  /**
330
351
  * Local-only key/value table of the row-identity layer. Not
331
352
  * `tasks_schema_meta`: the twin collapse drops twin-only keys of that table on
@@ -370,6 +391,16 @@ export declare function preReleaseBirthFp(db: DatabaseSync, table: string, row:
370
391
  * @param db - Connection on the project `cleo.db`.
371
392
  */
372
393
  export declare function writeRecipeMarker(db: DatabaseSync, writers?: RowIdentityWriters): void;
394
+ /**
395
+ * Whether the store's identity values are shared with other devices
396
+ * ({@link ROW_IDENTITY_SYNCED_KEY} present): from then on no local write may
397
+ * clear or re-point a uid.
398
+ *
399
+ * @param db - Connection on the project `cleo.db`.
400
+ * @returns True once identity has been sent or received.
401
+ * @task T12806
402
+ */
403
+ export declare function rowIdentityShared(db: DatabaseSync): boolean;
373
404
  /**
374
405
  * Record that identity values are now SHARED with other devices: the recipe
375
406
  * marker, and {@link ROW_IDENTITY_SYNCED_KEY} (first write wins, with the
@@ -383,6 +414,65 @@ export declare function writeRecipeMarker(db: DatabaseSync, writers?: RowIdentit
383
414
  * @task T12746
384
415
  */
385
416
  export declare function markRowIdentityShared(db: DatabaseSync, direction: 'send' | 'receive', writers?: RowIdentityWriters): void;
417
+ /**
418
+ * Re-create the row-identity SCHEMA a store should have, on every open,
419
+ * whatever the fill flag (T12878). DDL only: the identity tables, the
420
+ * graveyard trigger, the columns an early table lacks, the uid columns and
421
+ * their indexes. No row value is read or written. A store whose uid migration
422
+ * was journaled without running its statements (journal Scenario 3 Case A:
423
+ * live cleocode under 9.25, spec §12.2) gets the full schema here; the fill,
424
+ * refill and TEMP triggers stay opt-in ({@link prepareRowIdentity}).
425
+ *
426
+ * Never throws: a failure is logged and the open continues.
427
+ *
428
+ * @param db - Connection on a `cleo.db`, inside the cold-open lease.
429
+ * @param scope - The store's scope.
430
+ * @returns The DDL statements it ran (empty when the schema was complete).
431
+ * @task T12878
432
+ */
433
+ export declare function healRowIdentitySchema(db: DatabaseSync, scope: TableScope, writers?: RowIdentityWriters | undefined): string[];
434
+ /**
435
+ * Meta key of the heal receipt: when the open last re-created missing
436
+ * identity schema, and what (T12878). Local-only bookkeeping of the heal
437
+ * itself, in `tasks_row_identity_meta`; no row value of any other table is
438
+ * written. `cleo doctor` (the `row_identity` check) shows it.
439
+ */
440
+ export declare const ROW_IDENTITY_HEAL_KEY = "row_identity_schema_healed";
441
+ /** The receipt {@link healRowIdentitySchema} leaves when it re-created schema. */
442
+ export interface RowIdentityHealReceipt {
443
+ readonly at: string;
444
+ readonly scope?: TableScope;
445
+ readonly statements: number;
446
+ /** Objects re-created or columns added (table, index, trigger or `table.column`). */
447
+ readonly objects: readonly string[];
448
+ }
449
+ /**
450
+ * The last heal receipt of a store, read-only (`undefined` when the schema
451
+ * never needed healing).
452
+ *
453
+ * @param db - Connection on a project `cleo.db` (read-only is fine).
454
+ * @returns The receipt.
455
+ * @task T12878
456
+ */
457
+ export declare function readRowIdentityHealReceipt(db: DatabaseSync): RowIdentityHealReceipt | undefined;
458
+ /**
459
+ * Every heal receipt a store kept, oldest first (read-only).
460
+ *
461
+ * @param db - Connection on a project `cleo.db` (read-only is fine).
462
+ * @returns The receipts (empty when the schema never needed healing).
463
+ * @task T12878
464
+ */
465
+ export declare function readRowIdentityHealHistory(db: DatabaseSync): RowIdentityHealReceipt[];
466
+ /**
467
+ * Identity schema a project store lacks, read-only: missing identity tables,
468
+ * the graveyard trigger, and the alias / graveyard columns an early table
469
+ * lacks (T12878; the open heals them).
470
+ *
471
+ * @param db - Connection on a project `cleo.db` (read-only is fine).
472
+ * @returns What is missing (empty when complete).
473
+ * @task T12878
474
+ */
475
+ export declare function missingRowIdentitySchema(db: DatabaseSync): string[];
386
476
  export declare function prepareRowIdentity(db: DatabaseSync, scope: TableScope, options?: {
387
477
  readonly triggers?: boolean;
388
478
  readonly writers?: RowIdentityWriters;
@@ -1 +1 @@
1
- {"version":3,"file":"row-identity.d.ts","sourceRoot":"","sources":["../../src/store/row-identity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"row-identity.d.ts","sourceRoot":"","sources":["../../src/store/row-identity.ts"],"names":[],"mappings":"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"}
@@ -67,6 +67,7 @@ import { getLogger } from '../logger.js';
67
67
  import { acTextHash } from '../tasks/ac-table.js';
68
68
  import { rowUidFillEnabled } from './row-identity-flag.js';
69
69
  import { BIRTH_FP_COLUMN, ROW_IDENTITY, rowIdentityColumns, rowIdentitySpec, UID_COLUMN, } from './row-identity-registry.js';
70
+ import { schemaWritesAllowed } from './worktree-build-guard.js';
70
71
  export { BIRTH_FP_COLUMN, ROW_IDENTITY, rowIdentityColumns, rowIdentitySpec, UID_COLUMN };
71
72
  /** Domain separator hashed into every deterministic uid. Changing it re-keys every row. */
72
73
  export const ROW_UID_DOMAIN = 'cleo/row-uid/v1';
@@ -938,7 +939,49 @@ export function installRowUidTriggers(db, scope) {
938
939
  * {@link ROW_IDENTITY_RECIPE_KEY} by the first fill; bumped by any change to a
939
940
  * recipe, a frozen content or birth-fact list, or the encoding.
940
941
  */
941
- export const ROW_IDENTITY_RECIPE = 'cleo/row-identity/v1';
942
+ export const ROW_IDENTITY_RECIPE = 'cleo/row-identity/v2';
943
+ /**
944
+ * The recipe 9.25 shipped (v1). v2 changed only the birth facts of AC history
945
+ * and evidence bindings (@refFp:ac_uid and ac_text_hash → none / ac_id,
946
+ * T12802); a store filled
947
+ * with v1 has those two tables' fingerprints re-derived (see
948
+ * {@link v1ReleaseBirthFp}).
949
+ */
950
+ export const ROW_IDENTITY_RECIPE_V1 = 'cleo/row-identity/v1';
951
+ /**
952
+ * The v1 fingerprint of an AC-history or binding row, to recognise a value
953
+ * the 9.25 recipe derived: v1 hashed the criterion's fingerprint
954
+ * (`@refFp:ac_uid`), or `ref:none` / `ref:missing`. Other tables: `null`
955
+ * (their v1 and v2 recipes are the same).
956
+ *
957
+ * @param db - Connection on the project `cleo.db`.
958
+ * @param table - Declared minted table.
959
+ * @param row - The stored row.
960
+ * @returns The v1 value, or `null`.
961
+ * @task T12802
962
+ */
963
+ export function v1ReleaseBirthFp(db, table, row) {
964
+ const col = (c) => row[c] ?? null;
965
+ const facts = table === 'tasks_task_acceptance_criteria_history'
966
+ ? [col('ac_id'), col('previous_text'), col('reason')]
967
+ : table === 'tasks_evidence_ac_bindings'
968
+ ? [col('evidence_atom_id'), col('binding_type'), col('ac_text_hash')]
969
+ : [];
970
+ if (facts.length === 0)
971
+ return null;
972
+ const acUid = col('ac_uid');
973
+ let ref = 'ref:none';
974
+ if (acUid !== null) {
975
+ const ac = db
976
+ .prepare('SELECT birth_fp AS fp FROM main.tasks_task_acceptance_criteria WHERE uid = ?')
977
+ .get(acUid);
978
+ ref = ac === undefined ? 'ref:missing' : ac.fp;
979
+ if (ref === null)
980
+ return null;
981
+ }
982
+ const spec = rowIdentitySpec('project', table);
983
+ return birthFingerprint(table, spec?.birth ? col(spec.birth) : null, [...facts, ref]);
984
+ }
942
985
  /**
943
986
  * Local-only key/value table of the row-identity layer. Not
944
987
  * `tasks_schema_meta`: the twin collapse drops twin-only keys of that table on
@@ -1124,7 +1167,8 @@ export function preReleaseBirthFp(db, table, row) {
1124
1167
  * Birth fingerprints a pre-release build derived (e.g. a worktree CLI that
1125
1168
  * opened live cleocode, 2026-09-28) are cleared, so the fill re-derives them
1126
1169
  * with the release recipe. Targeted: a value is cleared ONLY when it equals
1127
- * {@link preReleaseBirthFp} of its row; values from any other source (a
1170
+ * {@link preReleaseBirthFp} of its row, or hashes a fingerprint cleared here
1171
+ * (@ownerFp / @refFp; T12801); values from any other source (a
1128
1172
  * device that received them by sync, a store whose meta table was lost) are
1129
1173
  * kept, and alias rows are never touched. The uid recipe did not change, so
1130
1174
  * uids are kept. Runs only while the recipe marker is missing or stale, and
@@ -1135,26 +1179,64 @@ function resetStaleIdentity(db, scope, writers) {
1135
1179
  return 'none';
1136
1180
  if (readMeta(db, ROW_IDENTITY_RECIPE_KEY) === ROW_IDENTITY_RECIPE)
1137
1181
  return 'none';
1138
- const minted = ROW_IDENTITY.project.filter((spec) => spec.kind === 'minted' &&
1139
- hasTable(db, spec.table) &&
1140
- columnsOf(db, spec.table).has(BIRTH_FP_COLUMN));
1182
+ const minted = fingerprintOrder('project', ROW_IDENTITY.project).filter((spec) => hasTable(db, spec.table) && columnsOf(db, spec.table).has(BIRTH_FP_COLUMN));
1183
+ // A value is stale when it equals the pre-release recipe of its row, OR when
1184
+ // it was derived from a stale value: a fingerprint that hashes its owner's
1185
+ // or its criterion's fingerprint (@ownerFp / @refFp) whose source is being
1186
+ // cleared. That covers a criterion whose uid and fingerprint an edit carried
1187
+ // onto a new row (the pre-release recipe cannot be recomputed from the new
1188
+ // row, T12801). Tables are visited owners first.
1141
1189
  const stale = [];
1190
+ const cleared = new Map(); // `${table}.${column}` → cleared values
1191
+ const mark = (table, column, value) => {
1192
+ if (value === null || value === undefined)
1193
+ return;
1194
+ const key = `${table}.${column}`;
1195
+ const set = cleared.get(key) ?? new Set();
1196
+ set.add(String(value));
1197
+ cleared.set(key, set);
1198
+ };
1142
1199
  for (const spec of minted) {
1200
+ const deps = (spec.birthFacts ?? [])
1201
+ .map((fact) => fingerprintRef('project', spec, fact))
1202
+ .filter((d) => d !== null);
1203
+ const keyColumn = targetKey('project', spec.table);
1143
1204
  const rows = db
1144
1205
  .prepare(`SELECT rowid AS _rowid, * FROM main.${q(spec.table)} WHERE ${q(BIRTH_FP_COLUMN)} IS NOT NULL`)
1145
1206
  .all();
1146
1207
  for (const row of rows) {
1147
- if (row[BIRTH_FP_COLUMN] === preReleaseBirthFp(db, spec.table, row)) {
1208
+ const derivedFromStale = deps.some((d) => cleared.get(`${d.table}.${d.key}`)?.has(String(row[d.column] ?? '')));
1209
+ // Three sources of a stale value, all kept (T12801 x T12802): derived
1210
+ // from a value cleared in this pass, the pre-release recipe, or the
1211
+ // 9.25 (v1) recipe.
1212
+ if (derivedFromStale ||
1213
+ row[BIRTH_FP_COLUMN] === preReleaseBirthFp(db, spec.table, row) ||
1214
+ row[BIRTH_FP_COLUMN] === v1ReleaseBirthFp(db, spec.table, row)) {
1148
1215
  stale.push({ table: spec.table, rowid: row._rowid });
1216
+ mark(spec.table, keyColumn, row[keyColumn]);
1217
+ mark(spec.table, UID_COLUMN, row[UID_COLUMN]);
1149
1218
  }
1150
1219
  }
1151
1220
  }
1152
- if (stale.length === 0)
1221
+ // The AC uid graveyard records deletions made while the recipe marker was
1222
+ // missing or stale: with the fill off, the schema heal (T12878) restores
1223
+ // its trigger, which then records the stale pre-release uid and birth_fp of
1224
+ // every criterion deleted since. Relinking those would carry a stale
1225
+ // fingerprint into the release recipe and, through the ordinal fallback,
1226
+ // hand a deleted criterion's uid to an unrelated new one. They are not an
1227
+ // older build's delete-and-recreate of one edit, so they are dropped in
1228
+ // this same step, before the fill relinks anything (T12878 review F1).
1229
+ const graveyardRows = scope === 'project' && hasTable(db, AC_UID_GRAVEYARD)
1230
+ ? db.prepare(`SELECT count(*) AS n FROM main.${AC_UID_GRAVEYARD}`).get().n
1231
+ : 0;
1232
+ if (stale.length === 0 && graveyardRows === 0)
1153
1233
  return 'none';
1154
1234
  if (readMeta(db, ROW_IDENTITY_SYNCED_KEY) !== undefined)
1155
1235
  return 'refused';
1156
1236
  db.exec('SAVEPOINT row_identity_reset');
1157
1237
  try {
1238
+ if (graveyardRows > 0)
1239
+ writers.clearAcUidGraveyardNative(db);
1158
1240
  for (const { table, rowid } of stale) {
1159
1241
  writers.clearBirthFpNative(db, table, rowid);
1160
1242
  }
@@ -1179,6 +1261,18 @@ export function writeRecipeMarker(db, writers = requireWriters()) {
1179
1261
  return;
1180
1262
  writers.writeRowIdentityMetaNative(db, ROW_IDENTITY_RECIPE_KEY, ROW_IDENTITY_RECIPE);
1181
1263
  }
1264
+ /**
1265
+ * Whether the store's identity values are shared with other devices
1266
+ * ({@link ROW_IDENTITY_SYNCED_KEY} present): from then on no local write may
1267
+ * clear or re-point a uid.
1268
+ *
1269
+ * @param db - Connection on the project `cleo.db`.
1270
+ * @returns True once identity has been sent or received.
1271
+ * @task T12806
1272
+ */
1273
+ export function rowIdentityShared(db) {
1274
+ return readMeta(db, ROW_IDENTITY_SYNCED_KEY) !== undefined;
1275
+ }
1182
1276
  /**
1183
1277
  * Record that identity values are now SHARED with other devices: the recipe
1184
1278
  * marker, and {@link ROW_IDENTITY_SYNCED_KEY} (first write wins, with the
@@ -1200,6 +1294,181 @@ export function markRowIdentityShared(db, direction, writers = requireWriters())
1200
1294
  return;
1201
1295
  writers.writeRowIdentityMetaNative(db, ROW_IDENTITY_SYNCED_KEY, JSON.stringify({ first: direction, at: new Date().toISOString() }));
1202
1296
  }
1297
+ /**
1298
+ * Re-create the row-identity SCHEMA a store should have, on every open,
1299
+ * whatever the fill flag (T12878). DDL only: the identity tables, the
1300
+ * graveyard trigger, the columns an early table lacks, the uid columns and
1301
+ * their indexes. No row value is read or written. A store whose uid migration
1302
+ * was journaled without running its statements (journal Scenario 3 Case A:
1303
+ * live cleocode under 9.25, spec §12.2) gets the full schema here; the fill,
1304
+ * refill and TEMP triggers stay opt-in ({@link prepareRowIdentity}).
1305
+ *
1306
+ * Never throws: a failure is logged and the open continues.
1307
+ *
1308
+ * @param db - Connection on a `cleo.db`, inside the cold-open lease.
1309
+ * @param scope - The store's scope.
1310
+ * @returns The DDL statements it ran (empty when the schema was complete).
1311
+ * @task T12878
1312
+ */
1313
+ export function healRowIdentitySchema(db, scope, writers = registeredWriters) {
1314
+ const log = getLogger('row-identity');
1315
+ if (ROW_IDENTITY[scope].length === 0) {
1316
+ // Nothing is declared in this scope yet (the global store today): there is
1317
+ // no identity schema to heal, so nothing to record either.
1318
+ return [];
1319
+ }
1320
+ // A build that may not change this store's schema (a worktree-built CLI on a
1321
+ // live store, T12687) never heals it: the released build will.
1322
+ if (!schemaWritesAllowed(db)) {
1323
+ log.debug({ scope }, 'row identity schema heal skipped: schema writes refused for this build');
1324
+ return [];
1325
+ }
1326
+ try {
1327
+ db.exec('SAVEPOINT row_identity_heal');
1328
+ try {
1329
+ const healed = [
1330
+ ...(scope === 'project' ? ensureIdentityTables(db) : []),
1331
+ ...ensureRowIdentitySchema(db, scope),
1332
+ ];
1333
+ if (healed.length > 0)
1334
+ recordHealReceipt(db, scope, healed, writers);
1335
+ db.exec('RELEASE SAVEPOINT row_identity_heal');
1336
+ if (healed.length > 0)
1337
+ log.info({ scope, healed: healed.length }, 'row identity schema healed');
1338
+ return healed;
1339
+ }
1340
+ catch (error) {
1341
+ db.exec('ROLLBACK TO SAVEPOINT row_identity_heal');
1342
+ db.exec('RELEASE SAVEPOINT row_identity_heal');
1343
+ throw error;
1344
+ }
1345
+ }
1346
+ catch (error) {
1347
+ log.error({ scope, error }, 'row identity schema heal failed');
1348
+ return [];
1349
+ }
1350
+ }
1351
+ /**
1352
+ * Meta key of the heal receipt: when the open last re-created missing
1353
+ * identity schema, and what (T12878). Local-only bookkeeping of the heal
1354
+ * itself, in `tasks_row_identity_meta`; no row value of any other table is
1355
+ * written. `cleo doctor` (the `row_identity` check) shows it.
1356
+ */
1357
+ export const ROW_IDENTITY_HEAL_KEY = 'row_identity_schema_healed';
1358
+ /** The table holding each scope's heal receipts (`null`: the scope has none yet). */
1359
+ const HEAL_RECEIPT_TABLE = {
1360
+ project: ROW_IDENTITY_META_TABLE,
1361
+ // The global store declares no identity table yet (ROW_IDENTITY.global is
1362
+ // empty, so its heal never runs); the change that declares one adds its
1363
+ // meta table here.
1364
+ global: null,
1365
+ };
1366
+ /** How many heal receipts a store keeps (newest last). */
1367
+ const HEAL_RECEIPTS_KEPT = 20;
1368
+ function recordHealReceipt(db, scope, healed, writers) {
1369
+ const table = HEAL_RECEIPT_TABLE[scope];
1370
+ const log = getLogger('row-identity');
1371
+ if (table === null || !hasTable(db, table) || !writers) {
1372
+ log.warn({ scope, healed: healed.length }, 'row identity schema healed; no receipt table');
1373
+ return;
1374
+ }
1375
+ const objects = healed.map((stmt) => {
1376
+ const col = /ALTER TABLE main\.("?)(\w+)\1 ADD COLUMN ("?)(\w+)\3/.exec(stmt);
1377
+ if (col)
1378
+ return `${col[2]}.${col[4]}`;
1379
+ const obj = /(TABLE|INDEX|TRIGGER) IF NOT EXISTS main\.("?)(\w+)\2/.exec(stmt);
1380
+ return obj ? `${obj[1].toLowerCase()} ${obj[3]}` : stmt.slice(0, 60);
1381
+ });
1382
+ const receipt = {
1383
+ at: new Date().toISOString(),
1384
+ scope,
1385
+ statements: healed.length,
1386
+ objects,
1387
+ };
1388
+ // Appended, never overwritten: a later partial heal keeps the earlier ones.
1389
+ const history = [...readRowIdentityHealHistory(db), receipt].slice(-HEAL_RECEIPTS_KEPT);
1390
+ writers.writeRowIdentityMetaNative(db, ROW_IDENTITY_HEAL_KEY, JSON.stringify(history));
1391
+ }
1392
+ /**
1393
+ * The last heal receipt of a store, read-only (`undefined` when the schema
1394
+ * never needed healing).
1395
+ *
1396
+ * @param db - Connection on a project `cleo.db` (read-only is fine).
1397
+ * @returns The receipt.
1398
+ * @task T12878
1399
+ */
1400
+ export function readRowIdentityHealReceipt(db) {
1401
+ const history = readRowIdentityHealHistory(db);
1402
+ return history[history.length - 1];
1403
+ }
1404
+ /**
1405
+ * Every heal receipt a store kept, oldest first (read-only).
1406
+ *
1407
+ * @param db - Connection on a project `cleo.db` (read-only is fine).
1408
+ * @returns The receipts (empty when the schema never needed healing).
1409
+ * @task T12878
1410
+ */
1411
+ export function readRowIdentityHealHistory(db) {
1412
+ const raw = readMeta(db, ROW_IDENTITY_HEAL_KEY);
1413
+ if (raw === undefined)
1414
+ return [];
1415
+ try {
1416
+ const parsed = JSON.parse(raw);
1417
+ return Array.isArray(parsed) ? parsed : [parsed];
1418
+ }
1419
+ catch {
1420
+ return [];
1421
+ }
1422
+ }
1423
+ /**
1424
+ * Identity schema a project store lacks, read-only: missing identity tables,
1425
+ * the graveyard trigger, and the alias / graveyard columns an early table
1426
+ * lacks (T12878; the open heals them).
1427
+ *
1428
+ * @param db - Connection on a project `cleo.db` (read-only is fine).
1429
+ * @returns What is missing (empty when complete).
1430
+ * @task T12878
1431
+ */
1432
+ export function missingRowIdentitySchema(db) {
1433
+ if (!hasTable(db, 'tasks_task_acceptance_criteria'))
1434
+ return [];
1435
+ const missing = [];
1436
+ for (const table of Object.keys(IDENTITY_TABLE_DDL)) {
1437
+ if (!hasTable(db, table))
1438
+ missing.push(`table ${table}`);
1439
+ }
1440
+ for (const [table, column] of [
1441
+ ['tasks_display_id_aliases', 'displaced_hlc'],
1442
+ ['tasks_display_id_aliases', 'entity_birth_fp'],
1443
+ [AC_UID_GRAVEYARD, 'birth_fp'],
1444
+ ]) {
1445
+ if (hasTable(db, table) && !columnsOf(db, table).has(column)) {
1446
+ missing.push(`column ${table}.${column}`);
1447
+ }
1448
+ }
1449
+ if (!hasObject(db, 'trigger', 'trg_tasks_ac_uid_graveyard')) {
1450
+ missing.push('trigger trg_tasks_ac_uid_graveyard');
1451
+ }
1452
+ // The uid columns and indexes ensureRowIdentitySchema heals.
1453
+ for (const spec of ROW_IDENTITY.project) {
1454
+ if (!hasTable(db, spec.table))
1455
+ continue;
1456
+ const cols = columnsOf(db, spec.table);
1457
+ for (const column of rowIdentityColumns('project', spec.table)) {
1458
+ if (!cols.has(column))
1459
+ missing.push(`column ${spec.table}.${column}`);
1460
+ }
1461
+ if (!uidIsPrimaryKey(db, spec.table) && !hasObject(db, 'index', `uq_${spec.table}_uid`)) {
1462
+ missing.push(`index uq_${spec.table}_uid`);
1463
+ }
1464
+ for (const ref of spec.storedRefUids ?? []) {
1465
+ const index = `idx_${spec.table}_${ref.column}`;
1466
+ if (!hasObject(db, 'index', index))
1467
+ missing.push(`index ${index}`);
1468
+ }
1469
+ }
1470
+ return missing;
1471
+ }
1203
1472
  export function prepareRowIdentity(db, scope, options = {}) {
1204
1473
  if (ROW_IDENTITY[scope].length === 0)
1205
1474
  return null;