@cleocode/core 2026.4.45 → 2026.4.47
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/index.js +84 -77
- package/dist/index.js.map +3 -3
- package/dist/internal.d.ts +3 -3
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +128 -109
- package/dist/internal.js.map +3 -3
- package/dist/memory/brain-lifecycle.d.ts +13 -6
- package/dist/memory/brain-lifecycle.d.ts.map +1 -1
- package/dist/memory/brain-maintenance.d.ts +11 -0
- package/dist/memory/brain-maintenance.d.ts.map +1 -1
- package/dist/store/db-helpers.d.ts +8 -3
- package/dist/store/db-helpers.d.ts.map +1 -1
- package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/package.json +18 -8
- package/src/internal.ts +16 -3
- package/src/memory/__tests__/brain-automation.test.ts +1 -0
- package/src/memory/__tests__/brain-lifecycle-tier-promotion.test.ts +367 -0
- package/src/memory/brain-lifecycle.ts +43 -32
- package/src/memory/brain-maintenance.ts +27 -2
- package/src/store/__tests__/db-helpers.test.ts +46 -6
- package/src/store/db-helpers.ts +25 -4
- package/src/store/sqlite-data-accessor.ts +2 -1
- package/src/tasks/__tests__/epic-auto-complete.test.ts +331 -0
- package/src/tasks/complete.ts +7 -4
|
@@ -83,14 +83,21 @@ export interface PromotionResult {
|
|
|
83
83
|
/**
|
|
84
84
|
* Run tier promotion for all memory tables.
|
|
85
85
|
*
|
|
86
|
-
* Promotion rules (per spec §1.1–§1.3):
|
|
87
|
-
* - short → medium:
|
|
88
|
-
*
|
|
89
|
-
*
|
|
86
|
+
* Promotion rules (per spec §1.1–§1.3, relaxed in T614):
|
|
87
|
+
* - short → medium:
|
|
88
|
+
* A. (citationCount >= 3 AND age > 24h) — citation-based track
|
|
89
|
+
* B. (qualityScore >= 0.7 AND age > 24h) — quality fast-track
|
|
90
|
+
* C. (verified = true AND age > 24h) — owner-verified track
|
|
91
|
+
* Note: `verified` is no longer a hard gate for routes A and B.
|
|
92
|
+
* Requiring verified=true on all paths caused all 235 short-tier observations
|
|
93
|
+
* to be permanently stuck (T614 bug).
|
|
94
|
+
* - medium → long:
|
|
95
|
+
* (citationCount >= 5 AND age > 7 days) OR (verified = true AND age > 7 days)
|
|
96
|
+
* Verified entries accelerate to long-tier without citation threshold.
|
|
90
97
|
*
|
|
91
98
|
* Eviction rules:
|
|
92
|
-
* - short-term entries older than
|
|
93
|
-
*
|
|
99
|
+
* - short-term entries older than 7 days with no promotion eligibility are
|
|
100
|
+
* soft-evicted (invalidAt = now).
|
|
94
101
|
* - long-term entries are NEVER auto-evicted.
|
|
95
102
|
*
|
|
96
103
|
* @param projectRoot - Project root directory for brain.db resolution
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brain-lifecycle.d.ts","sourceRoot":"","sources":["../../src/memory/brain-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD,OAAO,CAAC,WAAW,CAAC,CAqCtB;AAMD,0CAA0C;AAC1C,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAmID;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5D,OAAO,CAAC,mBAAmB,CAAC,CA+G9B;AAMD,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,gDAAgD;AAChD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED
|
|
1
|
+
{"version":3,"file":"brain-lifecycle.d.ts","sourceRoot":"","sources":["../../src/memory/brain-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD,OAAO,CAAC,WAAW,CAAC,CAqCtB;AAMD,0CAA0C;AAC1C,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAmID;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5D,OAAO,CAAC,mBAAmB,CAAC,CA+G9B;AAMD,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,gDAAgD;AAChD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAuKpF;AAMD,oDAAoD;AACpD,MAAM,WAAW,sBAAsB;IACrC,oCAAoC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,+BAA+B;IAC/B,cAAc,EAAE,eAAe,CAAC;IAChC,iCAAiC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,+BAA+B;IAC/B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,uCAAuC;IACvC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAkF3F"}
|
|
@@ -48,6 +48,13 @@ export interface BrainMaintenanceEmbeddingsResult {
|
|
|
48
48
|
/** Observations that failed embedding. */
|
|
49
49
|
errors: number;
|
|
50
50
|
}
|
|
51
|
+
/** Tier promotion step result. */
|
|
52
|
+
export interface BrainMaintenanceTierPromotionResult {
|
|
53
|
+
/** Number of entries promoted (short→medium or medium→long). */
|
|
54
|
+
promoted: number;
|
|
55
|
+
/** Number of stale short-tier entries soft-evicted. */
|
|
56
|
+
evicted: number;
|
|
57
|
+
}
|
|
51
58
|
/**
|
|
52
59
|
* Aggregated result from a full brain maintenance run.
|
|
53
60
|
*
|
|
@@ -61,6 +68,8 @@ export interface BrainMaintenanceResult {
|
|
|
61
68
|
consolidation: BrainMaintenanceConsolidationResult;
|
|
62
69
|
/** Results from the cross-DB orphaned reference reconciliation step. */
|
|
63
70
|
reconciliation: BrainMaintenanceReconciliationResult;
|
|
71
|
+
/** Results from the tier promotion step. */
|
|
72
|
+
tierPromotion: BrainMaintenanceTierPromotionResult;
|
|
64
73
|
/** Results from the embedding backfill step. */
|
|
65
74
|
embeddings: BrainMaintenanceEmbeddingsResult;
|
|
66
75
|
/** Total wall-clock duration of the maintenance run in milliseconds. */
|
|
@@ -79,6 +88,8 @@ export interface BrainMaintenanceOptions {
|
|
|
79
88
|
skipConsolidation?: boolean;
|
|
80
89
|
/** Skip the cross-DB orphaned reference reconciliation step. Default: false. */
|
|
81
90
|
skipReconciliation?: boolean;
|
|
91
|
+
/** Skip the tier promotion step (short→medium, medium→long). Default: false. */
|
|
92
|
+
skipTierPromotion?: boolean;
|
|
82
93
|
/** Skip the embedding backfill step. Default: false. */
|
|
83
94
|
skipEmbeddings?: boolean;
|
|
84
95
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brain-maintenance.d.ts","sourceRoot":"","sources":["../../src/memory/brain-maintenance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAUH,oEAAoE;AACpE,MAAM,WAAW,2BAA2B;IAC1C,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,0EAA0E;AAC1E,MAAM,WAAW,mCAAmC;IAClD,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qDAAqD;AACrD,MAAM,WAAW,oCAAoC;IACnD,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,sCAAsC;AACtC,MAAM,WAAW,gCAAgC;IAC/C,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,4CAA4C;IAC5C,KAAK,EAAE,2BAA2B,CAAC;IACnC,kDAAkD;IAClD,aAAa,EAAE,mCAAmC,CAAC;IACnD,wEAAwE;IACxE,cAAc,EAAE,oCAAoC,CAAC;IACrD,gDAAgD;IAChD,UAAU,EAAE,gCAAgC,CAAC;IAC7C,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,oDAAoD;IACpD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,0DAA0D;IAC1D,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wDAAwD;IACxD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACrE;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,sBAAsB,CAAC,
|
|
1
|
+
{"version":3,"file":"brain-maintenance.d.ts","sourceRoot":"","sources":["../../src/memory/brain-maintenance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAUH,oEAAoE;AACpE,MAAM,WAAW,2BAA2B;IAC1C,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,0EAA0E;AAC1E,MAAM,WAAW,mCAAmC;IAClD,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qDAAqD;AACrD,MAAM,WAAW,oCAAoC;IACnD,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,sCAAsC;AACtC,MAAM,WAAW,gCAAgC;IAC/C,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,kCAAkC;AAClC,MAAM,WAAW,mCAAmC;IAClD,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,4CAA4C;IAC5C,KAAK,EAAE,2BAA2B,CAAC;IACnC,kDAAkD;IAClD,aAAa,EAAE,mCAAmC,CAAC;IACnD,wEAAwE;IACxE,cAAc,EAAE,oCAAoC,CAAC;IACrD,4CAA4C;IAC5C,aAAa,EAAE,mCAAmC,CAAC;IACnD,gDAAgD;IAChD,UAAU,EAAE,gCAAgC,CAAC;IAC7C,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,oDAAoD;IACpD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,0DAA0D;IAC1D,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,wDAAwD;IACxD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACrE;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,sBAAsB,CAAC,CAoFjC"}
|
|
@@ -22,10 +22,15 @@ export interface ArchiveFields {
|
|
|
22
22
|
* Upsert a single task row into the tasks table.
|
|
23
23
|
* Handles both active task upsert and archived task upsert via optional archiveFields.
|
|
24
24
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* When `allowOrphanParent` is true (bulk/migration mode, T5034): silently nulls out
|
|
26
|
+
* parentId if the referenced parent does not exist, preventing FK violations.
|
|
27
|
+
* When false (normal single-task writes, default): logs a warning but still proceeds
|
|
28
|
+
* so that FK enforcement at the DB level provides the final safety net.
|
|
29
|
+
*
|
|
30
|
+
* Callers that perform bulk imports or archive restoration should pass
|
|
31
|
+
* `allowOrphanParent: true` to enable the lenient behavior.
|
|
27
32
|
*/
|
|
28
|
-
export declare function upsertTask(db: DrizzleDb, row: NewTaskRow, archiveFields?: ArchiveFields): Promise<void>;
|
|
33
|
+
export declare function upsertTask(db: DrizzleDb, row: NewTaskRow, archiveFields?: ArchiveFields, allowOrphanParent?: boolean): Promise<void>;
|
|
29
34
|
/**
|
|
30
35
|
* Upsert a single session row into the sessions table.
|
|
31
36
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-helpers.d.ts","sourceRoot":"","sources":["../../src/store/db-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"db-helpers.d.ts","sourceRoot":"","sources":["../../src/store/db-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAI5C,sCAAsC;AACtC,KAAK,SAAS,GAAG,kBAAkB,CAAC,OAAO,MAAM,CAAC,CAAC;AAEnD,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAC9B,EAAE,EAAE,SAAS,EACb,GAAG,EAAE,UAAU,EACf,aAAa,CAAC,EAAE,aAAa,EAC7B,iBAAiB,UAAQ,GACxB,OAAO,CAAC,IAAI,CAAC,CAoEf;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAkClF;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,SAAS,EACb,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,SAAS,EACb,KAAK,EAAE,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,EAChD,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,SAAS,EACb,KAAK,EAAE,IAAI,EAAE,EACb,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAC1B,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BvF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-data-accessor.d.ts","sourceRoot":"","sources":["../../src/store/sqlite-data-accessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAEV,YAAY,EAKb,MAAM,oBAAoB,CAAC;AA0C5B,yDAAyD;AACzD,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,IAAI,CAAC,CAWf;AAID;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"sqlite-data-accessor.d.ts","sourceRoot":"","sources":["../../src/store/sqlite-data-accessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAEV,YAAY,EAKb,MAAM,oBAAoB,CAAC;AA0C5B,yDAAyD;AACzD,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,IAAI,CAAC,CAWf;AAID;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAy1BlF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../../src/tasks/complete.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAoB,MAAM,qBAAqB,CAAC;AAM3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAI9D,qCAAqC;AACrC,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mCAAmC;AACnC,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,IAAI,CAAC;IACX,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC;CACvD;AAiED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,EAC5B,GAAG,CAAC,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,YAAY,GACtB,OAAO,CAAC,kBAAkB,CAAC,
|
|
1
|
+
{"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../../src/tasks/complete.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAoB,MAAM,qBAAqB,CAAC;AAM3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAI9D,qCAAqC;AACrC,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mCAAmC;AACnC,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,IAAI,CAAC;IACX,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC;CACvD;AAiED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,EAC5B,GAAG,CAAC,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,YAAY,GACtB,OAAO,CAAC,kBAAkB,CAAC,CAsQ7B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleocode/core",
|
|
3
|
-
"version": "2026.4.
|
|
3
|
+
"version": "2026.4.47",
|
|
4
4
|
"description": "CLEO core business logic kernel — tasks, sessions, memory, orchestration, lifecycle, with bundled SQLite store",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,6 +21,16 @@
|
|
|
21
21
|
"import": "./dist/conduit/index.js",
|
|
22
22
|
"require": "./dist/conduit/index.js"
|
|
23
23
|
},
|
|
24
|
+
"./store/*": {
|
|
25
|
+
"types": "./dist/store/*.d.ts",
|
|
26
|
+
"import": "./dist/store/*.js",
|
|
27
|
+
"require": "./dist/store/*.js"
|
|
28
|
+
},
|
|
29
|
+
"./conduit/*": {
|
|
30
|
+
"types": "./dist/conduit/*.d.ts",
|
|
31
|
+
"import": "./dist/conduit/*.js",
|
|
32
|
+
"require": "./dist/conduit/*.js"
|
|
33
|
+
},
|
|
24
34
|
"./*": {
|
|
25
35
|
"types": "./dist/*.d.ts",
|
|
26
36
|
"import": "./dist/*.js",
|
|
@@ -53,13 +63,13 @@
|
|
|
53
63
|
"write-file-atomic": "^7.0.1",
|
|
54
64
|
"yaml": "^2.8.3",
|
|
55
65
|
"zod": "^4.3.6",
|
|
56
|
-
"@cleocode/adapters": "2026.4.
|
|
57
|
-
"@cleocode/agents": "2026.4.
|
|
58
|
-
"@cleocode/caamp": "2026.4.
|
|
59
|
-
"@cleocode/lafs": "2026.4.
|
|
60
|
-
"@cleocode/contracts": "2026.4.
|
|
61
|
-
"@cleocode/nexus": "2026.4.
|
|
62
|
-
"@cleocode/skills": "2026.4.
|
|
66
|
+
"@cleocode/adapters": "2026.4.47",
|
|
67
|
+
"@cleocode/agents": "2026.4.47",
|
|
68
|
+
"@cleocode/caamp": "2026.4.47",
|
|
69
|
+
"@cleocode/lafs": "2026.4.47",
|
|
70
|
+
"@cleocode/contracts": "2026.4.47",
|
|
71
|
+
"@cleocode/nexus": "2026.4.47",
|
|
72
|
+
"@cleocode/skills": "2026.4.47"
|
|
63
73
|
},
|
|
64
74
|
"engines": {
|
|
65
75
|
"node": ">=24.0.0"
|
package/src/internal.ts
CHANGED
|
@@ -170,9 +170,21 @@ export { STAGE_DEFINITIONS } from './lifecycle/stages.js';
|
|
|
170
170
|
export { instantiateTessera, showTessera } from './lifecycle/tessera-engine.js';
|
|
171
171
|
export type { BrainBackfillResult } from './memory/brain-backfill.js';
|
|
172
172
|
export { backfillBrainGraph } from './memory/brain-backfill.js';
|
|
173
|
-
// Memory — brain lifecycle (temporal decay + consolidation)
|
|
174
|
-
export type {
|
|
175
|
-
|
|
173
|
+
// Memory — brain lifecycle (temporal decay + consolidation + tier promotion)
|
|
174
|
+
export type {
|
|
175
|
+
ConsolidationResult,
|
|
176
|
+
DecayResult,
|
|
177
|
+
EvictionRecord,
|
|
178
|
+
PromotionRecord,
|
|
179
|
+
PromotionResult,
|
|
180
|
+
RunConsolidationResult,
|
|
181
|
+
} from './memory/brain-lifecycle.js';
|
|
182
|
+
export {
|
|
183
|
+
applyTemporalDecay,
|
|
184
|
+
consolidateMemories,
|
|
185
|
+
runConsolidation,
|
|
186
|
+
runTierPromotion,
|
|
187
|
+
} from './memory/brain-lifecycle.js';
|
|
176
188
|
// Memory — brain maintenance
|
|
177
189
|
export type {
|
|
178
190
|
BrainMaintenanceConsolidationResult,
|
|
@@ -181,6 +193,7 @@ export type {
|
|
|
181
193
|
BrainMaintenanceOptions,
|
|
182
194
|
BrainMaintenanceReconciliationResult,
|
|
183
195
|
BrainMaintenanceResult,
|
|
196
|
+
BrainMaintenanceTierPromotionResult,
|
|
184
197
|
} from './memory/brain-maintenance.js';
|
|
185
198
|
export { runBrainMaintenance } from './memory/brain-maintenance.js';
|
|
186
199
|
export type { PurgeResult } from './memory/brain-purge.js';
|
|
@@ -46,6 +46,7 @@ vi.mock('../brain-retrieval.js', () => ({
|
|
|
46
46
|
vi.mock('../brain-lifecycle.js', () => ({
|
|
47
47
|
applyTemporalDecay: vi.fn().mockResolvedValue({ updated: 3 }),
|
|
48
48
|
consolidateMemories: vi.fn().mockResolvedValue({ merged: 2, archived: 4 }),
|
|
49
|
+
runTierPromotion: vi.fn().mockResolvedValue({ promoted: [], evicted: [] }),
|
|
49
50
|
}));
|
|
50
51
|
|
|
51
52
|
// Mock auto-extract dependencies
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for runTierPromotion in brain-lifecycle.ts (T614 fix).
|
|
3
|
+
*
|
|
4
|
+
* Verifies that tier promotion correctly promotes entries from short → medium
|
|
5
|
+
* and medium → long based on quality score, citation count, verification status,
|
|
6
|
+
* and age thresholds — WITHOUT requiring `verified = true` as a hard gate for
|
|
7
|
+
* the quality/citation tracks (T614 bug fix).
|
|
8
|
+
*
|
|
9
|
+
* @task T614
|
|
10
|
+
* @epic T569
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Hoisted mock factories
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
const { mockGetBrainDb, mockGetBrainNativeDb } = vi.hoisted(() => ({
|
|
20
|
+
mockGetBrainDb: vi.fn().mockResolvedValue({}),
|
|
21
|
+
mockGetBrainNativeDb: vi.fn(),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
vi.mock('../../store/brain-sqlite.js', () => ({
|
|
25
|
+
getBrainDb: mockGetBrainDb,
|
|
26
|
+
getBrainNativeDb: mockGetBrainNativeDb,
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Import module under test (after all mocks)
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
import { runTierPromotion } from '../brain-lifecycle.js';
|
|
34
|
+
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Helpers
|
|
37
|
+
// ============================================================================
|
|
38
|
+
|
|
39
|
+
const PROJECT_ROOT = '/fake/project';
|
|
40
|
+
|
|
41
|
+
/** ISO datetime string for `daysAgo` days before now. */
|
|
42
|
+
function daysAgo(days: number): string {
|
|
43
|
+
return new Date(Date.now() - days * 24 * 60 * 60 * 1000)
|
|
44
|
+
.toISOString()
|
|
45
|
+
.replace('T', ' ')
|
|
46
|
+
.slice(0, 19);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type PreparedStmt = {
|
|
50
|
+
run: ReturnType<typeof vi.fn>;
|
|
51
|
+
get: ReturnType<typeof vi.fn>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/** Build a minimal SQLite-like prepared statement stub. */
|
|
55
|
+
function makeStmt(rows: unknown[] = []): PreparedStmt {
|
|
56
|
+
return {
|
|
57
|
+
run: vi.fn().mockReturnValue({ changes: rows.length }),
|
|
58
|
+
get: vi.fn().mockReturnValue(rows[0] ?? undefined),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Tests
|
|
64
|
+
// ============================================================================
|
|
65
|
+
|
|
66
|
+
describe('runTierPromotion', () => {
|
|
67
|
+
let capturedUpdates: Array<{ table: string; id: string; tier: string }> = [];
|
|
68
|
+
|
|
69
|
+
beforeEach(() => {
|
|
70
|
+
capturedUpdates = [];
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
vi.clearAllMocks();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('returns empty result when nativeDb is unavailable', async () => {
|
|
78
|
+
mockGetBrainNativeDb.mockReturnValue(null);
|
|
79
|
+
|
|
80
|
+
const result = await runTierPromotion(PROJECT_ROOT);
|
|
81
|
+
|
|
82
|
+
expect(result.promoted).toHaveLength(0);
|
|
83
|
+
expect(result.evicted).toHaveLength(0);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('promotes unverified observation with quality_score >= 0.7 (T614 fix: no verified gate)', async () => {
|
|
87
|
+
// This is the core T614 regression test: an unverified (verified=0) observation
|
|
88
|
+
// with quality_score=0.8 older than 24h MUST promote to medium.
|
|
89
|
+
const shortObs = [
|
|
90
|
+
{
|
|
91
|
+
id: 'O-test-001',
|
|
92
|
+
citation_count: 0,
|
|
93
|
+
quality_score: 0.8,
|
|
94
|
+
verified: 0, // NOT verified — was blocked before T614 fix
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
const nativeDb = buildMockDb({
|
|
99
|
+
brain_observations: {
|
|
100
|
+
shortToMedium: shortObs,
|
|
101
|
+
mediumToLong: [],
|
|
102
|
+
toEvict: [],
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
mockGetBrainNativeDb.mockReturnValue(nativeDb);
|
|
106
|
+
|
|
107
|
+
const result = await runTierPromotion(PROJECT_ROOT);
|
|
108
|
+
|
|
109
|
+
expect(result.promoted).toHaveLength(1);
|
|
110
|
+
expect(result.promoted[0]).toMatchObject({
|
|
111
|
+
id: 'O-test-001',
|
|
112
|
+
table: 'brain_observations',
|
|
113
|
+
fromTier: 'short',
|
|
114
|
+
toTier: 'medium',
|
|
115
|
+
});
|
|
116
|
+
expect(result.promoted[0]!.reason).toContain('qualityScore=0.80');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('promotes unverified observation with citation_count >= 3 (T614 fix)', async () => {
|
|
120
|
+
const shortObs = [
|
|
121
|
+
{
|
|
122
|
+
id: 'O-test-002',
|
|
123
|
+
citation_count: 5,
|
|
124
|
+
quality_score: 0.4, // low quality but high citations
|
|
125
|
+
verified: 0,
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
const nativeDb = buildMockDb({
|
|
130
|
+
brain_observations: {
|
|
131
|
+
shortToMedium: shortObs,
|
|
132
|
+
mediumToLong: [],
|
|
133
|
+
toEvict: [],
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
mockGetBrainNativeDb.mockReturnValue(nativeDb);
|
|
137
|
+
|
|
138
|
+
const result = await runTierPromotion(PROJECT_ROOT);
|
|
139
|
+
|
|
140
|
+
expect(result.promoted).toHaveLength(1);
|
|
141
|
+
expect(result.promoted[0]).toMatchObject({
|
|
142
|
+
id: 'O-test-002',
|
|
143
|
+
fromTier: 'short',
|
|
144
|
+
toTier: 'medium',
|
|
145
|
+
});
|
|
146
|
+
expect(result.promoted[0]!.reason).toContain('citationCount=5');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('promotes verified observation with any quality (verified track)', async () => {
|
|
150
|
+
const shortObs = [
|
|
151
|
+
{
|
|
152
|
+
id: 'O-test-003',
|
|
153
|
+
citation_count: 0,
|
|
154
|
+
quality_score: 0.3, // low quality, low citations, but verified
|
|
155
|
+
verified: 1,
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
const nativeDb = buildMockDb({
|
|
160
|
+
brain_observations: {
|
|
161
|
+
shortToMedium: shortObs,
|
|
162
|
+
mediumToLong: [],
|
|
163
|
+
toEvict: [],
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
mockGetBrainNativeDb.mockReturnValue(nativeDb);
|
|
167
|
+
|
|
168
|
+
const result = await runTierPromotion(PROJECT_ROOT);
|
|
169
|
+
|
|
170
|
+
expect(result.promoted).toHaveLength(1);
|
|
171
|
+
expect(result.promoted[0]).toMatchObject({
|
|
172
|
+
id: 'O-test-003',
|
|
173
|
+
fromTier: 'short',
|
|
174
|
+
toTier: 'medium',
|
|
175
|
+
});
|
|
176
|
+
expect(result.promoted[0]!.reason).toContain('verified=true');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('promotes medium entry to long with citation_count >= 5 (no verified requirement)', async () => {
|
|
180
|
+
const mediumObs = [
|
|
181
|
+
{
|
|
182
|
+
id: 'O-test-004',
|
|
183
|
+
citation_count: 7,
|
|
184
|
+
quality_score: 0.9,
|
|
185
|
+
verified: 0, // not verified but high citations
|
|
186
|
+
},
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
const nativeDb = buildMockDb({
|
|
190
|
+
brain_observations: {
|
|
191
|
+
shortToMedium: [],
|
|
192
|
+
mediumToLong: mediumObs,
|
|
193
|
+
toEvict: [],
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
mockGetBrainNativeDb.mockReturnValue(nativeDb);
|
|
197
|
+
|
|
198
|
+
const result = await runTierPromotion(PROJECT_ROOT);
|
|
199
|
+
|
|
200
|
+
expect(result.promoted).toHaveLength(1);
|
|
201
|
+
expect(result.promoted[0]).toMatchObject({
|
|
202
|
+
id: 'O-test-004',
|
|
203
|
+
fromTier: 'medium',
|
|
204
|
+
toTier: 'long',
|
|
205
|
+
});
|
|
206
|
+
expect(result.promoted[0]!.reason).toContain('citationCount=7');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('promotes verified medium entry to long (accelerated track)', async () => {
|
|
210
|
+
const mediumObs = [
|
|
211
|
+
{
|
|
212
|
+
id: 'O-test-005',
|
|
213
|
+
citation_count: 1, // only 1 citation but verified
|
|
214
|
+
quality_score: 0.6,
|
|
215
|
+
verified: 1,
|
|
216
|
+
},
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
const nativeDb = buildMockDb({
|
|
220
|
+
brain_observations: {
|
|
221
|
+
shortToMedium: [],
|
|
222
|
+
mediumToLong: mediumObs,
|
|
223
|
+
toEvict: [],
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
mockGetBrainNativeDb.mockReturnValue(nativeDb);
|
|
227
|
+
|
|
228
|
+
const result = await runTierPromotion(PROJECT_ROOT);
|
|
229
|
+
|
|
230
|
+
expect(result.promoted).toHaveLength(1);
|
|
231
|
+
expect(result.promoted[0]).toMatchObject({
|
|
232
|
+
id: 'O-test-005',
|
|
233
|
+
fromTier: 'medium',
|
|
234
|
+
toTier: 'long',
|
|
235
|
+
});
|
|
236
|
+
expect(result.promoted[0]!.reason).toContain('verified=true');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('soft-evicts stale short entries with low quality and no promotion', async () => {
|
|
240
|
+
const staleObs = [
|
|
241
|
+
{
|
|
242
|
+
id: 'O-test-006',
|
|
243
|
+
quality_score: 0.1,
|
|
244
|
+
},
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
const nativeDb = buildMockDb({
|
|
248
|
+
brain_observations: {
|
|
249
|
+
shortToMedium: [],
|
|
250
|
+
mediumToLong: [],
|
|
251
|
+
toEvict: staleObs,
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
mockGetBrainNativeDb.mockReturnValue(nativeDb);
|
|
255
|
+
|
|
256
|
+
const result = await runTierPromotion(PROJECT_ROOT);
|
|
257
|
+
|
|
258
|
+
expect(result.evicted).toHaveLength(1);
|
|
259
|
+
expect(result.evicted[0]).toMatchObject({
|
|
260
|
+
id: 'O-test-006',
|
|
261
|
+
table: 'brain_observations',
|
|
262
|
+
tier: 'short',
|
|
263
|
+
});
|
|
264
|
+
expect(result.promoted).toHaveLength(0);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('processes all four memory tables', async () => {
|
|
268
|
+
// Each table should get one promotion
|
|
269
|
+
const obsRow = [{ id: 'O-obs', citation_count: 5, quality_score: 0.8, verified: 0 }];
|
|
270
|
+
const learningRow = [{ id: 'L-learn', citation_count: 0, quality_score: 0.75, verified: 0 }];
|
|
271
|
+
const patternRow = [{ id: 'P-pat', citation_count: 3, quality_score: 0.5, verified: 0 }];
|
|
272
|
+
const decisionRow = [{ id: 'D-dec', citation_count: 0, quality_score: 0.0, verified: 1 }];
|
|
273
|
+
|
|
274
|
+
const nativeDb = buildMockDb({
|
|
275
|
+
brain_observations: { shortToMedium: obsRow, mediumToLong: [], toEvict: [] },
|
|
276
|
+
brain_learnings: { shortToMedium: learningRow, mediumToLong: [], toEvict: [] },
|
|
277
|
+
brain_patterns: { shortToMedium: patternRow, mediumToLong: [], toEvict: [] },
|
|
278
|
+
brain_decisions: { shortToMedium: decisionRow, mediumToLong: [], toEvict: [] },
|
|
279
|
+
});
|
|
280
|
+
mockGetBrainNativeDb.mockReturnValue(nativeDb);
|
|
281
|
+
|
|
282
|
+
const result = await runTierPromotion(PROJECT_ROOT);
|
|
283
|
+
|
|
284
|
+
expect(result.promoted).toHaveLength(4);
|
|
285
|
+
const promotedTables = result.promoted.map((p) => p.table);
|
|
286
|
+
expect(promotedTables).toContain('brain_observations');
|
|
287
|
+
expect(promotedTables).toContain('brain_learnings');
|
|
288
|
+
expect(promotedTables).toContain('brain_patterns');
|
|
289
|
+
expect(promotedTables).toContain('brain_decisions');
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('returns empty result when no entries qualify', async () => {
|
|
293
|
+
const nativeDb = buildMockDb({
|
|
294
|
+
brain_observations: { shortToMedium: [], mediumToLong: [], toEvict: [] },
|
|
295
|
+
brain_learnings: { shortToMedium: [], mediumToLong: [], toEvict: [] },
|
|
296
|
+
brain_patterns: { shortToMedium: [], mediumToLong: [], toEvict: [] },
|
|
297
|
+
brain_decisions: { shortToMedium: [], mediumToLong: [], toEvict: [] },
|
|
298
|
+
});
|
|
299
|
+
mockGetBrainNativeDb.mockReturnValue(nativeDb);
|
|
300
|
+
|
|
301
|
+
const result = await runTierPromotion(PROJECT_ROOT);
|
|
302
|
+
|
|
303
|
+
expect(result.promoted).toHaveLength(0);
|
|
304
|
+
expect(result.evicted).toHaveLength(0);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// ============================================================================
|
|
309
|
+
// Helper: build a mock nativeDb that routes prepare() calls based on SQL content
|
|
310
|
+
// ============================================================================
|
|
311
|
+
|
|
312
|
+
type TableFixtures = {
|
|
313
|
+
shortToMedium: unknown[];
|
|
314
|
+
mediumToLong: unknown[];
|
|
315
|
+
toEvict: unknown[];
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
function buildMockDb(tables: Partial<Record<string, TableFixtures>>): {
|
|
319
|
+
prepare: ReturnType<typeof vi.fn>;
|
|
320
|
+
} {
|
|
321
|
+
const allTables = ['brain_observations', 'brain_learnings', 'brain_patterns', 'brain_decisions'];
|
|
322
|
+
|
|
323
|
+
// Normalise: every table gets a default empty fixture
|
|
324
|
+
const fixtures: Record<string, TableFixtures> = {};
|
|
325
|
+
for (const t of allTables) {
|
|
326
|
+
fixtures[t] = tables[t] ?? { shortToMedium: [], mediumToLong: [], toEvict: [] };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return {
|
|
330
|
+
prepare: vi.fn().mockImplementation((sql: string) => {
|
|
331
|
+
// Determine which table this statement targets
|
|
332
|
+
const targetTable = allTables.find((t) => sql.includes(t)) ?? 'brain_observations';
|
|
333
|
+
const fix = fixtures[targetTable]!;
|
|
334
|
+
|
|
335
|
+
// SELECT for eviction — identified by "(verified = 0 OR verified IS NULL)"
|
|
336
|
+
// Must be checked BEFORE the short-promotion check since both use memory_tier='short'
|
|
337
|
+
if (sql.includes('verified = 0') || sql.includes('verified IS NULL')) {
|
|
338
|
+
return {
|
|
339
|
+
all: vi.fn().mockReturnValue(fix.toEvict),
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
// SELECT for short→medium promotion — uses "(citation_count >= 3 OR quality_score >= 0.7 OR verified = 1)"
|
|
343
|
+
if (sql.includes("memory_tier = 'short'") && sql.includes('SELECT')) {
|
|
344
|
+
return {
|
|
345
|
+
all: vi.fn().mockReturnValue(fix.shortToMedium),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
// SELECT for medium→long promotion
|
|
349
|
+
if (sql.includes("memory_tier = 'medium'") && sql.includes('SELECT')) {
|
|
350
|
+
return {
|
|
351
|
+
all: vi.fn().mockReturnValue(fix.mediumToLong),
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
// UPDATE statements (tier change, eviction)
|
|
355
|
+
if (sql.includes('UPDATE')) {
|
|
356
|
+
return {
|
|
357
|
+
run: vi.fn().mockReturnValue({ changes: 1 }),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
// Fallback: SELECT returning empty
|
|
361
|
+
return {
|
|
362
|
+
all: vi.fn().mockReturnValue([]),
|
|
363
|
+
run: vi.fn().mockReturnValue({ changes: 0 }),
|
|
364
|
+
};
|
|
365
|
+
}),
|
|
366
|
+
};
|
|
367
|
+
}
|