@cleocode/contracts 2026.10.1 → 2026.10.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.
- package/dist/cloud/api.d.ts +469 -2
- package/dist/cloud/api.d.ts.map +1 -1
- package/dist/cloud/api.js +209 -8
- package/dist/cloud/api.js.map +1 -1
- package/dist/cloud/envelope.d.ts +6 -0
- package/dist/cloud/envelope.d.ts.map +1 -1
- package/dist/cloud/envelope.js +12 -0
- package/dist/cloud/envelope.js.map +1 -1
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/heavy-command-hook.d.ts +123 -0
- package/dist/heavy-command-hook.d.ts.map +1 -0
- package/dist/heavy-command-hook.js +17 -0
- package/dist/heavy-command-hook.js.map +1 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +8 -0
- package/dist/install.d.ts.map +1 -1
- package/dist/invariants/index.d.ts +2 -0
- package/dist/invariants/index.d.ts.map +1 -1
- package/dist/invariants/index.js +1 -0
- package/dist/invariants/index.js.map +1 -1
- package/dist/invariants/sync-write-invariants.d.ts +115 -0
- package/dist/invariants/sync-write-invariants.d.ts.map +1 -0
- package/dist/invariants/sync-write-invariants.js +123 -0
- package/dist/invariants/sync-write-invariants.js.map +1 -0
- package/dist/nexus-account.d.ts +1 -1
- package/dist/nexus-account.d.ts.map +1 -1
- package/dist/nexus-account.js +27 -0
- package/dist/nexus-account.js.map +1 -1
- package/dist/nexus-cloud.d.ts +16 -1
- package/dist/nexus-cloud.d.ts.map +1 -1
- package/dist/nexus-cloud.js +12 -0
- package/dist/nexus-cloud.js.map +1 -1
- package/dist/nexus-vault.d.ts +266 -0
- package/dist/nexus-vault.d.ts.map +1 -0
- package/dist/nexus-vault.js +82 -0
- package/dist/nexus-vault.js.map +1 -0
- package/dist/portable-bundle.d.ts +5 -0
- package/dist/portable-bundle.d.ts.map +1 -1
- package/dist/resource-governor.d.ts +13 -0
- package/dist/resource-governor.d.ts.map +1 -1
- package/dist/resource-governor.js +13 -0
- package/dist/resource-governor.js.map +1 -1
- package/dist/sync-schema.d.ts +34 -0
- package/dist/sync-schema.d.ts.map +1 -0
- package/dist/sync-schema.js +34 -0
- package/dist/sync-schema.js.map +1 -0
- package/dist/task.d.ts +49 -1
- package/dist/task.d.ts.map +1 -1
- package/dist/tasks.d.ts +6 -0
- package/dist/tasks.d.ts.map +1 -1
- package/dist/tasks.js.map +1 -1
- package/package.json +2 -2
- package/src/cloud/api.ts +258 -9
- package/src/cloud/envelope.ts +12 -0
- package/src/config.ts +13 -0
- package/src/heavy-command-hook.ts +136 -0
- package/src/index.ts +44 -0
- package/src/install.ts +9 -0
- package/src/invariants/index.ts +6 -0
- package/src/invariants/sync-write-invariants.ts +460 -0
- package/src/nexus-account.ts +27 -0
- package/src/nexus-cloud.ts +29 -0
- package/src/nexus-vault.ts +280 -0
- package/src/portable-bundle.ts +5 -0
- package/src/resource-governor.ts +15 -0
- package/src/sync-schema.ts +34 -0
- package/src/task.ts +49 -1
- package/src/tasks.ts +6 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync write-invariant registry (T12881): every rule TypeScript enforces on a
|
|
3
|
+
* write to a synced table, with the class that says how it survives merged
|
|
4
|
+
* apply (spec `t12859-sync-write-validator-inventory` §3.6.7, journal design
|
|
5
|
+
* §3.6).
|
|
6
|
+
*
|
|
7
|
+
* The raw applier never runs the TypeScript write paths, so a rejection site
|
|
8
|
+
* on a synced write path carries `// @sync-invariant <id>` naming an entry
|
|
9
|
+
* here, or an escape tag (`none:input-shape <reason>`,
|
|
10
|
+
* `none:local-only <reason>`). Gate 38 (`scripts/lint-sync-write-invariants.mjs`)
|
|
11
|
+
* finds the sites with the TypeScript compiler API and fails on an untagged
|
|
12
|
+
* one that is not in its shrink-only baseline, on a dangling tag, and on a
|
|
13
|
+
* registry entry that does not close (§3.6.7 rule 5).
|
|
14
|
+
*
|
|
15
|
+
* Seeded from the inventory's distinct catalogues: the trigger and index
|
|
16
|
+
* guards of §3.6.2, the fifteen post-apply check families PAC-01..PAC-15 of
|
|
17
|
+
* §3.6.5 and the typed merge rules of §3.6.6. The executable post-apply
|
|
18
|
+
* checks and the merge-rule registry belong to the merge engine (T12344) and
|
|
19
|
+
* do not exist yet, so each PAC and each rule carries `pending` naming its
|
|
20
|
+
* own open T12344 item (T12922..T12936 for PAC-01..15, T12937..T12945 for the
|
|
21
|
+
* rules); `--verify-tasks` checks those tasks are open. Trigger-covered
|
|
22
|
+
* entries carry no pending: their triggers and indexes are checked against
|
|
23
|
+
* migrations and fresh stores. The §3.5 Rule 4 guard-class footprint check is
|
|
24
|
+
* not enforced until that class exists.
|
|
25
|
+
*
|
|
26
|
+
* Types and const data only (arch gate 10).
|
|
27
|
+
*
|
|
28
|
+
* @task T12881
|
|
29
|
+
* @epic T12323
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* How a write rule survives merged apply (§3.6 intro).
|
|
34
|
+
*
|
|
35
|
+
* - `trigger-covered` — a migration-created trigger, CHECK, UNIQUE or FK
|
|
36
|
+
* enforces the same rule on apply.
|
|
37
|
+
* - `post-apply-check` — a multi-row or cross-table rule evaluated over the
|
|
38
|
+
* apply page's footprint (a T12344 PAC item).
|
|
39
|
+
* - `monotonic-merge-rule` — a per-row or per-field transition or grouping
|
|
40
|
+
* rule, enforced by a T12344 typed merge rule.
|
|
41
|
+
* - `not-sync-relevant` — input shape, local-only data or a single-column
|
|
42
|
+
* value rule LWW cannot break.
|
|
43
|
+
* - `identity-layer` — really about id minting; T12341 uids and re-minting
|
|
44
|
+
* own it.
|
|
45
|
+
*
|
|
46
|
+
* @task T12881
|
|
47
|
+
*/
|
|
48
|
+
export type SyncWriteInvariantClass =
|
|
49
|
+
| 'trigger-covered'
|
|
50
|
+
| 'post-apply-check'
|
|
51
|
+
| 'monotonic-merge-rule'
|
|
52
|
+
| 'not-sync-relevant'
|
|
53
|
+
| 'identity-layer';
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A closure requirement the entry cannot meet yet, and the task that meets it.
|
|
57
|
+
*
|
|
58
|
+
* @task T12881
|
|
59
|
+
*/
|
|
60
|
+
export interface SyncWriteInvariantPending {
|
|
61
|
+
/** The task that lands the missing piece (e.g. the T12344 check or rule). */
|
|
62
|
+
readonly task: string;
|
|
63
|
+
/** What is missing. */
|
|
64
|
+
readonly reason: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* One distinct write rule on synced tables.
|
|
69
|
+
*
|
|
70
|
+
* @task T12881
|
|
71
|
+
*/
|
|
72
|
+
export interface SyncWriteInvariant {
|
|
73
|
+
/** Stable id named by `@sync-invariant <id>` tags, e.g. `task.status.absorbing`. */
|
|
74
|
+
readonly id: string;
|
|
75
|
+
/** How the rule survives merged apply. */
|
|
76
|
+
readonly class: SyncWriteInvariantClass;
|
|
77
|
+
/** Physical tables; each must be classified by Gate A. */
|
|
78
|
+
readonly tables: readonly string[];
|
|
79
|
+
/** Known rejection sites (repo-relative file, enclosing symbol, codes). */
|
|
80
|
+
readonly sites: readonly {
|
|
81
|
+
readonly file: string;
|
|
82
|
+
readonly symbol: string;
|
|
83
|
+
readonly codes: readonly string[];
|
|
84
|
+
}[];
|
|
85
|
+
/** `trigger-covered`: trigger or index names a migration creates. */
|
|
86
|
+
readonly triggers?: readonly string[];
|
|
87
|
+
/**
|
|
88
|
+
* `post-apply-check`: the check and its footprint. `module` and
|
|
89
|
+
* `functionName` may be absent only while `pending` is set.
|
|
90
|
+
*/
|
|
91
|
+
readonly check?: {
|
|
92
|
+
readonly module?: string;
|
|
93
|
+
readonly functionName?: string;
|
|
94
|
+
readonly footprint: readonly string[];
|
|
95
|
+
};
|
|
96
|
+
/** `monotonic-merge-rule`: the table and columns the rule merges (`*` = whole row). */
|
|
97
|
+
readonly mergeRule?: { readonly table: string; readonly columns: readonly string[] };
|
|
98
|
+
/** A runtime guard enforcing the rule; it must have a production caller. */
|
|
99
|
+
readonly runtimeGate?: { readonly module: string; readonly functionName: string };
|
|
100
|
+
/** Convergence sources the rule reads (config, file, env, local-only column). */
|
|
101
|
+
readonly readsNonSynced?: readonly string[];
|
|
102
|
+
/** Set when every replica is pinned to the same `readsNonSynced` policy (§3.5 Rule 1). */
|
|
103
|
+
readonly pinnedPolicy?: boolean;
|
|
104
|
+
/** Inventory rows behind the entry (PAC item, V/C/S/B/D/… row ids). */
|
|
105
|
+
readonly inventory?: readonly string[];
|
|
106
|
+
/** A closure requirement not met yet. */
|
|
107
|
+
readonly pending?: SyncWriteInvariantPending;
|
|
108
|
+
/** Why; required for `not-sync-relevant` and `identity-layer`. */
|
|
109
|
+
readonly reason: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** A post-apply check not implemented yet; `task` is its T12344 item. */
|
|
113
|
+
function checkPending(task: string): SyncWriteInvariantPending {
|
|
114
|
+
return { task, reason: 'the post-apply check is not implemented yet (T12344 item)' };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** A merge rule the merge-rule registry does not define yet; `task` is its T12344 item. */
|
|
118
|
+
function rulePending(task: string): SyncWriteInvariantPending {
|
|
119
|
+
return { task, reason: 'the typed merge-rule registry does not define it yet (T12344 item)' };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function trigger(
|
|
123
|
+
id: string,
|
|
124
|
+
tables: readonly string[],
|
|
125
|
+
triggers: readonly string[],
|
|
126
|
+
inventory: readonly string[],
|
|
127
|
+
reason: string,
|
|
128
|
+
): SyncWriteInvariant {
|
|
129
|
+
return { id, class: 'trigger-covered', tables, sites: [], triggers, inventory, reason };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function pac(
|
|
133
|
+
id: string,
|
|
134
|
+
item: string,
|
|
135
|
+
task: string,
|
|
136
|
+
tables: readonly string[],
|
|
137
|
+
footprint: readonly string[],
|
|
138
|
+
reason: string,
|
|
139
|
+
): SyncWriteInvariant {
|
|
140
|
+
return {
|
|
141
|
+
id,
|
|
142
|
+
class: 'post-apply-check',
|
|
143
|
+
tables,
|
|
144
|
+
sites: [],
|
|
145
|
+
check: { footprint },
|
|
146
|
+
inventory: [item],
|
|
147
|
+
pending: checkPending(task),
|
|
148
|
+
reason,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function rule(
|
|
153
|
+
id: string,
|
|
154
|
+
task: string,
|
|
155
|
+
table: string,
|
|
156
|
+
columns: readonly string[],
|
|
157
|
+
inventory: readonly string[],
|
|
158
|
+
reason: string,
|
|
159
|
+
tables: readonly string[] = [table],
|
|
160
|
+
): SyncWriteInvariant {
|
|
161
|
+
return {
|
|
162
|
+
id,
|
|
163
|
+
class: 'monotonic-merge-rule',
|
|
164
|
+
tables,
|
|
165
|
+
sites: [],
|
|
166
|
+
mergeRule: { table, columns },
|
|
167
|
+
inventory,
|
|
168
|
+
pending: rulePending(task),
|
|
169
|
+
reason,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* The sync write-invariant registry, in inventory order.
|
|
175
|
+
*
|
|
176
|
+
* @task T12881
|
|
177
|
+
*/
|
|
178
|
+
export const SYNC_WRITE_INVARIANTS: readonly SyncWriteInvariant[] = Object.freeze([
|
|
179
|
+
// §3.6.2: RAISE guards and UNIQUE indexes a migration creates on synced tables.
|
|
180
|
+
trigger(
|
|
181
|
+
'task.parent.no-cycle',
|
|
182
|
+
['tasks_tasks'],
|
|
183
|
+
['tasks_tasks_parent_cycle_guard_insert', 'tasks_tasks_parent_cycle_guard_update'],
|
|
184
|
+
['V10', 'V11'],
|
|
185
|
+
'the parent chain is acyclic (the INSERT variant misses a self-parent, §3.6.3 item 2)',
|
|
186
|
+
),
|
|
187
|
+
trigger(
|
|
188
|
+
'task.parent.type-matrix',
|
|
189
|
+
['tasks_tasks'],
|
|
190
|
+
['tasks_tasks_parent_type_matrix_insert', 'tasks_tasks_parent_type_matrix_update'],
|
|
191
|
+
['X1'],
|
|
192
|
+
'parent/child type matrix; weaker than TypeScript (§3.6.3 item 1), the rest is PAC-01',
|
|
193
|
+
),
|
|
194
|
+
trigger(
|
|
195
|
+
'task.status.pipeline-consistency',
|
|
196
|
+
['tasks_tasks'],
|
|
197
|
+
['trg_tasks_tasks_status_pipeline_insert', 'trg_tasks_tasks_status_pipeline_update'],
|
|
198
|
+
['V19', 'C21'],
|
|
199
|
+
'T877: status and pipeline_stage agree on one row',
|
|
200
|
+
),
|
|
201
|
+
trigger(
|
|
202
|
+
'task.relation.non-containment',
|
|
203
|
+
['tasks_task_relations'],
|
|
204
|
+
['tasks_task_relations_non_containment_insert', 'tasks_task_relations_non_containment_update'],
|
|
205
|
+
['V31'],
|
|
206
|
+
'no relation between a parent and its child, on relation writes only (§3.6.3 item 4)',
|
|
207
|
+
),
|
|
208
|
+
trigger(
|
|
209
|
+
'task.ac.child-target',
|
|
210
|
+
['tasks_task_acceptance_criteria'],
|
|
211
|
+
['tasks_task_acceptance_child_target_insert', 'tasks_task_acceptance_child_target_update'],
|
|
212
|
+
['C28'],
|
|
213
|
+
'a child_task AC targets a direct child, on AC writes only (§3.6.3 item 4)',
|
|
214
|
+
),
|
|
215
|
+
trigger(
|
|
216
|
+
'session.handoff.append-only',
|
|
217
|
+
['tasks_session_handoff_entries'],
|
|
218
|
+
['trg_tasks_session_handoff_no_update'],
|
|
219
|
+
['H2'],
|
|
220
|
+
'handoff entries are never updated',
|
|
221
|
+
),
|
|
222
|
+
trigger(
|
|
223
|
+
'task.claim.lease-iso',
|
|
224
|
+
['tasks_tasks'],
|
|
225
|
+
['tasks_tasks_lease_iso_insert', 'tasks_tasks_lease_iso_update'],
|
|
226
|
+
[],
|
|
227
|
+
'claim lease timestamps are ISO-8601 (t12736)',
|
|
228
|
+
),
|
|
229
|
+
trigger(
|
|
230
|
+
'task.ac-binding.unique',
|
|
231
|
+
['tasks_evidence_ac_bindings'],
|
|
232
|
+
['uq_tasks_evidence_ac_bindings_atom_ac_type'],
|
|
233
|
+
['C31'],
|
|
234
|
+
'one binding per (evidence atom, AC, binding type)',
|
|
235
|
+
),
|
|
236
|
+
trigger(
|
|
237
|
+
'release.version.unique',
|
|
238
|
+
['tasks_releases'],
|
|
239
|
+
['uq_tasks_releases_version'],
|
|
240
|
+
['REL-1'],
|
|
241
|
+
'one release row per version',
|
|
242
|
+
),
|
|
243
|
+
trigger(
|
|
244
|
+
'selfimprove.dhq.one-open',
|
|
245
|
+
['selfimprove_dhq'],
|
|
246
|
+
['ux_selfimprove_dhq_open'],
|
|
247
|
+
['D49'],
|
|
248
|
+
'at most one open DHQ entry per question_hash',
|
|
249
|
+
),
|
|
250
|
+
trigger(
|
|
251
|
+
'account.one-active-per-provider',
|
|
252
|
+
['accounts'],
|
|
253
|
+
['ux_accounts_active_provider'],
|
|
254
|
+
['A40'],
|
|
255
|
+
'at most one active account per provider (partial UNIQUE)',
|
|
256
|
+
),
|
|
257
|
+
|
|
258
|
+
// §3.6.5: post-apply check families (T12344 items).
|
|
259
|
+
pac(
|
|
260
|
+
'task.tree.shape',
|
|
261
|
+
'PAC-01',
|
|
262
|
+
'T12922',
|
|
263
|
+
['tasks_tasks'],
|
|
264
|
+
['rows with a changed parent_id or type', 'their direct children and ancestor chain'],
|
|
265
|
+
'saga→epic→task→subtask matrix, non-saga has a parent, depth ≤ 3, no self-parent',
|
|
266
|
+
),
|
|
267
|
+
pac(
|
|
268
|
+
'task.terminal-parent.live-children',
|
|
269
|
+
'PAC-02',
|
|
270
|
+
'T12923',
|
|
271
|
+
['tasks_tasks'],
|
|
272
|
+
['rows whose status, stage or parent changed', 'their parent and siblings'],
|
|
273
|
+
'no terminal parent with a live child; epic stage ≥ children; rollup re-evaluated',
|
|
274
|
+
),
|
|
275
|
+
pac(
|
|
276
|
+
'task.dependency.graph',
|
|
277
|
+
'PAC-03',
|
|
278
|
+
'T12924',
|
|
279
|
+
['tasks_task_dependencies', 'tasks_tasks'],
|
|
280
|
+
['dependency edges inserted in the page', 'reachability from each depends_on'],
|
|
281
|
+
'dependencies are acyclic, with no self-edge',
|
|
282
|
+
),
|
|
283
|
+
pac(
|
|
284
|
+
'task.done.evidence',
|
|
285
|
+
'PAC-04',
|
|
286
|
+
'T12925',
|
|
287
|
+
['tasks_tasks', 'tasks_task_acceptance_criteria', 'tasks_evidence_ac_bindings'],
|
|
288
|
+
['done rows in the page', 'their AC rows, bindings, acceptance_json and verification_json'],
|
|
289
|
+
'a done task has passed verification and every AC bound or waived',
|
|
290
|
+
),
|
|
291
|
+
pac(
|
|
292
|
+
'task.ac.integrity',
|
|
293
|
+
'PAC-05',
|
|
294
|
+
'T12926',
|
|
295
|
+
['tasks_tasks', 'tasks_task_acceptance_criteria', 'tasks_evidence_ac_bindings'],
|
|
296
|
+
['tasks whose AC rows, bindings, acceptance_json, parent_id or relations changed'],
|
|
297
|
+
'acceptance_json ⇔ AC rows, child_task ACs match children, bindings resolve',
|
|
298
|
+
),
|
|
299
|
+
pac(
|
|
300
|
+
'lifecycle.rows',
|
|
301
|
+
'PAC-06',
|
|
302
|
+
'T12927',
|
|
303
|
+
['tasks_lifecycle_pipelines', 'tasks_lifecycle_stages', 'tasks_tasks'],
|
|
304
|
+
['lifecycle pipeline, stage and gate rows in the page', 'the owning task row'],
|
|
305
|
+
'one pipeline per task, one stage row per name, pipeline_stage ≥ completed stages',
|
|
306
|
+
),
|
|
307
|
+
pac(
|
|
308
|
+
'derived.mirrors',
|
|
309
|
+
'PAC-07',
|
|
310
|
+
'T12928',
|
|
311
|
+
['tasks_task_labels', 'brain_sticky_tags', 'docs_wikilinks', 'tasks_tasks'],
|
|
312
|
+
['owning rows whose JSON or junction changed'],
|
|
313
|
+
'junction tables equal their JSON source',
|
|
314
|
+
),
|
|
315
|
+
pac(
|
|
316
|
+
'session.goal.singletons',
|
|
317
|
+
'PAC-08',
|
|
318
|
+
'T12929',
|
|
319
|
+
['tasks_sessions', 'tasks_task_work_history', 'tasks_goal'],
|
|
320
|
+
['session rows whose status, scope or chain changed', 'work-history rows per session'],
|
|
321
|
+
'one active session per scope, reciprocal chain, one open interval, one live goal',
|
|
322
|
+
),
|
|
323
|
+
pac(
|
|
324
|
+
'audit.uniqueness',
|
|
325
|
+
'PAC-09',
|
|
326
|
+
'T12930',
|
|
327
|
+
['tasks_audit_log'],
|
|
328
|
+
['audit rows with a non-null idempotency key', 'rollback audit rows'],
|
|
329
|
+
'idempotency keys are unique across replicas; at most one rollback per receipt',
|
|
330
|
+
),
|
|
331
|
+
pac(
|
|
332
|
+
'identity.collisions',
|
|
333
|
+
'PAC-10',
|
|
334
|
+
'T12931',
|
|
335
|
+
['tasks_tasks', 'tasks_sessions', 'brain_observations', 'docs_attachments'],
|
|
336
|
+
['inserted rows', "the natural key's index"],
|
|
337
|
+
'display-id and natural-key clashes become an alias or re-mint (T12341)',
|
|
338
|
+
),
|
|
339
|
+
pac(
|
|
340
|
+
'dedupe.at-most-one',
|
|
341
|
+
'PAC-11',
|
|
342
|
+
'T12932',
|
|
343
|
+
['brain_decisions', 'brain_learnings', 'tasks_external_task_links'],
|
|
344
|
+
['inserted rows grouped by the natural key'],
|
|
345
|
+
'duplicates created on two devices are merged',
|
|
346
|
+
),
|
|
347
|
+
pac(
|
|
348
|
+
'ref.soft-orphans',
|
|
349
|
+
'PAC-12',
|
|
350
|
+
'T12933',
|
|
351
|
+
['brain_memory_links', 'brain_page_edges', 'docs_attachment_refs', 'tasks_task_commits'],
|
|
352
|
+
['reference columns of the rows in the page', 'their targets'],
|
|
353
|
+
'cross-DB and polymorphic references resolve',
|
|
354
|
+
),
|
|
355
|
+
pac(
|
|
356
|
+
'supersession.graph',
|
|
357
|
+
'PAC-13',
|
|
358
|
+
'T12934',
|
|
359
|
+
['brain_decisions', 'docs_attachments', 'tasks_architecture_decisions'],
|
|
360
|
+
['rows whose supersession columns changed', 'a chain walk'],
|
|
361
|
+
'supersession chains are acyclic with at most one successor',
|
|
362
|
+
),
|
|
363
|
+
pac(
|
|
364
|
+
'derived.counters',
|
|
365
|
+
'PAC-14',
|
|
366
|
+
'T12935',
|
|
367
|
+
['docs_attachments', 'conduit_attachments', 'docs_pipeline_manifest'],
|
|
368
|
+
['the owning rows'],
|
|
369
|
+
'derived counters and cross-column facts agree with their rows',
|
|
370
|
+
),
|
|
371
|
+
pac(
|
|
372
|
+
'apply.preconditions',
|
|
373
|
+
'PAC-15',
|
|
374
|
+
'T12936',
|
|
375
|
+
['tasks_tasks'],
|
|
376
|
+
['the whole page'],
|
|
377
|
+
'no apply while the local twin collapse has failed; restore is a re-baseline',
|
|
378
|
+
),
|
|
379
|
+
|
|
380
|
+
// §3.6.6: typed merge rules (T12344).
|
|
381
|
+
rule(
|
|
382
|
+
'task.status.absorbing',
|
|
383
|
+
'T12937',
|
|
384
|
+
'tasks_tasks',
|
|
385
|
+
['status', 'completed_at', 'cancelled_at', 'cancellation_reason', 'pipeline_stage'],
|
|
386
|
+
['V18', 'V20', 'C22', 'P40', 'P46', 'P47'],
|
|
387
|
+
'done, cancelled and archived are absorbing; leaving them needs an explicit op',
|
|
388
|
+
),
|
|
389
|
+
rule(
|
|
390
|
+
'task.pipeline-stage.max',
|
|
391
|
+
'T12938',
|
|
392
|
+
'tasks_tasks',
|
|
393
|
+
['pipeline_stage'],
|
|
394
|
+
['V21', 'C20', 'P01', 'F3'],
|
|
395
|
+
'pipeline_stage is the maximum by STAGE_ORDER, except an explicit restore',
|
|
396
|
+
),
|
|
397
|
+
rule(
|
|
398
|
+
'task.verification.frozen-on-done',
|
|
399
|
+
'T12939',
|
|
400
|
+
'tasks_tasks',
|
|
401
|
+
['verification_json'],
|
|
402
|
+
['C01', 'C02', 'C03', 'C17'],
|
|
403
|
+
'verification is frozen once done; otherwise merged per gate',
|
|
404
|
+
),
|
|
405
|
+
rule(
|
|
406
|
+
'session.status.terminal',
|
|
407
|
+
'T12940',
|
|
408
|
+
'tasks_sessions',
|
|
409
|
+
['status', 'ended_at'],
|
|
410
|
+
['S7', 'S8', 'S9', 'S10', 'S11', 'S17', 'S18', 'SC2', 'N1', 'SK5', 'P32'],
|
|
411
|
+
'ended and orphaned are terminal except on an explicit resume',
|
|
412
|
+
),
|
|
413
|
+
rule('audit.immutable', 'T12941', 'tasks_audit_log', ['*'], ['SA3'], 'audit rows are immutable', [
|
|
414
|
+
'tasks_audit_log',
|
|
415
|
+
'audit_log',
|
|
416
|
+
]),
|
|
417
|
+
rule(
|
|
418
|
+
'sticky.status.one-way',
|
|
419
|
+
'T12942',
|
|
420
|
+
'brain_sticky_notes',
|
|
421
|
+
['status', 'converted_to_json'],
|
|
422
|
+
['SK2', 'SK3', 'B24'],
|
|
423
|
+
'active→{converted, archived} is one-way; converted_to_json is write-once',
|
|
424
|
+
),
|
|
425
|
+
rule(
|
|
426
|
+
'release.status.absorbing',
|
|
427
|
+
'T12943',
|
|
428
|
+
'tasks_releases',
|
|
429
|
+
['status'],
|
|
430
|
+
['REL-2', 'REL-3', 'REL-4', 'REL-5', 'REL-6', 'REL-7', 'REL-8'],
|
|
431
|
+
'shipped release statuses are absorbing',
|
|
432
|
+
),
|
|
433
|
+
rule(
|
|
434
|
+
'playbook.approval.once',
|
|
435
|
+
'T12944',
|
|
436
|
+
'tasks_playbook_approvals',
|
|
437
|
+
['status'],
|
|
438
|
+
['PB-1', 'PB-4', 'PB-7', 'P33'],
|
|
439
|
+
'an approval goes pending→decided once',
|
|
440
|
+
),
|
|
441
|
+
rule(
|
|
442
|
+
'docs.lifecycle.transitions',
|
|
443
|
+
'T12945',
|
|
444
|
+
'docs_attachments',
|
|
445
|
+
['lifecycle_status'],
|
|
446
|
+
['D04', 'D05', 'D31', 'D32', 'D39', 'D50'],
|
|
447
|
+
'superseded is sticky; lifecycle transitions follow a matrix',
|
|
448
|
+
),
|
|
449
|
+
|
|
450
|
+
// §3.6.0 identity-layer rows.
|
|
451
|
+
{
|
|
452
|
+
id: 'identity.local-minting',
|
|
453
|
+
class: 'identity-layer',
|
|
454
|
+
tables: ['tasks_tasks', 'tasks_sessions'],
|
|
455
|
+
sites: [],
|
|
456
|
+
inventory: ['V12', 'B07', 'B12'],
|
|
457
|
+
reason:
|
|
458
|
+
'ids minted from local state (display ids, integer PKs, device-derived ids) collide across replicas; T12341 uids and re-minting own it, no validator port can',
|
|
459
|
+
},
|
|
460
|
+
]);
|
package/src/nexus-account.ts
CHANGED
|
@@ -69,6 +69,33 @@ export const NEXUS_ACCOUNT_ERROR_CODES = [
|
|
|
69
69
|
'E_NEXUS_DEVICE_REQUIRED',
|
|
70
70
|
/** `.cleo/nexus-link.json` is in a format this CLEO cannot update (written by a newer CLEO). */
|
|
71
71
|
'E_NEXUS_LINK_FILE_UNSUPPORTED',
|
|
72
|
+
// Cloud vault (encrypted snapshots, T12336 / T12337 / T12338).
|
|
73
|
+
/** Another device holds this stream's write lease (`--force` takes it and labels a fork). */
|
|
74
|
+
'E_NEXUS_VAULT_LEASE_HELD',
|
|
75
|
+
/** Another device pushed a newer snapshot than this machine last pushed or restored: pull first. */
|
|
76
|
+
'E_NEXUS_VAULT_BEHIND',
|
|
77
|
+
/** Local data changed since the last push or restore; pulling would overwrite it. */
|
|
78
|
+
'E_NEXUS_VAULT_LOCAL_CHANGES',
|
|
79
|
+
/** A snapshot's counts or hashes do not match its manifest; nothing was activated. */
|
|
80
|
+
'E_NEXUS_VAULT_VERIFY_FAILED',
|
|
81
|
+
/** The account encryption key could not be obtained for this device. */
|
|
82
|
+
'E_NEXUS_VAULT_KEY_UNAVAILABLE',
|
|
83
|
+
/** The project is not linked to Cleo Nexus (`cleo project link`). */
|
|
84
|
+
'E_NEXUS_VAULT_NOT_LINKED',
|
|
85
|
+
/** The cloud holds no snapshot to restore. */
|
|
86
|
+
'E_NEXUS_VAULT_EMPTY',
|
|
87
|
+
/** The server refused the snapshot (lineage or regression check). */
|
|
88
|
+
'E_NEXUS_VAULT_REFUSED',
|
|
89
|
+
/**
|
|
90
|
+
* The stream moved past what this CLEO's vault writes (T13034): it takes only checkpoint/v3
|
|
91
|
+
* snapshots (the server's `E_STREAM_VERSION`), or it holds data of a newer sync schema.
|
|
92
|
+
* Pull, restore and verify still work; nothing was written.
|
|
93
|
+
*/
|
|
94
|
+
'E_NEXUS_VAULT_STREAM_UPGRADED',
|
|
95
|
+
/** Another CLEO process is writing to the store a restore would replace. */
|
|
96
|
+
'E_NEXUS_VAULT_STORE_BUSY',
|
|
97
|
+
/** A project restore target already holds a different project. */
|
|
98
|
+
'E_NEXUS_VAULT_TARGET_OCCUPIED',
|
|
72
99
|
] as const;
|
|
73
100
|
|
|
74
101
|
/** One of {@link NEXUS_ACCOUNT_ERROR_CODES}. */
|
package/src/nexus-cloud.ts
CHANGED
|
@@ -457,10 +457,26 @@ export interface CloudStatusResult {
|
|
|
457
457
|
local: CloudStatusLocal;
|
|
458
458
|
/** E3's answer (or the same shape composed from E2/E14/E15), or `null` when not signed in. */
|
|
459
459
|
remote: NexusCloudStatus | null;
|
|
460
|
+
/** This device's global store on the account's home stream (T12952); absent when not signed in. */
|
|
461
|
+
global?: CloudStatusGlobalStore;
|
|
460
462
|
/** Non-fatal problems. */
|
|
461
463
|
warnings: CloudWarning[];
|
|
462
464
|
}
|
|
463
465
|
|
|
466
|
+
/** `cleo cloud status`: this device's global store (the main brain) on Cleo Nexus (T12952). */
|
|
467
|
+
export interface CloudStatusGlobalStore {
|
|
468
|
+
/** The server lists home-stream replicas. */
|
|
469
|
+
supported: boolean;
|
|
470
|
+
/** This device's global store is attached. */
|
|
471
|
+
attached: boolean;
|
|
472
|
+
/** Its replica id. */
|
|
473
|
+
replicaId: string | null;
|
|
474
|
+
/** When its presence was last reported. */
|
|
475
|
+
presenceAt: string | null;
|
|
476
|
+
/** Devices with a global store attached to the account. */
|
|
477
|
+
devices: number;
|
|
478
|
+
}
|
|
479
|
+
|
|
464
480
|
/** `details` of the `E_NEXUS_UNREACHABLE` failure of `cleo cloud status` (§4.4 "Offline"). */
|
|
465
481
|
export interface CloudStatusOfflineDetails {
|
|
466
482
|
/** Local state. */
|
|
@@ -497,6 +513,8 @@ export const NEXUS_FORBIDDEN_REASONS = [
|
|
|
497
513
|
'bearer-session-required',
|
|
498
514
|
'session-not-fresh',
|
|
499
515
|
'device-limit',
|
|
516
|
+
'device-flow-session-required',
|
|
517
|
+
'device-not-enrolled',
|
|
500
518
|
] as const;
|
|
501
519
|
|
|
502
520
|
/** `details.revokedReason` values of a 401 `credential-revoked` (§4.0.4, v2.11). */
|
|
@@ -618,6 +636,17 @@ export const NEXUS_FORBIDDEN_ERRORS: Readonly<
|
|
|
618
636
|
message: 'the account has reached its device limit',
|
|
619
637
|
fix: 'revoke unused devices on cleocode.dev, then retry',
|
|
620
638
|
},
|
|
639
|
+
'device-flow-session-required': {
|
|
640
|
+
code: 'E_NEXUS_SESSION_EXPIRED',
|
|
641
|
+
message:
|
|
642
|
+
'this session was not issued to the CLI by `cleo login nexus`, so it cannot enrol a device',
|
|
643
|
+
fix: SESSION_EXPIRED_LOGIN.fix,
|
|
644
|
+
},
|
|
645
|
+
'device-not-enrolled': {
|
|
646
|
+
code: 'E_NEXUS_NOT_SIGNED_IN',
|
|
647
|
+
message: 'this device is not enrolled with Cleo Nexus',
|
|
648
|
+
fix: LOGIN,
|
|
649
|
+
},
|
|
621
650
|
};
|
|
622
651
|
|
|
623
652
|
/**
|