@cleocode/contracts 2026.10.21 → 2026.10.22
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__/memory-wire-shapes.test.js.map +1 -1
- package/dist/boundary.d.ts.map +1 -1
- package/dist/boundary.js +2 -17
- package/dist/boundary.js.map +1 -1
- package/dist/config.d.ts +67 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/dispatch/operations-registry.d.ts.map +1 -1
- package/dist/dispatch/operations-registry.js +25 -0
- package/dist/dispatch/operations-registry.js.map +1 -1
- package/dist/evidence-atom-schema.d.ts +4 -0
- package/dist/evidence-atom-schema.d.ts.map +1 -1
- package/dist/evidence-atom-schema.js +25 -7
- package/dist/evidence-atom-schema.js.map +1 -1
- package/dist/exit-codes.d.ts +24 -1
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/exit-codes.js +23 -0
- package/dist/exit-codes.js.map +1 -1
- package/dist/facade.d.ts +1 -1
- package/dist/facade.d.ts.map +1 -1
- package/dist/facade.js +2 -0
- package/dist/facade.js.map +1 -1
- package/dist/git-hooks.d.ts +7 -2
- package/dist/git-hooks.d.ts.map +1 -1
- package/dist/git-hooks.js +5 -2
- package/dist/git-hooks.js.map +1 -1
- package/dist/graph.d.ts +91 -0
- package/dist/graph.d.ts.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lean.d.ts +27 -0
- package/dist/lean.d.ts.map +1 -1
- package/dist/memory/observe.d.ts +20 -8
- package/dist/memory/observe.d.ts.map +1 -1
- package/dist/operations/docs.d.ts +2 -1
- package/dist/operations/docs.d.ts.map +1 -1
- package/dist/operations/docs.js +8 -2
- package/dist/operations/docs.js.map +1 -1
- package/dist/operations/nexus.d.ts +19 -1
- package/dist/operations/nexus.d.ts.map +1 -1
- package/dist/operations/session.d.ts +6 -0
- package/dist/operations/session.d.ts.map +1 -1
- package/dist/resource-governor.d.ts +26 -0
- package/dist/resource-governor.d.ts.map +1 -1
- package/dist/resource-governor.js.map +1 -1
- package/dist/task.d.ts +21 -0
- package/dist/task.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
- package/src/__tests__/memory-wire-shapes.test.ts +3 -0
- package/src/boundary.ts +2 -18
- package/src/config.ts +69 -1
- package/src/dispatch/operations-registry.ts +26 -0
- package/src/evidence-atom-schema.ts +26 -8
- package/src/exit-codes.ts +25 -0
- package/src/facade.ts +2 -0
- package/src/git-hooks.ts +5 -2
- package/src/graph.ts +92 -0
- package/src/index.ts +8 -0
- package/src/lean.ts +29 -0
- package/src/memory/observe.ts +20 -8
- package/src/operations/docs.ts +8 -2
- package/src/operations/nexus.ts +20 -1
- package/src/operations/session.ts +6 -0
- package/src/resource-governor.ts +28 -0
- package/src/task.ts +21 -0
|
@@ -224,6 +224,9 @@ type _ObserveBrainResultShape = {
|
|
|
224
224
|
createdAt: string;
|
|
225
225
|
typeSource?: 'caller' | 'keyword' | 'system-one';
|
|
226
226
|
typeConfidence?: number;
|
|
227
|
+
stored?: false;
|
|
228
|
+
gateAction?: 'merged' | 'pending' | 'rejected';
|
|
229
|
+
gateReason?: string;
|
|
227
230
|
};
|
|
228
231
|
|
|
229
232
|
type _AssertObserveResultPinned = AssertEquals1<
|
package/src/boundary.ts
CHANGED
|
@@ -300,22 +300,6 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
|
|
|
300
300
|
rationale:
|
|
301
301
|
'Verified-dead code: 531 LOC Rust crate referenced ONLY by packages/core/src/llm/rust/__tests__/. Production transports (chat-completions/gemini/openai) import StreamingThinkScrubber from the TS think-scrubber.js path. CLEO_USE_RUST gate is never set in any workflow. Scheduled for deletion in T10205; see ADR-078 implementation tasks.',
|
|
302
302
|
},
|
|
303
|
-
{
|
|
304
|
-
module: 'cleo-conduit-core',
|
|
305
|
-
intent: 'data-manifest',
|
|
306
|
-
rustCore: 'crates/cleo-conduit-core',
|
|
307
|
-
canonicalHome: 'cleocode',
|
|
308
|
-
perfBudget: {
|
|
309
|
-
latency_p50_ms: 10,
|
|
310
|
-
},
|
|
311
|
-
safetyBudget: {
|
|
312
|
-
panic_unwind: 'forbidden',
|
|
313
|
-
root_escape: 'forbidden',
|
|
314
|
-
},
|
|
315
|
-
amendments: [],
|
|
316
|
-
rationale:
|
|
317
|
-
'Conduit serde-types crate (681 LOC) — renamed from conduit-core to cleo-conduit-core (T10185, saga T10180) because conduit-core is squatted on crates.io. Stable contract surface consumed by cant-core, signaldock-protocol, signaldock-core, integration-tests, AND external /mnt/projects/signaldock-core + /mnt/projects/signaldock/backend via git deps. Mirrors @cleocode/contracts/conduit.ts. Forced KEEP+publish=true: deleting breaks production, and signaldock-protocol publish requires it.',
|
|
318
|
-
},
|
|
319
303
|
{
|
|
320
304
|
module: 'cleo-supervisor',
|
|
321
305
|
intent: 'scaffold-pending-consumer',
|
|
@@ -349,7 +333,7 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
|
|
|
349
333
|
},
|
|
350
334
|
amendments: [],
|
|
351
335
|
rationale:
|
|
352
|
-
'Internal Rust test harness over lafs-core/cleo-conduit-core
|
|
336
|
+
'Internal Rust test harness over lafs-core/cant-core (cleo-conduit-core removed in T13954). publish=false intentional — test code never ships.',
|
|
353
337
|
},
|
|
354
338
|
{
|
|
355
339
|
module: 'lafs-core',
|
|
@@ -367,7 +351,7 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
|
|
|
367
351
|
},
|
|
368
352
|
amendments: [],
|
|
369
353
|
rationale:
|
|
370
|
-
'LAFS envelope serialization core (1,432 LOC) consumed by
|
|
354
|
+
'LAFS envelope serialization core (1,432 LOC) consumed by signaldock-protocol, lafs-napi, integration-tests. Mirrors @cleocode/lafs/src/envelope.ts. Flip publish=true to expose canonical envelope spec as a Rust library.',
|
|
371
355
|
},
|
|
372
356
|
{
|
|
373
357
|
module: 'lafs-napi',
|
package/src/config.ts
CHANGED
|
@@ -126,6 +126,14 @@ export interface VerificationConfig {
|
|
|
126
126
|
maxRounds: number;
|
|
127
127
|
/** Gate names required for completion. */
|
|
128
128
|
requiredGates: string[];
|
|
129
|
+
/**
|
|
130
|
+
* Gates required per evidence kind (T14001), overriding `requiredGates`
|
|
131
|
+
* for that kind. Defaults: research and documentation get `requiredGates`
|
|
132
|
+
* without `testsPassed`/`qaPassed`; code gets `requiredGates`. A task whose
|
|
133
|
+
* implemented evidence or change set touches any non-document path owes
|
|
134
|
+
* the code gates whatever its kind.
|
|
135
|
+
*/
|
|
136
|
+
requiredGatesByKind?: Partial<Record<'code' | 'documentation' | 'research', string[]>>;
|
|
129
137
|
}
|
|
130
138
|
|
|
131
139
|
/** Lifecycle enforcement mode. */
|
|
@@ -159,6 +167,18 @@ export interface LifecycleConfig {
|
|
|
159
167
|
* @defaultValue 'critical'
|
|
160
168
|
*/
|
|
161
169
|
depsRequiredAt?: DepsRequiredAt;
|
|
170
|
+
/**
|
|
171
|
+
* Parent-epic stage gate, separate from {@link LifecycleConfig.mode}. When
|
|
172
|
+
* `strict`, a child cannot complete while its parent epic sits in a
|
|
173
|
+
* planning stage, an epic cannot advance while children sit at its stage,
|
|
174
|
+
* and a child cannot pass its epic's stage; `advisory` only warns. Unset
|
|
175
|
+
* means `off`: those three checks are opt-in, because together they can
|
|
176
|
+
* strand an epic's children (T13969). Evidence strictness stays on `mode`.
|
|
177
|
+
*
|
|
178
|
+
* @task T13970
|
|
179
|
+
* @defaultValue 'off'
|
|
180
|
+
*/
|
|
181
|
+
stageGate?: LifecycleEnforcementMode;
|
|
162
182
|
}
|
|
163
183
|
|
|
164
184
|
/** Sharing mode: whether .cleo/ files are committed to the project git repo. */
|
|
@@ -777,8 +797,10 @@ export interface NexusAutoRefreshConfig {
|
|
|
777
797
|
*/
|
|
778
798
|
enabled?: boolean;
|
|
779
799
|
/**
|
|
780
|
-
*
|
|
800
|
+
* Retired (T14014) and ignored: a query refreshes inline whenever the
|
|
801
|
+
* size-scaled cost estimate fits `budgetMs`.
|
|
781
802
|
*
|
|
803
|
+
* @deprecated The cost estimate against `budgetMs` decides.
|
|
782
804
|
* @defaultValue 25
|
|
783
805
|
*/
|
|
784
806
|
maxFiles?: number;
|
|
@@ -792,10 +814,56 @@ export interface NexusAutoRefreshConfig {
|
|
|
792
814
|
budgetMs?: number;
|
|
793
815
|
}
|
|
794
816
|
|
|
817
|
+
/**
|
|
818
|
+
* When a graph answer from a stale index is refused (T14008).
|
|
819
|
+
*
|
|
820
|
+
* @task T14008
|
|
821
|
+
*/
|
|
822
|
+
export interface NexusFreshnessConfig {
|
|
823
|
+
/**
|
|
824
|
+
* Most commits the index may trail the checked-out HEAD before graph
|
|
825
|
+
* queries fail with `E_NEXUS_INDEX_STALE` (`--allow-stale` or
|
|
826
|
+
* `CLEO_NEXUS_ALLOW_STALE=1` accepts the stale answer).
|
|
827
|
+
*
|
|
828
|
+
* @defaultValue 50
|
|
829
|
+
*/
|
|
830
|
+
maxCommitsBehind?: number;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Background reindex after a merge into the base branch (T14010). Started by
|
|
835
|
+
* the CLEO `post-merge` hook only when the `cleo.nexusReindexOnMerge` git
|
|
836
|
+
* marker is set (`cleo nexus reindex-on-merge --enable` sets both); runs at
|
|
837
|
+
* the lowest admission priority, one at a time per project, and records its
|
|
838
|
+
* outcome for `cleo nexus status`.
|
|
839
|
+
*
|
|
840
|
+
* @task T14010
|
|
841
|
+
*/
|
|
842
|
+
export interface NexusReindexOnMergeConfig {
|
|
843
|
+
/**
|
|
844
|
+
* Whether a merge into the base branch starts a background reindex. Off by
|
|
845
|
+
* default: a heavy analysis after every merge must be a choice.
|
|
846
|
+
*
|
|
847
|
+
* @defaultValue false
|
|
848
|
+
*/
|
|
849
|
+
enabled?: boolean;
|
|
850
|
+
/**
|
|
851
|
+
* Seconds the reindex keeps trying for `cleo run` admission (never queueing,
|
|
852
|
+
* only when nothing else waits) before it is recorded as deferred.
|
|
853
|
+
*
|
|
854
|
+
* @defaultValue 300
|
|
855
|
+
*/
|
|
856
|
+
timeoutSeconds?: number;
|
|
857
|
+
}
|
|
858
|
+
|
|
795
859
|
/** Code-graph (`cleo nexus`) configuration. */
|
|
796
860
|
export interface NexusConfig {
|
|
861
|
+
/** Background reindex after a merge into the base branch (T14010). */
|
|
862
|
+
reindexOnMerge?: NexusReindexOnMergeConfig;
|
|
797
863
|
/** Inline refresh of a slightly stale index before graph queries answer. */
|
|
798
864
|
autoRefresh?: NexusAutoRefreshConfig;
|
|
865
|
+
/** When an answer from a stale index is refused instead of returned. */
|
|
866
|
+
freshness?: NexusFreshnessConfig;
|
|
799
867
|
}
|
|
800
868
|
|
|
801
869
|
/**
|
|
@@ -4815,6 +4815,32 @@ export const OPERATIONS: OperationDef[] = [
|
|
|
4815
4815
|
},
|
|
4816
4816
|
],
|
|
4817
4817
|
},
|
|
4818
|
+
// T13949 — curated lessons index (D11181)
|
|
4819
|
+
{
|
|
4820
|
+
gateway: 'query',
|
|
4821
|
+
domain: 'memory',
|
|
4822
|
+
operation: 'lessons',
|
|
4823
|
+
description:
|
|
4824
|
+
"memory.lessons (query) — list current type='lesson' observations (one-line rule + why), newest first, superseded hidden",
|
|
4825
|
+
tier: 1,
|
|
4826
|
+
idempotent: true,
|
|
4827
|
+
sessionRequired: false,
|
|
4828
|
+
requiredParams: [],
|
|
4829
|
+
params: [
|
|
4830
|
+
{
|
|
4831
|
+
name: 'scope',
|
|
4832
|
+
type: 'string',
|
|
4833
|
+
required: false,
|
|
4834
|
+
description: 'Store to read: project, global (the main brain) or all (default)',
|
|
4835
|
+
},
|
|
4836
|
+
{
|
|
4837
|
+
name: 'limit',
|
|
4838
|
+
type: 'number',
|
|
4839
|
+
required: false,
|
|
4840
|
+
description: 'Max lessons to return (default: 50)',
|
|
4841
|
+
},
|
|
4842
|
+
],
|
|
4843
|
+
},
|
|
4818
4844
|
// T1006 — long-poll stream of recent brain writes (SSE-style polling stub)
|
|
4819
4845
|
{
|
|
4820
4846
|
gateway: 'query',
|
|
@@ -228,6 +228,15 @@ export const prAtomSchema = z.object({
|
|
|
228
228
|
kind: z.literal('pr'),
|
|
229
229
|
prNumber: z.number().int().positive('pr atom requires a positive integer PR number'),
|
|
230
230
|
componentPrNumber: z.number().int().positive().optional(),
|
|
231
|
+
/**
|
|
232
|
+
* `pr:<owner>/<repo>#<n>` (T13912): the GitHub repository the PR number
|
|
233
|
+
* belongs to, lowercased. Validated against the evidence git root's
|
|
234
|
+
* `origin`, since PR numbers are only unique per repository.
|
|
235
|
+
*/
|
|
236
|
+
repo: z
|
|
237
|
+
.string()
|
|
238
|
+
.regex(/^[a-z0-9._-]+\/[a-z0-9._-]+$/)
|
|
239
|
+
.optional(),
|
|
231
240
|
});
|
|
232
241
|
|
|
233
242
|
/**
|
|
@@ -246,6 +255,11 @@ export const ciAtomSchema = z.object({
|
|
|
246
255
|
kind: z.literal('ci'),
|
|
247
256
|
prNumber: z.number().int().positive('ci atom requires a positive integer PR number'),
|
|
248
257
|
componentPrNumber: z.number().int().positive().optional(),
|
|
258
|
+
/** `ci:<owner>/<repo>#<n>`, as for `pr:` (T13912). */
|
|
259
|
+
repo: z
|
|
260
|
+
.string()
|
|
261
|
+
.regex(/^[a-z0-9._-]+\/[a-z0-9._-]+$/)
|
|
262
|
+
.optional(),
|
|
249
263
|
});
|
|
250
264
|
|
|
251
265
|
/**
|
|
@@ -803,18 +817,22 @@ export class EvidenceParseError extends Error {
|
|
|
803
817
|
* @adr ADR-051
|
|
804
818
|
*/
|
|
805
819
|
/**
|
|
806
|
-
* `<n>` or `<component>@<integration>` (T12671), each a positive integer
|
|
807
|
-
* The integration PR is the
|
|
820
|
+
* `<n>` or `<component>@<integration>` (T12671), each a positive integer,
|
|
821
|
+
* optionally prefixed `<owner>/<repo>#` (T13912). The integration PR is the
|
|
822
|
+
* atom's `prNumber`.
|
|
808
823
|
*/
|
|
809
824
|
function parsePrReference(
|
|
810
825
|
payload: string,
|
|
811
|
-
): { prNumber: number; componentPrNumber?: number } | null {
|
|
812
|
-
const match = /^(\d+)(?:@(\d+))?$/.exec(payload);
|
|
826
|
+
): { prNumber: number; componentPrNumber?: number; repo?: string } | null {
|
|
827
|
+
const match = /^(?:([A-Za-z0-9._-]+\/[A-Za-z0-9._-]+)#)?(\d+)(?:@(\d+))?$/.exec(payload);
|
|
813
828
|
if (!match) return null;
|
|
814
|
-
const
|
|
815
|
-
const
|
|
829
|
+
const repo = match[1] === undefined ? {} : { repo: match[1].toLowerCase() };
|
|
830
|
+
const first = Number(match[2]);
|
|
831
|
+
const second = match[3] === undefined ? null : Number(match[3]);
|
|
816
832
|
if (first <= 0 || (second !== null && (second <= 0 || second === first))) return null;
|
|
817
|
-
return second === null
|
|
833
|
+
return second === null
|
|
834
|
+
? { prNumber: first, ...repo }
|
|
835
|
+
: { prNumber: second, componentPrNumber: first, ...repo };
|
|
818
836
|
}
|
|
819
837
|
|
|
820
838
|
export function parseEvidenceString(raw: string): EvidenceAtom[] {
|
|
@@ -895,7 +913,7 @@ export function parseEvidenceString(raw: string): EvidenceAtom[] {
|
|
|
895
913
|
if (!ref) {
|
|
896
914
|
throw new EvidenceParseError(
|
|
897
915
|
`${kind} atom requires a positive integer PR number, got "${payload}" in "${chunk}"`,
|
|
898
|
-
`Use format: ${kind}:<number> e.g. ${kind}:357,
|
|
916
|
+
`Use format: ${kind}:<number> e.g. ${kind}:357, ${kind}:<component>@<integration> e.g. ${kind}:356@357, or ${kind}:<owner>/<repo>#<number>`,
|
|
899
917
|
);
|
|
900
918
|
}
|
|
901
919
|
atoms.push(kind === 'pr' ? { kind: 'pr', ...ref } : { kind: 'ci', ...ref });
|
package/src/exit-codes.ts
CHANGED
|
@@ -325,6 +325,31 @@ export enum ExitCode {
|
|
|
325
325
|
* @saga T10538 (PM-Core V2 agent-trust)
|
|
326
326
|
*/
|
|
327
327
|
CANCELLED_CHILD_NO_WAIVER = 109,
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* E_OBSERVE_NOT_STORED — `cleo memory observe` stored nothing: the
|
|
331
|
+
* extraction gate merged the text into an existing entry (a duplicate),
|
|
332
|
+
* deferred it or rejected it.
|
|
333
|
+
*
|
|
334
|
+
* `error.details.id` names the existing entry for a duplicate, so a script
|
|
335
|
+
* can treat this code as "already known" and keep that id.
|
|
336
|
+
*
|
|
337
|
+
* @codeName E_OBSERVE_NOT_STORED
|
|
338
|
+
* @task T13949
|
|
339
|
+
*/
|
|
340
|
+
OBSERVE_NOT_STORED = 110,
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* E_NEXUS_INDEX_STALE — a `cleo nexus` graph answer was refused because the
|
|
344
|
+
* index trails the checked-out HEAD by more than
|
|
345
|
+
* `nexus.freshness.maxCommitsBehind`, or the queried symbol is missing from
|
|
346
|
+
* an index that is stale for this checkout. `error.fix` names the reindex command; `--allow-stale` (or
|
|
347
|
+
* `CLEO_NEXUS_ALLOW_STALE=1`) accepts the stale answer.
|
|
348
|
+
*
|
|
349
|
+
* @codeName E_NEXUS_INDEX_STALE
|
|
350
|
+
* @task T14008
|
|
351
|
+
*/
|
|
352
|
+
NEXUS_INDEX_STALE = 111,
|
|
328
353
|
}
|
|
329
354
|
|
|
330
355
|
// ---------------------------------------------------------------------------
|
package/src/facade.ts
CHANGED
package/src/git-hooks.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/** Hash-guarded Git hook installation and recovery contracts. */
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Canonical names of the shipped Git hooks: three checks, and `post-merge`,
|
|
6
|
+
* which starts an opt-in background NEXUS reindex (T14010).
|
|
7
|
+
*/
|
|
8
|
+
export const CLEO_GIT_HOOK_NAMES = ['commit-msg', 'pre-commit', 'pre-push', 'post-merge'] as const;
|
|
6
9
|
|
|
7
10
|
/** One recoverable managed Git hook change. */
|
|
8
11
|
export const GitHookChangeSchema = z.object({
|
package/src/graph.ts
CHANGED
|
@@ -831,6 +831,12 @@ export interface GraphParseCacheUpdate {
|
|
|
831
831
|
upserts: GraphParseCacheEntry[];
|
|
832
832
|
/** Paths whose entries no longer describe a current, successfully parsed file. */
|
|
833
833
|
deletePaths: string[];
|
|
834
|
+
/**
|
|
835
|
+
* When present, delete every entry whose path is not listed (T14009): a
|
|
836
|
+
* checkpointing run already stored what it parsed during the run, so it keeps
|
|
837
|
+
* exactly this generation's files instead of rewriting them.
|
|
838
|
+
*/
|
|
839
|
+
retainPaths?: string[];
|
|
834
840
|
}
|
|
835
841
|
|
|
836
842
|
/** Why and how an index run chose between reusing prior extraction and parsing everything. */
|
|
@@ -860,6 +866,12 @@ export interface GraphIndexRunSummary {
|
|
|
860
866
|
resolvedFiles: number;
|
|
861
867
|
/** Wall-clock milliseconds per pipeline phase, for cost disclosure. */
|
|
862
868
|
phaseMs: Record<string, number>;
|
|
869
|
+
/**
|
|
870
|
+
* Set when community detection was skipped because its estimate exceeded
|
|
871
|
+
* the run's remaining budget (T14016); the generation then has no
|
|
872
|
+
* communities until an unbudgeted analysis.
|
|
873
|
+
*/
|
|
874
|
+
communitiesSkipped?: string;
|
|
863
875
|
}
|
|
864
876
|
|
|
865
877
|
/**
|
|
@@ -875,12 +887,61 @@ export interface GraphIndexFreshness {
|
|
|
875
887
|
status: 'fresh' | 'stale' | 'unknown';
|
|
876
888
|
/** Assessment timestamp of the published generation. */
|
|
877
889
|
lastIndexedAt: string | null;
|
|
890
|
+
/**
|
|
891
|
+
* Revision the published generation indexed; `null` when no revision was
|
|
892
|
+
* recorded (T14007).
|
|
893
|
+
*/
|
|
894
|
+
indexedRevision?: string | null;
|
|
895
|
+
/**
|
|
896
|
+
* Ref freshness is measured against: origin's default branch, else a local
|
|
897
|
+
* `main`/`master`, else `HEAD` (T14007).
|
|
898
|
+
*/
|
|
899
|
+
baseRef?: string | null;
|
|
900
|
+
/** Commit {@link GraphIndexFreshness.baseRef} names. */
|
|
901
|
+
baseRevision?: string | null;
|
|
902
|
+
/**
|
|
903
|
+
* Commits on the base ref the index has not seen (`git rev-list --count
|
|
904
|
+
* indexedRevision..baseRef`); `null` when either revision is unknown here.
|
|
905
|
+
*/
|
|
906
|
+
commitsBehind?: number | null;
|
|
907
|
+
/**
|
|
908
|
+
* Commits on the checked-out HEAD the index has not seen (`git rev-list
|
|
909
|
+
* --count indexedRevision..HEAD`); `null` when unknown. A stale refusal is
|
|
910
|
+
* judged on this, never on {@link GraphIndexFreshness.commitsBehind} (T14008).
|
|
911
|
+
*/
|
|
912
|
+
commitsBehindHead?: number | null;
|
|
913
|
+
/**
|
|
914
|
+
* Tracked files that differ between the indexed revision and the working
|
|
915
|
+
* tree (`git diff --name-only`), counted without a walk (T14014). Reporting
|
|
916
|
+
* only: `staleFileCount` (manifest walk) stays authoritative.
|
|
917
|
+
*/
|
|
918
|
+
changedFilesSinceIndex?: number | null;
|
|
919
|
+
/**
|
|
920
|
+
* Set while inline refreshes are paused after one failed or was aborted
|
|
921
|
+
* (T14014): until this instant, or the next successful analysis.
|
|
922
|
+
*/
|
|
923
|
+
refreshCooldownUntil?: string;
|
|
924
|
+
/** Whole days since {@link GraphIndexFreshness.lastIndexedAt}; `null` when never indexed. */
|
|
925
|
+
ageDays?: number | null;
|
|
878
926
|
/** Files recorded in the published generation's assessment. */
|
|
879
927
|
fileCount: number;
|
|
880
928
|
/** Modified, added and deleted source files. */
|
|
881
929
|
staleFileCount: number;
|
|
882
930
|
/** Up to a bounded sample of stale paths, for disclosure. */
|
|
883
931
|
stalePaths: string[];
|
|
932
|
+
/**
|
|
933
|
+
* Indexed files that no longer exist on disk (a deleted file or a removed
|
|
934
|
+
* clone the index still answers from; T14021). A subset of the stale set.
|
|
935
|
+
*/
|
|
936
|
+
missingFileCount?: number;
|
|
937
|
+
/** Up to a bounded sample of {@link GraphIndexFreshness.missingFileCount}'s paths. */
|
|
938
|
+
missingPaths?: string[];
|
|
939
|
+
/**
|
|
940
|
+
* Set when the published generation was analyzed under a budget that skipped
|
|
941
|
+
* community detection (T14016): it has no communities until an unbudgeted
|
|
942
|
+
* `cleo nexus analyze`. Carries the reason.
|
|
943
|
+
*/
|
|
944
|
+
communitiesSkipped?: string;
|
|
884
945
|
/** Whether the queried symbol's own file is stale; absent when no symbol file is known. */
|
|
885
946
|
symbolFileStale?: boolean;
|
|
886
947
|
/** The symbol file the `symbolFileStale` verdict describes. */
|
|
@@ -906,6 +967,37 @@ export interface GraphIndexFreshness {
|
|
|
906
967
|
};
|
|
907
968
|
}
|
|
908
969
|
|
|
970
|
+
/**
|
|
971
|
+
* How current the graph behind an answer is, returned at the top level of
|
|
972
|
+
* every index-sensitive `cleo nexus` answer as `data.freshness` (T14008).
|
|
973
|
+
*/
|
|
974
|
+
export interface NexusAnswerFreshness {
|
|
975
|
+
/** Working-tree verdict of {@link GraphIndexFreshness.status}. */
|
|
976
|
+
status: GraphIndexFreshness['status'];
|
|
977
|
+
/** Revision the index was built from. */
|
|
978
|
+
indexedRevision: string | null;
|
|
979
|
+
/** Ref the index is measured against. */
|
|
980
|
+
baseRef: string | null;
|
|
981
|
+
/** Commits on the base ref the index has not seen (reported only). */
|
|
982
|
+
commitsBehind: number | null;
|
|
983
|
+
/** Commits on the checked-out HEAD the index has not seen; the stale refusal is judged on this. */
|
|
984
|
+
commitsBehindHead: number | null;
|
|
985
|
+
/** Tracked files changed since the indexed revision (walk-free, reporting only; T14014). */
|
|
986
|
+
changedFilesSinceIndex: number | null;
|
|
987
|
+
/** Inline refreshes are paused until this instant after one failed (T14014). */
|
|
988
|
+
refreshCooldownUntil: string | null;
|
|
989
|
+
/** Whole days since the index was built. */
|
|
990
|
+
ageDays: number | null;
|
|
991
|
+
/** When the index was built. */
|
|
992
|
+
lastIndexedAt: string | null;
|
|
993
|
+
/** Indexed files that no longer exist on disk; `null` when unassessed (T14021). */
|
|
994
|
+
missingFileCount: number | null;
|
|
995
|
+
/** A bounded sample of those paths. */
|
|
996
|
+
missingPaths: string[];
|
|
997
|
+
/** Why the generation has no communities (a budgeted run skipped them); `null` otherwise (T14016). */
|
|
998
|
+
communitiesSkipped: string | null;
|
|
999
|
+
}
|
|
1000
|
+
|
|
909
1001
|
// ---------------------------------------------------------------------------
|
|
910
1002
|
// Impact analysis result
|
|
911
1003
|
// ---------------------------------------------------------------------------
|
package/src/index.ts
CHANGED
|
@@ -301,6 +301,8 @@ export type {
|
|
|
301
301
|
MemoryBridgeMode,
|
|
302
302
|
NexusAutoRefreshConfig,
|
|
303
303
|
NexusConfig,
|
|
304
|
+
NexusFreshnessConfig,
|
|
305
|
+
NexusReindexOnMergeConfig,
|
|
304
306
|
OutputConfig,
|
|
305
307
|
OutputFormat,
|
|
306
308
|
ProviderConfig,
|
|
@@ -825,6 +827,7 @@ export type {
|
|
|
825
827
|
ImpactResult,
|
|
826
828
|
InferredProvenance,
|
|
827
829
|
KnowledgeGraph,
|
|
830
|
+
NexusAnswerFreshness,
|
|
828
831
|
NexusNodeInsertRow,
|
|
829
832
|
NexusRelationInsertRow,
|
|
830
833
|
ProcessNode,
|
|
@@ -946,6 +949,8 @@ export {
|
|
|
946
949
|
} from './lafs.js';
|
|
947
950
|
// === ct-lean levels (T13663 / Epic T13418) ===
|
|
948
951
|
export type {
|
|
952
|
+
BriefMapFile,
|
|
953
|
+
BriefMapReport,
|
|
949
954
|
LeanConfig,
|
|
950
955
|
LeanDebtItem,
|
|
951
956
|
LeanDebtReport,
|
|
@@ -1647,6 +1652,7 @@ export type {
|
|
|
1647
1652
|
NexusProjectRecord,
|
|
1648
1653
|
NexusProjectStats,
|
|
1649
1654
|
NexusProjectsCleanIdMismatch,
|
|
1655
|
+
NexusProjectsCleanOtherHost,
|
|
1650
1656
|
NexusProjectsCleanParams,
|
|
1651
1657
|
NexusProjectsCleanReason,
|
|
1652
1658
|
NexusProjectsCleanReceipt,
|
|
@@ -2360,6 +2366,8 @@ export type {
|
|
|
2360
2366
|
HeavyHeapSource,
|
|
2361
2367
|
HeavyLeverChange,
|
|
2362
2368
|
HeavyToolResourcePlan,
|
|
2369
|
+
MemoryConsumer,
|
|
2370
|
+
MemoryConsumers,
|
|
2363
2371
|
MemoryPressureReading,
|
|
2364
2372
|
ResourceClass,
|
|
2365
2373
|
ResourceDeferral,
|
package/src/lean.ts
CHANGED
|
@@ -33,6 +33,35 @@ export interface LeanConfig {
|
|
|
33
33
|
* @defaultValue 'full'
|
|
34
34
|
*/
|
|
35
35
|
level?: LeanLevel;
|
|
36
|
+
/**
|
|
37
|
+
* Add the brief codebase map (`cleo map --brief`) to `cleo briefing`, so
|
|
38
|
+
* "reuse first" costs no search (T13666).
|
|
39
|
+
*
|
|
40
|
+
* @defaultValue false
|
|
41
|
+
*/
|
|
42
|
+
map?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** One file's exported names, the input to the brief map (T13666). */
|
|
46
|
+
export interface BriefMapFile {
|
|
47
|
+
/** Repo-relative path. */
|
|
48
|
+
readonly file: string;
|
|
49
|
+
/** Exported top-level names, in source order. */
|
|
50
|
+
readonly names: readonly string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** `cleo map --brief`: what already exists, one line per folder, within a budget. */
|
|
54
|
+
export interface BriefMapReport {
|
|
55
|
+
/** Where the names came from: the parsed nexus graph, or a git-tracked regex walk. */
|
|
56
|
+
readonly source: 'nexus' | 'git-walk';
|
|
57
|
+
/** The rendered map (empty when nothing was found). */
|
|
58
|
+
readonly text: string;
|
|
59
|
+
/** Folders listed. */
|
|
60
|
+
readonly folders: number;
|
|
61
|
+
/** Folders left out to stay within the budget. */
|
|
62
|
+
readonly skipped: number;
|
|
63
|
+
/** The character budget used. */
|
|
64
|
+
readonly budget: number;
|
|
36
65
|
}
|
|
37
66
|
|
|
38
67
|
/**
|
package/src/memory/observe.ts
CHANGED
|
@@ -164,23 +164,35 @@ export interface ObserveBrainResult {
|
|
|
164
164
|
typeSource?: 'caller' | 'keyword' | 'system-one';
|
|
165
165
|
/** T12494: confidence behind a `keyword` (flat 0.5) or `system-one` type. */
|
|
166
166
|
typeConfidence?: number;
|
|
167
|
+
/**
|
|
168
|
+
* T13949: `false` when the extraction gate stored nothing (it merged into an
|
|
169
|
+
* existing entry, deferred or rejected the write). `id` is then the existing
|
|
170
|
+
* entry, or a placeholder. Absent when a row was stored.
|
|
171
|
+
*/
|
|
172
|
+
stored?: false;
|
|
173
|
+
/** T13949: the gate's decision when nothing was stored. */
|
|
174
|
+
gateAction?: 'merged' | 'pending' | 'rejected';
|
|
175
|
+
/** T13949: the gate's explanation when nothing was stored. */
|
|
176
|
+
gateReason?: string;
|
|
167
177
|
}
|
|
168
178
|
|
|
169
179
|
// ── DocAttachmentObservationPayload ─────────────────────────────────
|
|
170
180
|
|
|
171
181
|
/**
|
|
172
|
-
*
|
|
173
|
-
*
|
|
182
|
+
* Source metadata of a document projection: the attachment, its owner, when
|
|
183
|
+
* it was added, and its slug and type.
|
|
174
184
|
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
185
|
+
* Since T13985 (D11180) `cleo docs add` records the link as a `documents`
|
|
186
|
+
* graph edge and writes no "Doc attached" observation; this payload is the
|
|
187
|
+
* projection proposal's source metadata (`DocsProjectionProposal.observation`).
|
|
188
|
+
* Legacy observations written before T13985 still carry it as JSON in their
|
|
189
|
+
* `narrative`, which the auto-row filters and the quarantine match on.
|
|
178
190
|
*
|
|
179
|
-
* The
|
|
180
|
-
*
|
|
181
|
-
* for round-trip attachment-existence checks.
|
|
191
|
+
* The name is kept deliberately (T13992): it is a published `@cleocode/contracts`
|
|
192
|
+
* type, and renaming it would break consumers for no behavioural gain.
|
|
182
193
|
*
|
|
183
194
|
* @task T9976
|
|
195
|
+
* @task T13992
|
|
184
196
|
*/
|
|
185
197
|
export interface DocAttachmentObservationPayload {
|
|
186
198
|
/** Slug recorded for this attachment (optional — omitted when none set). */
|
package/src/operations/docs.ts
CHANGED
|
@@ -642,11 +642,17 @@ export const DOCS_PROJECTION_RECEIPT_SCHEMA = z
|
|
|
642
642
|
version: z.literal(1),
|
|
643
643
|
sourceHash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
644
644
|
graph: z.enum(['completed', 'disabled']),
|
|
645
|
-
|
|
645
|
+
/** Legacy receipts (before T13985): the "Doc attached" observation the projection wrote. */
|
|
646
|
+
observationId: z.string().min(1).optional(),
|
|
647
|
+
/** T13985 (D11180): the canonical `llmtxt --documents--> owner` edge was written. */
|
|
648
|
+
documents: z.literal(true).optional(),
|
|
646
649
|
verifiedAt: z.string().min(1),
|
|
647
650
|
actor: z.string().min(1),
|
|
648
651
|
})
|
|
649
|
-
.strict()
|
|
652
|
+
.strict()
|
|
653
|
+
.refine((receipt) => receipt.observationId !== undefined || receipt.documents === true, {
|
|
654
|
+
message: 'A projection receipt records its documents edge or, for legacy jobs, its observation',
|
|
655
|
+
});
|
|
650
656
|
|
|
651
657
|
/** Domain verification record; this is not a cross-database atomic repair receipt. */
|
|
652
658
|
export type DocsProjectionReceipt = z.infer<typeof DOCS_PROJECTION_RECEIPT_SCHEMA>;
|
package/src/operations/nexus.ts
CHANGED
|
@@ -1815,7 +1815,7 @@ export interface NexusProjectsCleanParams {
|
|
|
1815
1815
|
includeTemp?: boolean;
|
|
1816
1816
|
/** Match paths containing tmp/test(s)/__tests__/fixture(s)/scratch/sandbox segments. */
|
|
1817
1817
|
includeTests?: boolean;
|
|
1818
|
-
/** Also match
|
|
1818
|
+
/** Also match rows recorded unreachable whose path no longer answers (T14011). */
|
|
1819
1819
|
matchUnhealthy?: boolean;
|
|
1820
1820
|
/** Also match never-indexed rows. */
|
|
1821
1821
|
matchNeverIndexed?: boolean;
|
|
@@ -1957,6 +1957,25 @@ export interface NexusProjectsCleanResult {
|
|
|
1957
1957
|
* path proves a project gone. Absent when none.
|
|
1958
1958
|
*/
|
|
1959
1959
|
idMismatch?: NexusProjectsCleanIdMismatch[];
|
|
1960
|
+
/**
|
|
1961
|
+
* Matched rows that were NOT removed because the project lives on another
|
|
1962
|
+
* machine (T14013): its recorded locations are all on other devices
|
|
1963
|
+
* (`other-host`), or it has none and its path root does not exist here
|
|
1964
|
+
* (`foreign-root`). Absent when none.
|
|
1965
|
+
*/
|
|
1966
|
+
otherHost?: NexusProjectsCleanOtherHost[];
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
/** A registry row `nexus.projects.clean` kept because it belongs to another machine (T14013). */
|
|
1970
|
+
export interface NexusProjectsCleanOtherHost {
|
|
1971
|
+
/** Immutable registry project ID. */
|
|
1972
|
+
projectId: string;
|
|
1973
|
+
/** Registered path. */
|
|
1974
|
+
projectPath: string;
|
|
1975
|
+
/** `other-host` (recorded on other devices only) or `foreign-root` (no record; path root absent here). */
|
|
1976
|
+
host: 'other-host' | 'foreign-root';
|
|
1977
|
+
/** Devices holding a recorded location (empty for `foreign-root`). */
|
|
1978
|
+
devices: string[];
|
|
1960
1979
|
}
|
|
1961
1980
|
|
|
1962
1981
|
/** A registry row `nexus.projects.clean` kept because its path declares another id (T12471). */
|
|
@@ -527,6 +527,12 @@ export interface SessionBriefingShowResult {
|
|
|
527
527
|
* when the attachment store is unavailable or no attachments exist.
|
|
528
528
|
*/
|
|
529
529
|
docsContext?: SessionBriefingDocsContext;
|
|
530
|
+
/**
|
|
531
|
+
* Brief codebase map (`cleo map --brief`): exported names per folder.
|
|
532
|
+
* Present only when the `lean.map` config key is on (T13666). Read from the
|
|
533
|
+
* nexus graph only; an unindexed project gets `no code index; run \`cleo nexus analyze\``.
|
|
534
|
+
*/
|
|
535
|
+
codebaseMap?: string;
|
|
530
536
|
}
|
|
531
537
|
|
|
532
538
|
// session.history (query — not in primary handler but exported for completeness)
|
package/src/resource-governor.ts
CHANGED
|
@@ -128,6 +128,34 @@ export interface MemoryPressureReading {
|
|
|
128
128
|
readonly memAvailableBytes: number | null;
|
|
129
129
|
/** The platform signals in one line (kernel level, swap, compressor, PSI). */
|
|
130
130
|
readonly summary: string;
|
|
131
|
+
/**
|
|
132
|
+
* The host's largest memory users when the gate refused (T13914), so a wait
|
|
133
|
+
* names what took the memory, CLEO or not. Absent when not probed.
|
|
134
|
+
*/
|
|
135
|
+
readonly topConsumers?: MemoryConsumers;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** One process holding host memory (T13914). */
|
|
139
|
+
export interface MemoryConsumer {
|
|
140
|
+
/** Process id. */
|
|
141
|
+
readonly pid: number;
|
|
142
|
+
/** Command name, as the process table shows it. */
|
|
143
|
+
readonly command: string;
|
|
144
|
+
/**
|
|
145
|
+
* Memory it holds in bytes: on macOS its footprint (`top` MEM: resident plus
|
|
146
|
+
* compressed and swapped pages, which `ps` RSS leaves out), on Linux its RSS.
|
|
147
|
+
*/
|
|
148
|
+
readonly footprintBytes: number;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** The largest memory users at one moment (T13914). */
|
|
152
|
+
export interface MemoryConsumers {
|
|
153
|
+
/** When they were probed (epoch ms). */
|
|
154
|
+
readonly sampledAtMs: number;
|
|
155
|
+
/** Largest first; empty when the probe failed. */
|
|
156
|
+
readonly processes: readonly MemoryConsumer[];
|
|
157
|
+
/** `true` when no probe could list them (the cause is then UNKNOWN). */
|
|
158
|
+
readonly failed?: boolean;
|
|
131
159
|
}
|
|
132
160
|
|
|
133
161
|
/**
|