@cleocode/contracts 2026.10.4 → 2026.10.5
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/__tests__/cloud-replica-retirement.test.d.ts +10 -0
- package/dist/__tests__/cloud-replica-retirement.test.d.ts.map +1 -0
- package/dist/__tests__/cloud-replica-retirement.test.js +111 -0
- package/dist/__tests__/cloud-replica-retirement.test.js.map +1 -0
- package/dist/__tests__/cloud-status-checkpoint-reads.test.d.ts +10 -0
- package/dist/__tests__/cloud-status-checkpoint-reads.test.d.ts.map +1 -0
- package/dist/__tests__/cloud-status-checkpoint-reads.test.js +49 -0
- package/dist/__tests__/cloud-status-checkpoint-reads.test.js.map +1 -0
- package/dist/agent-registry.d.ts +5 -1
- package/dist/agent-registry.d.ts.map +1 -1
- package/dist/cloud/api.d.ts +156 -0
- package/dist/cloud/api.d.ts.map +1 -1
- package/dist/cloud/api.js +99 -1
- package/dist/cloud/api.js.map +1 -1
- package/dist/cloud/envelope.d.ts +3 -3
- package/dist/dispatch/operations-registry.js +1 -1
- package/dist/dispatch/operations-registry.js.map +1 -1
- package/dist/doctor.d.ts +76 -3
- package/dist/doctor.d.ts.map +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/invariants/sync-write-invariants.d.ts.map +1 -1
- package/dist/invariants/sync-write-invariants.js +31 -6
- package/dist/invariants/sync-write-invariants.js.map +1 -1
- package/dist/ledger/index.d.ts +211 -0
- package/dist/ledger/index.d.ts.map +1 -0
- package/dist/ledger/index.js +122 -0
- package/dist/ledger/index.js.map +1 -0
- package/dist/nexus-account.d.ts +9 -0
- package/dist/nexus-account.d.ts.map +1 -1
- package/dist/nexus-cloud.d.ts +171 -0
- package/dist/nexus-cloud.d.ts.map +1 -1
- package/dist/nexus-cloud.js +10 -0
- package/dist/nexus-cloud.js.map +1 -1
- package/dist/nexus-vault.d.ts +113 -1
- package/dist/nexus-vault.d.ts.map +1 -1
- package/dist/nexus-vault.js +27 -0
- package/dist/nexus-vault.js.map +1 -1
- package/dist/operations/admin.d.ts +14 -1
- package/dist/operations/admin.d.ts.map +1 -1
- package/dist/operations/docs-lifecycle.d.ts +23 -0
- package/dist/operations/docs-lifecycle.d.ts.map +1 -0
- package/dist/operations/docs-lifecycle.js +29 -0
- package/dist/operations/docs-lifecycle.js.map +1 -0
- package/dist/operations/docs.d.ts +2 -11
- package/dist/operations/docs.d.ts.map +1 -1
- package/dist/operations/docs.js +3 -17
- package/dist/operations/docs.js.map +1 -1
- package/dist/project-context.d.ts +7 -0
- package/dist/project-context.d.ts.map +1 -1
- package/dist/release/evidence-atoms.d.ts +15 -7
- package/dist/release/evidence-atoms.d.ts.map +1 -1
- package/dist/release/evidence-atoms.js +16 -12
- package/dist/release/evidence-atoms.js.map +1 -1
- package/dist/resource-governor.d.ts +39 -0
- package/dist/resource-governor.d.ts.map +1 -1
- package/dist/resource-governor.js.map +1 -1
- package/dist/row-identity.d.ts +81 -0
- package/dist/row-identity.d.ts.map +1 -1
- package/dist/store-restore.d.ts +66 -0
- package/dist/store-restore.d.ts.map +1 -0
- package/dist/store-restore.js +9 -0
- package/dist/store-restore.js.map +1 -0
- package/dist/task.d.ts +6 -0
- package/dist/task.d.ts.map +1 -1
- package/package.json +6 -2
- package/src/__tests__/cloud-replica-retirement.test.ts +135 -0
- package/src/__tests__/cloud-status-checkpoint-reads.test.ts +60 -0
- package/src/agent-registry.ts +5 -1
- package/src/cloud/api.ts +125 -1
- package/src/dispatch/operations-registry.ts +1 -1
- package/src/doctor.ts +82 -3
- package/src/index.ts +33 -0
- package/src/invariants/sync-write-invariants.ts +81 -42
- package/src/ledger/index.ts +141 -0
- package/src/nexus-account.ts +9 -0
- package/src/nexus-cloud.ts +186 -0
- package/src/nexus-vault.ts +124 -1
- package/src/operations/admin.ts +14 -1
- package/src/operations/conduit.ts +1 -1
- package/src/operations/docs-lifecycle.ts +31 -0
- package/src/operations/docs.ts +4 -19
- package/src/project-context.ts +7 -0
- package/src/release/evidence-atoms.ts +16 -12
- package/src/resource-governor.ts +40 -0
- package/src/row-identity.ts +98 -0
- package/src/store-restore.ts +70 -0
- package/src/task.ts +6 -0
package/src/nexus-vault.ts
CHANGED
|
@@ -46,6 +46,38 @@ export const nexusProjectKeysSchema = z.looseObject({
|
|
|
46
46
|
),
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
+
// ---------- wire: replica retirement refusals (cleo-nexus T123) ----------
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The `details.reason` values E31 (`POST …/replicas/:replicaId/retirements`) and the calls it guards answer with
|
|
53
|
+
* (cleo-nexus #41, contract v2.28):
|
|
54
|
+
* - `not-pinned-or-owner` (403): the caller is neither the replica's pinned device nor, on a project stream, an
|
|
55
|
+
* owner's device (on a home stream, any device of the account may retire).
|
|
56
|
+
* - `retire-below-head` (409): `lastReplicaSeq` is under the replica's highest appended replicaSeq, or null while
|
|
57
|
+
* the replica has a segment.
|
|
58
|
+
* - `retired` (409): the replica is already retired with another successor or `lastReplicaSeq` (the same pair
|
|
59
|
+
* again answers 200 with the stored record).
|
|
60
|
+
* - `successor-other-user` (403): on a project stream, the successor is pinned to another user's device.
|
|
61
|
+
* - `successor-taken` (409): another user's retirement already names that successor.
|
|
62
|
+
* - `successor-retired` (409): the successor is itself retired (this also refuses a cycle).
|
|
63
|
+
* - `replica-retired` (409, `E_CONFLICT`, with `lastReplicaSeq` and `successor`): a segment append past the
|
|
64
|
+
* retirement, or a lease request, for a retired replica.
|
|
65
|
+
* The server's strings, not a cloud wire schema: kept here, beside the vault's other wire constants, so the
|
|
66
|
+
* `@cleocode/contracts/cloud` mirror stays byte-for-byte the server's.
|
|
67
|
+
*/
|
|
68
|
+
export const NEXUS_REPLICA_RETIREMENT_REASONS = [
|
|
69
|
+
'not-pinned-or-owner',
|
|
70
|
+
'retire-below-head',
|
|
71
|
+
'retired',
|
|
72
|
+
'successor-other-user',
|
|
73
|
+
'successor-taken',
|
|
74
|
+
'successor-retired',
|
|
75
|
+
'replica-retired',
|
|
76
|
+
] as const;
|
|
77
|
+
|
|
78
|
+
/** One E31 refusal reason ({@link NEXUS_REPLICA_RETIREMENT_REASONS}). */
|
|
79
|
+
export type NexusReplicaRetirementReason = (typeof NEXUS_REPLICA_RETIREMENT_REASONS)[number];
|
|
80
|
+
|
|
49
81
|
// ---------- wire: leases (cleo-nexus T083) ----------
|
|
50
82
|
|
|
51
83
|
/** The lease role of the vault's single writer. */
|
|
@@ -195,15 +227,59 @@ export interface CloudRestoreResult {
|
|
|
195
227
|
tables: number;
|
|
196
228
|
/** Local safety backup taken before activation, if any. */
|
|
197
229
|
safetyBackup: string | null;
|
|
230
|
+
/**
|
|
231
|
+
* The placed file is a new store instance, so the replica this store had is
|
|
232
|
+
* retired and a new one bound (journal spec §1.5; T13109). The server keeps
|
|
233
|
+
* the retired one as history until S4 announces its retirement. `null` when
|
|
234
|
+
* nothing was placed or the store had no replica yet (a first restore here).
|
|
235
|
+
*
|
|
236
|
+
* `reason` is `vault-restore` when the retired replica was this device's
|
|
237
|
+
* replica of the replaced file (recorded as a retire candidate unless the
|
|
238
|
+
* registry write failed), `file-identity` or `foreign-device` when the store
|
|
239
|
+
* was a copy or another device's (the carried replica stays live where it
|
|
240
|
+
* belongs; nothing to retire), and `null` when the rebind's registry write
|
|
241
|
+
* failed.
|
|
242
|
+
*/
|
|
243
|
+
replica: {
|
|
244
|
+
retired: string;
|
|
245
|
+
current: string;
|
|
246
|
+
reason: 'vault-restore' | 'file-identity' | 'foreign-device' | null;
|
|
247
|
+
} | null;
|
|
198
248
|
warnings: CloudWarning[];
|
|
199
249
|
}
|
|
200
250
|
|
|
251
|
+
/**
|
|
252
|
+
* `cleo cloud verify --deep` (T13291): the cloud copy downloaded again and
|
|
253
|
+
* checked byte for byte. Local backups are checked by `cleo backup verify`.
|
|
254
|
+
*/
|
|
255
|
+
export interface CloudVerifyDeepCheck {
|
|
256
|
+
/**
|
|
257
|
+
* The head snapshot and each device's newest snapshot: its bundle downloaded,
|
|
258
|
+
* checked against the size and sha256 its signed checkpoint records, and
|
|
259
|
+
* decrypted. `problem` says why a check failed; `null` when it passed.
|
|
260
|
+
*/
|
|
261
|
+
snapshots: Array<{
|
|
262
|
+
checkpointId: string;
|
|
263
|
+
deviceId: string;
|
|
264
|
+
sizeBytes: number;
|
|
265
|
+
ok: boolean;
|
|
266
|
+
problem: string | null;
|
|
267
|
+
}>;
|
|
268
|
+
/**
|
|
269
|
+
* The journal segments after the head snapshot (from the start of the stream
|
|
270
|
+
* when there is none), each re-hashed, signature-checked and decrypted.
|
|
271
|
+
* `from` is the stream seq they start after; `checked` counts the segments
|
|
272
|
+
* that passed; `problem` is the first failure, `null` when none.
|
|
273
|
+
*/
|
|
274
|
+
segments: { from: number; checked: number; ok: boolean; problem: string | null };
|
|
275
|
+
}
|
|
276
|
+
|
|
201
277
|
/** `cleo cloud verify`. */
|
|
202
278
|
export interface CloudVerifyResult {
|
|
203
279
|
apiUrl: string;
|
|
204
280
|
scope: CloudVaultScope;
|
|
205
281
|
streamId: string;
|
|
206
|
-
/** `match`: local equals the head snapshot; `ahead`: local changed since this machine's last snapshot; `behind`: the cloud has a newer snapshot; `diverged`: both; `empty`: no snapshot yet; `untrusted`: the head snapshot's signature does not verify against a trusted device key (nothing is compared with it). */
|
|
282
|
+
/** `match`: local equals the head snapshot; `ahead`: local changed since this machine's last snapshot; `behind`: the cloud has a newer snapshot; `diverged`: both; `empty`: no snapshot yet; `untrusted`: the head snapshot's signature does not verify against a trusted device key (nothing is compared with it), or with `--deep`, its bundle or a segment after it failed the byte check. */
|
|
207
283
|
verdict: 'match' | 'ahead' | 'behind' | 'diverged' | 'empty' | 'untrusted';
|
|
208
284
|
/** What to do about a verdict other than `match` (or a failed integrity check); `null` when nothing. */
|
|
209
285
|
remedy: string | null;
|
|
@@ -222,6 +298,8 @@ export interface CloudVerifyResult {
|
|
|
222
298
|
createdAt: string | null;
|
|
223
299
|
matchesHead: boolean;
|
|
224
300
|
}>;
|
|
301
|
+
/** The byte-level check of `--deep`; absent without it. */
|
|
302
|
+
deep?: CloudVerifyDeepCheck;
|
|
225
303
|
warnings: CloudWarning[];
|
|
226
304
|
}
|
|
227
305
|
|
|
@@ -278,3 +356,48 @@ export interface CloudActivityResult {
|
|
|
278
356
|
nextBefore: string | null;
|
|
279
357
|
warnings: CloudWarning[];
|
|
280
358
|
}
|
|
359
|
+
|
|
360
|
+
/** One conflict an apply recorded (`cleo cloud conflicts`, T12344 PR-6). */
|
|
361
|
+
export interface CloudConflict {
|
|
362
|
+
id: number;
|
|
363
|
+
stream: string;
|
|
364
|
+
seq: number;
|
|
365
|
+
txnIdx: number;
|
|
366
|
+
/** Op index in the transaction; -1 for a whole-transaction (post-apply) conflict. */
|
|
367
|
+
opIdx: number;
|
|
368
|
+
/** `field`, `typed-rule`, `edit-vs-delete`, `delete-vs-edit`, `dangling-ref`, `guard`, `delete-with-live-children`, `uid-collision`, `post-apply` … */
|
|
369
|
+
kind: string;
|
|
370
|
+
table: string;
|
|
371
|
+
uid: string;
|
|
372
|
+
columns: string[];
|
|
373
|
+
/** The typed rule, guard message or invariant, when there is one. */
|
|
374
|
+
rule: string | null;
|
|
375
|
+
/** What happened to the incoming op: `incoming-applied`, `incoming-dropped`, `row-deleted`, `op-voided`. */
|
|
376
|
+
resolution: string;
|
|
377
|
+
opHlc: string;
|
|
378
|
+
localHlc: string | null;
|
|
379
|
+
/** The replica that wrote the op. */
|
|
380
|
+
origin: string;
|
|
381
|
+
createdAt: string;
|
|
382
|
+
resolvedAt: string | null;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/** `cleo cloud conflicts`: the store's recorded sync conflicts. */
|
|
386
|
+
export interface CloudConflictsResult {
|
|
387
|
+
scope: CloudVaultScope;
|
|
388
|
+
/** Unresolved conflicts in the store. */
|
|
389
|
+
open: number;
|
|
390
|
+
/** All conflicts in the store. */
|
|
391
|
+
total: number;
|
|
392
|
+
conflicts: CloudConflict[];
|
|
393
|
+
warnings: CloudWarning[];
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/** `cleo cloud conflicts resolve <id>`. */
|
|
397
|
+
export interface CloudConflictResolveResult {
|
|
398
|
+
scope: CloudVaultScope;
|
|
399
|
+
id: number;
|
|
400
|
+
/** False when no open conflict has that id. */
|
|
401
|
+
resolved: boolean;
|
|
402
|
+
warnings: CloudWarning[];
|
|
403
|
+
}
|
package/src/operations/admin.ts
CHANGED
|
@@ -108,6 +108,10 @@ export interface AdminBackupEntry {
|
|
|
108
108
|
note?: string;
|
|
109
109
|
/** File names captured in this backup. */
|
|
110
110
|
files: string[];
|
|
111
|
+
/** Which store it is of: `project` (`.cleo/cleo.db`) or `global` (`<CLEO_HOME>/cleo.db`, T13245). */
|
|
112
|
+
scope?: 'project' | 'global';
|
|
113
|
+
/** What its store file holds: `tasks`, `brain`, `conduit` (project) or `global` (T13245). */
|
|
114
|
+
contains?: string[];
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
/**
|
|
@@ -1194,7 +1198,10 @@ export type AdminAdrShowResult = AdminAdrRecord;
|
|
|
1194
1198
|
// ---------------------------------------------------------------------------
|
|
1195
1199
|
|
|
1196
1200
|
/** Parameters for `admin.backup` query — none required. */
|
|
1197
|
-
export
|
|
1201
|
+
export interface AdminBackupListParams {
|
|
1202
|
+
/** `project`, `global` or `all` (default `all`; T13245). */
|
|
1203
|
+
scope?: 'project' | 'global' | 'all';
|
|
1204
|
+
}
|
|
1198
1205
|
|
|
1199
1206
|
/** Result of `admin.backup` query. */
|
|
1200
1207
|
export interface AdminBackupListResult {
|
|
@@ -1653,6 +1660,10 @@ export interface AdminBackupMutateParams {
|
|
|
1653
1660
|
* project's live store (T12680). Without it such a restore is refused.
|
|
1654
1661
|
*/
|
|
1655
1662
|
confirmOwnerStore?: boolean;
|
|
1663
|
+
/** Create: also back up the global store (`<CLEO_HOME>/cleo.db`, T13245). */
|
|
1664
|
+
includeGlobal?: boolean;
|
|
1665
|
+
/** Restore by id: `global` restores a backup of the global store (T13245). */
|
|
1666
|
+
scope?: 'project' | 'global';
|
|
1656
1667
|
}
|
|
1657
1668
|
|
|
1658
1669
|
/** Result of `admin.backup` mutate (create action). */
|
|
@@ -1667,6 +1678,8 @@ export interface AdminBackupCreateResult {
|
|
|
1667
1678
|
type: string;
|
|
1668
1679
|
/** Files captured in the backup. */
|
|
1669
1680
|
files: string[];
|
|
1681
|
+
/** The global store's backup, when `includeGlobal` was set (T13245). */
|
|
1682
|
+
global?: AdminBackupCreateResult | null;
|
|
1670
1683
|
}
|
|
1671
1684
|
|
|
1672
1685
|
/** Result of `admin.backup` mutate (restore action — by ID). */
|
|
@@ -97,7 +97,7 @@ export interface ConduitStatusResult {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
// --------------------------------------------------------------------------
|
|
100
|
-
// conduit.peek → one-shot poll for new messages (
|
|
100
|
+
// conduit.peek → one-shot poll for new messages (never acks or consumes, T13169)
|
|
101
101
|
// --------------------------------------------------------------------------
|
|
102
102
|
|
|
103
103
|
/** Parameters for `conduit.peek`. */
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle statuses of a canonical doc.
|
|
3
|
+
*
|
|
4
|
+
* A leaf module with no schema imports: the operation registry
|
|
5
|
+
* (`dispatch/operations-registry.ts`) and the dispatch input sanitizer read
|
|
6
|
+
* these on every command, and `./docs.js` imports zod for its payload schemas.
|
|
7
|
+
* `./docs.js` re-exports both names.
|
|
8
|
+
*
|
|
9
|
+
* @task T10161
|
|
10
|
+
* @task T13126 - split out of operations/docs.ts
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Allowed `lifecycle_status` values mirrored from the attachments-table enum
|
|
15
|
+
* (`ATTACHMENT_LIFECYCLE_STATUSES` in
|
|
16
|
+
* `packages/core/src/store/schema/attachments.ts`). Kept inline here so the
|
|
17
|
+
* contract surface stays self-contained — the dispatch handler narrows raw
|
|
18
|
+
* `--status` input against this set before touching the store.
|
|
19
|
+
*
|
|
20
|
+
* @task T10161 (Epic T10157 / Saga T9855)
|
|
21
|
+
*/
|
|
22
|
+
export const DOCS_LIFECYCLE_STATUSES = [
|
|
23
|
+
'draft',
|
|
24
|
+
'proposed',
|
|
25
|
+
'accepted',
|
|
26
|
+
'superseded',
|
|
27
|
+
'archived',
|
|
28
|
+
'deprecated',
|
|
29
|
+
] as const;
|
|
30
|
+
|
|
31
|
+
export type DocsLifecycleStatus = (typeof DOCS_LIFECYCLE_STATUSES)[number];
|
package/src/operations/docs.ts
CHANGED
|
@@ -39,6 +39,7 @@ import type {
|
|
|
39
39
|
OperationExecutionIdentity,
|
|
40
40
|
} from '../jobs.js';
|
|
41
41
|
import type { DocAttachmentObservationPayload } from '../memory/observe.js';
|
|
42
|
+
import type { DocsLifecycleStatus } from './docs-lifecycle.js';
|
|
42
43
|
|
|
43
44
|
// ============================================================================
|
|
44
45
|
// Shared Attachment Types (API wire format)
|
|
@@ -686,25 +687,9 @@ export interface DocsRemoveResult {
|
|
|
686
687
|
// docs.update — UPDATE-in-place via slug (T10161)
|
|
687
688
|
// --------------------------------------------------------------------------
|
|
688
689
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
* `packages/core/src/store/schema/attachments.ts`). Kept inline here so the
|
|
693
|
-
* contract surface stays self-contained — the dispatch handler narrows raw
|
|
694
|
-
* `--status` input against this set before touching the store.
|
|
695
|
-
*
|
|
696
|
-
* @task T10161 (Epic T10157 / Saga T9855)
|
|
697
|
-
*/
|
|
698
|
-
export const DOCS_LIFECYCLE_STATUSES = [
|
|
699
|
-
'draft',
|
|
700
|
-
'proposed',
|
|
701
|
-
'accepted',
|
|
702
|
-
'superseded',
|
|
703
|
-
'archived',
|
|
704
|
-
'deprecated',
|
|
705
|
-
] as const;
|
|
706
|
-
|
|
707
|
-
export type DocsLifecycleStatus = (typeof DOCS_LIFECYCLE_STATUSES)[number];
|
|
690
|
+
// The lifecycle statuses live in `./docs-lifecycle.js`, a leaf without zod: the
|
|
691
|
+
// operation registry reads them on every dispatch (T13126).
|
|
692
|
+
export { DOCS_LIFECYCLE_STATUSES, type DocsLifecycleStatus } from './docs-lifecycle.js';
|
|
708
693
|
|
|
709
694
|
/**
|
|
710
695
|
* Parameters for `docs.update`.
|
package/src/project-context.ts
CHANGED
|
@@ -90,6 +90,13 @@ export interface ProjectEvidenceContext {
|
|
|
90
90
|
/** Job-name globs that must have run and succeeded for a code task's gate. */
|
|
91
91
|
jobs?: { tests?: string[]; qa?: string[] };
|
|
92
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* Repo-relative paths or globs (`path.matchesGlob`, `**` crosses directories)
|
|
95
|
+
* that never count as part of a change when evidence decides its scope —
|
|
96
|
+
* runtime state a tool writes into the checkout, e.g. `.opencode/goals/**`.
|
|
97
|
+
* CLEO's own installed hook files are always excluded (T13135, gh#1805).
|
|
98
|
+
*/
|
|
99
|
+
scopeExcludes?: string[];
|
|
93
100
|
}
|
|
94
101
|
|
|
95
102
|
/**
|
|
@@ -136,26 +136,30 @@ export const ghPrViewSchema = z
|
|
|
136
136
|
export type GhPrViewPayload = z.infer<typeof ghPrViewSchema>;
|
|
137
137
|
|
|
138
138
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
139
|
+
* cleocode's OWN required-check names: the `CI` aggregate of its ci.yml and the
|
|
140
|
+
* `Contracts Dep Lint` job, both real check names there (T13285; a test pins
|
|
141
|
+
* every name to a job or workflow in cleocode's `.github/workflows/`).
|
|
142
|
+
*
|
|
143
|
+
* This is cleocode's configuration, not a library default for other
|
|
144
|
+
* repositories. Since gh#1323 the evidence path (`resolveRequiredWorkflowsDetailed`)
|
|
145
|
+
* never applies it to another repository: with no env override, no
|
|
146
|
+
* `release.prRequiredWorkflows` in `.cleo/project-context.json` and no readable
|
|
147
|
+
* branch protection it returns tier `unknown` and refuses loudly. Only the
|
|
148
|
+
* synchronous `resolveRequiredWorkflows` still returns it as its last tier.
|
|
149
|
+
* cleocode itself resolves through its project-context entry. (`Lockfile Check`
|
|
150
|
+
* was dropped: no check ever reported under that name — its job is
|
|
151
|
+
* "Verify pnpm-lock.yaml consistency" — and CI now covers it, T13279.)
|
|
145
152
|
*
|
|
146
153
|
* @task T9764
|
|
154
|
+
* @task T13285
|
|
147
155
|
*/
|
|
148
|
-
export const PR_REQUIRED_WORKFLOWS: readonly string[] = Object.freeze([
|
|
149
|
-
'CI',
|
|
150
|
-
'Lockfile Check',
|
|
151
|
-
'Contracts Dep Lint',
|
|
152
|
-
]);
|
|
156
|
+
export const PR_REQUIRED_WORKFLOWS: readonly string[] = Object.freeze(['CI', 'Contracts Dep Lint']);
|
|
153
157
|
|
|
154
158
|
/**
|
|
155
159
|
* Name of the env var that overrides {@link PR_REQUIRED_WORKFLOWS}.
|
|
156
160
|
*
|
|
157
161
|
* Format: comma-separated workflow names (e.g.
|
|
158
|
-
* `"CI,
|
|
162
|
+
* `"CI,Contracts Dep Lint,MyExtraGate"`). Whitespace around commas is
|
|
159
163
|
* trimmed.
|
|
160
164
|
*
|
|
161
165
|
* @task T9764
|
package/src/resource-governor.ts
CHANGED
|
@@ -88,6 +88,41 @@ export const RUN_DEFERRED_EXIT_CODE = 75;
|
|
|
88
88
|
*/
|
|
89
89
|
export const RESOURCE_BACKPRESSURE_CODE = 'E_RESOURCE_BACKPRESSURE' as const;
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* The numbers behind a memory-pressure refusal of a heavy class (T13127).
|
|
93
|
+
*
|
|
94
|
+
* The memory gate refuses `test-run`, `scoped-build` and `full-build` while
|
|
95
|
+
* memory pressure is above {@link MemoryPressureReading.refuseAbove}; once it
|
|
96
|
+
* has refused, it admits again only when pressure falls to
|
|
97
|
+
* {@link MemoryPressureReading.resumeAtOrBelow} (hysteresis), so admission
|
|
98
|
+
* does not flap at the threshold.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const r: MemoryPressureReading = {
|
|
103
|
+
* score: 31, fullStall: 0, refuseAbove: 25, resumeAtOrBelow: 15, latched: false,
|
|
104
|
+
* memAvailableBytes: 21_000_000_000,
|
|
105
|
+
* summary: 'kernel level warn; 59% of RAM wired or compressed; swap 11.4 GiB used (24% of RAM)',
|
|
106
|
+
* };
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export interface MemoryPressureReading {
|
|
110
|
+
/** Memory pressure on the PSI `some avg10` scale (0–100). */
|
|
111
|
+
readonly score: number;
|
|
112
|
+
/** Memory `full avg10` stall (0–100); 0 when the platform reports none. */
|
|
113
|
+
readonly fullStall: number;
|
|
114
|
+
/** Heavy classes are refused while {@link score} is above this. */
|
|
115
|
+
readonly refuseAbove: number;
|
|
116
|
+
/** A refusing gate admits again once {@link score} is at or below this. */
|
|
117
|
+
readonly resumeAtOrBelow: number;
|
|
118
|
+
/** `true` when the gate was already refusing, so the resume threshold applied. */
|
|
119
|
+
readonly latched: boolean;
|
|
120
|
+
/** Available memory in bytes, when the platform reports it. */
|
|
121
|
+
readonly memAvailableBytes: number | null;
|
|
122
|
+
/** The platform signals in one line (kernel level, swap, compressor, PSI). */
|
|
123
|
+
readonly summary: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
91
126
|
/**
|
|
92
127
|
* Structured, retryable deferral returned when admission is denied. Never a
|
|
93
128
|
* silent drop; callers back off `retryAfterMs` and re-request, or annotate the
|
|
@@ -102,6 +137,11 @@ export interface ResourceDeferral {
|
|
|
102
137
|
readonly retryAfterMs: number;
|
|
103
138
|
/** Human-readable reason (pressure state, budget, held count). */
|
|
104
139
|
readonly reason: string;
|
|
140
|
+
/**
|
|
141
|
+
* Set when the deferral is a memory-pressure refusal of a heavy class rather
|
|
142
|
+
* than a full budget: the readings behind it (T13127).
|
|
143
|
+
*/
|
|
144
|
+
readonly memoryPressure?: MemoryPressureReading;
|
|
105
145
|
}
|
|
106
146
|
|
|
107
147
|
/**
|
package/src/row-identity.ts
CHANGED
|
@@ -130,3 +130,101 @@ export interface RowIdentitySpec {
|
|
|
130
130
|
/** Task that declared the table. */
|
|
131
131
|
readonly task: string;
|
|
132
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* One signal behind a store's row-identity share state (T13231).
|
|
136
|
+
*
|
|
137
|
+
* - `synced-marker`: `row_identity_synced` is set (a receive or a send happened);
|
|
138
|
+
* - `sync-flag`: `sync.seal`, `sync.push` or `sync.pull` is on;
|
|
139
|
+
* - `journal-rows`: a change-journal table holds rows (every `_sync_*` table
|
|
140
|
+
* except `_sync_meta`, `_sync_replica` and `_sync_clock`; they key rows by uid);
|
|
141
|
+
* - `journal-meta`: `_sync_meta` holds `suspect:` or `baseline:` keys;
|
|
142
|
+
* - `identity-aliases`: an alias table holds rows (uids were re-keyed);
|
|
143
|
+
* - `rebound`: the store was copied, moved or restored (`_sync_replica`);
|
|
144
|
+
* - `vault-pushed`: a vault push or restore of this root is recorded locally;
|
|
145
|
+
* - `nexus-linked-no-vault`: linked to Cleo Nexus with no local vault record;
|
|
146
|
+
* - `nexus-checkpoint`: Cleo Nexus holds a checkpoint or journal segment of the project;
|
|
147
|
+
* - `nexus-unreachable`: Cleo Nexus could not be asked;
|
|
148
|
+
* - `unreadable`: a record needed for the verdict could not be read.
|
|
149
|
+
*/
|
|
150
|
+
export type RowIdentityShareSignalCode =
|
|
151
|
+
| 'synced-marker'
|
|
152
|
+
| 'sync-flag'
|
|
153
|
+
| 'journal-rows'
|
|
154
|
+
| 'journal-meta'
|
|
155
|
+
| 'identity-aliases'
|
|
156
|
+
| 'rebound'
|
|
157
|
+
| 'vault-pushed'
|
|
158
|
+
| 'nexus-linked-no-vault'
|
|
159
|
+
| 'nexus-checkpoint'
|
|
160
|
+
| 'nexus-unreachable'
|
|
161
|
+
| 'unreadable';
|
|
162
|
+
|
|
163
|
+
/** One signal behind a share verdict (T13231). */
|
|
164
|
+
export interface RowIdentityShareSignal {
|
|
165
|
+
readonly code: RowIdentityShareSignalCode;
|
|
166
|
+
/** `shared`: the uids may have left the store; `unknown`: that cannot be ruled out. */
|
|
167
|
+
readonly kind: 'shared' | 'unknown';
|
|
168
|
+
readonly detail: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Whether a store's row identity may have left it (T13231).
|
|
173
|
+
*
|
|
174
|
+
* - `shared`: its uids have, or may have, reached another store;
|
|
175
|
+
* - `unknown`: that cannot be ruled out;
|
|
176
|
+
* - `unshared`: provably local, so a from-scratch refill changes nothing any
|
|
177
|
+
* other store holds.
|
|
178
|
+
*/
|
|
179
|
+
export interface RowIdentityShareState {
|
|
180
|
+
readonly state: 'shared' | 'unknown' | 'unshared';
|
|
181
|
+
/** Every signal that decided it (empty when unshared). */
|
|
182
|
+
readonly signals: readonly RowIdentityShareSignal[];
|
|
183
|
+
/** The signals' details, one line each (empty when unshared). */
|
|
184
|
+
readonly reasons: readonly string[];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** What Cleo Nexus answered for one linked origin (`cleo doctor row-identity`, T13231). */
|
|
188
|
+
export interface RowIdentityNexusAnswer {
|
|
189
|
+
readonly apiUrl: string;
|
|
190
|
+
readonly remoteProjectId: string;
|
|
191
|
+
readonly streamId: string;
|
|
192
|
+
/** `none`: no checkpoint and no journal segment; `present`: some; `error`: not answered. */
|
|
193
|
+
readonly answer: 'none' | 'present' | 'error';
|
|
194
|
+
readonly checkpoints: number;
|
|
195
|
+
readonly headSeq: number;
|
|
196
|
+
/** Why it was not answered (`error` only). */
|
|
197
|
+
readonly error?: string;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Rows whose identity a full refill re-derives, per table. */
|
|
201
|
+
export type RowIdentityRefillCounts = Readonly<Record<string, number>>;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* `cleo doctor row-identity --refill`: the verdict, its evidence and the plan
|
|
205
|
+
* (T13231). `applied` is false on a dry run and on a refusal.
|
|
206
|
+
*/
|
|
207
|
+
export interface RowIdentityRefillReport {
|
|
208
|
+
readonly projectRoot: string;
|
|
209
|
+
/** Whether the store's recipe marker is current (no refill is due). */
|
|
210
|
+
readonly recipeCurrent: boolean;
|
|
211
|
+
/** Whether `CLEO_ROW_UID_FILL` is on in this process (`--apply` needs it). */
|
|
212
|
+
readonly fillEnabled: boolean;
|
|
213
|
+
/** The local verdict (link, vault state, journal state). */
|
|
214
|
+
readonly local: RowIdentityShareState;
|
|
215
|
+
/** Cleo Nexus, one answer per linked origin (empty when unlinked). */
|
|
216
|
+
readonly nexus: readonly RowIdentityNexusAnswer[];
|
|
217
|
+
/** The verdict with the Nexus answers folded in. */
|
|
218
|
+
readonly verdict: RowIdentityShareState;
|
|
219
|
+
/** Rows per declared table that carry an identity value the refill clears. */
|
|
220
|
+
readonly planned: RowIdentityRefillCounts;
|
|
221
|
+
/** `refill`: due and allowed; `none`: nothing to do; `refuse`: shared or unknown. */
|
|
222
|
+
readonly action: 'refill' | 'none' | 'refuse';
|
|
223
|
+
/** What to do next, one line each. */
|
|
224
|
+
readonly remedy: readonly string[];
|
|
225
|
+
readonly applied: boolean;
|
|
226
|
+
/** `--apply`: the pre-refill snapshot. */
|
|
227
|
+
readonly snapshot: string | null;
|
|
228
|
+
/** `--apply`: how to undo the refill. */
|
|
229
|
+
readonly undo: string | null;
|
|
230
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cleo restore backup --snapshot <file> | --id <backupId>`: restore the live
|
|
3
|
+
* project `cleo.db` (which holds the tasks AND brain tables) from a named
|
|
4
|
+
* snapshot or a backup id, safely (T13240).
|
|
5
|
+
*
|
|
6
|
+
* @task T13240
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Where the snapshot came from. */
|
|
10
|
+
export interface StoreRestoreSource {
|
|
11
|
+
/** `snapshot`: a file named on the command line; `backup`: a backup id from `cleo backup list`. */
|
|
12
|
+
readonly kind: 'snapshot' | 'backup';
|
|
13
|
+
/** The file restored from (resolved, absolute). */
|
|
14
|
+
readonly path: string;
|
|
15
|
+
/** The backup id (`backup` only). */
|
|
16
|
+
readonly backupId: string | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** What the snapshot verification found. */
|
|
20
|
+
export interface StoreRestoreVerification {
|
|
21
|
+
/** `PRAGMA integrity_check` on a private copy of the snapshot: `ok`, or the first problems. */
|
|
22
|
+
readonly integrity: string;
|
|
23
|
+
/** Tasks in the snapshot (`tasks_tasks`). */
|
|
24
|
+
readonly tasks: number;
|
|
25
|
+
readonly sizeBytes: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The live store file that was replaced, kept so the restore can be undone. */
|
|
29
|
+
export interface StoreRestoreKept {
|
|
30
|
+
/** Backup id the kept file is listed under (`cleo backup list`), restorable with `--id`. */
|
|
31
|
+
readonly backupId: string;
|
|
32
|
+
/** The kept store file. */
|
|
33
|
+
readonly path: string;
|
|
34
|
+
/** Whether the live WAL was folded into the kept file (otherwise its raw sidecars are kept beside it). */
|
|
35
|
+
readonly checkpointed: boolean;
|
|
36
|
+
/** Raw sidecar copies kept beside it (`-wal`, `-shm`, `-journal`), when not checkpointed. */
|
|
37
|
+
readonly sidecars: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Result of a store restore (or its dry run). */
|
|
41
|
+
export interface StoreRestoreResult {
|
|
42
|
+
readonly dryRun: boolean;
|
|
43
|
+
/** `project` (`.cleo/cleo.db`) or `global` (`<CLEO_HOME>/cleo.db`, T13245). */
|
|
44
|
+
readonly scope: 'project' | 'global';
|
|
45
|
+
readonly restored: boolean;
|
|
46
|
+
/** The live store file. */
|
|
47
|
+
readonly target: string;
|
|
48
|
+
readonly source: StoreRestoreSource;
|
|
49
|
+
readonly verification: StoreRestoreVerification;
|
|
50
|
+
/** The replaced store, kept (`null` on a dry run, or when there was no live store). */
|
|
51
|
+
readonly kept: StoreRestoreKept | null;
|
|
52
|
+
/** Live-file sidecars removed so the restored file is not read with the old journal. */
|
|
53
|
+
readonly removedSidecars: readonly string[];
|
|
54
|
+
/** How to undo the restore (`null` on a dry run, or when nothing was replaced). */
|
|
55
|
+
readonly undo: string | null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* `cleo backup recover tasks|brain|conduit`: the freshest valid snapshot of the
|
|
60
|
+
* project store, restored through the same safe path as `cleo restore backup`
|
|
61
|
+
* (T13245). All three roles name the one live file, `.cleo/cleo.db`.
|
|
62
|
+
*/
|
|
63
|
+
export interface StoreRecoverResult extends StoreRestoreResult {
|
|
64
|
+
/** The role asked for (`tasks`, `brain` or `conduit`); the store restored is `cleo.db` either way. */
|
|
65
|
+
readonly role: string;
|
|
66
|
+
/** Snapshots that failed `PRAGMA quick_check` before one passed (newest first). */
|
|
67
|
+
readonly rejected: readonly string[];
|
|
68
|
+
/** Hours between the chosen snapshot and now, or `null` when its time is unknown. */
|
|
69
|
+
readonly dataLossWindowHours: number | null;
|
|
70
|
+
}
|
package/src/task.ts
CHANGED
|
@@ -540,6 +540,12 @@ export type EvidenceAtom =
|
|
|
540
540
|
* vouches for a PR that was not itself green.
|
|
541
541
|
*/
|
|
542
542
|
descendantPrHeadSha?: string;
|
|
543
|
+
/**
|
|
544
|
+
* The PR edited a pinned workflow, so only default-branch `push` runs
|
|
545
|
+
* attested it; its own `pull_request` runs (which ran the edited
|
|
546
|
+
* workflow) were never consulted, and none is re-checked (T13174).
|
|
547
|
+
*/
|
|
548
|
+
mainOnly?: boolean;
|
|
543
549
|
/** Task the PR was verified to be linked to (T12634). */
|
|
544
550
|
taskId?: string;
|
|
545
551
|
/** Component PR the task is linked through; its CI ran as this integration PR's (T12671). */
|