1+ import { appendFile , mkdir } from "node:fs/promises" ;
2+ import { dirname } from "node:path" ;
13import type { LongTermMemoryEntry , WorkspaceMemoryStore } from "./types.ts" ;
24import { LONG_TERM_LIMITS } from "./types.ts" ;
3- import { workspaceKey , workspaceMemoryPath } from "./paths.ts" ;
5+ import { migrationLogPath , workspaceKey , workspaceMemoryPath } from "./paths.ts" ;
46import { atomicWriteJSON , readJSON , updateJSON } from "./storage.ts" ;
57import { assessMemoryQuality , isHardQualityReason , isProgressSnapshotViolation } from "./memory-quality.ts" ;
68
@@ -50,6 +52,21 @@ export type WorkspaceMemoryNormalizationResult = LongTermLimitResult & {
5052 events : MemoryConsolidationEvent [ ] ;
5153} ;
5254
55+ export type QualityCleanupMigrationLogEntry = {
56+ migrationId : string ;
57+ timestamp : string ;
58+ workspaceKey : string ;
59+ workspaceRoot : string ;
60+ entryId : string ;
61+ type : LongTermMemoryEntry [ "type" ] ;
62+ source : LongTermMemoryEntry [ "source" ] ;
63+ text : string ;
64+ reasons : string [ ] ;
65+ hardReasons : string [ ] ;
66+ beforeStatus : "active" ;
67+ afterStatus : "superseded" ;
68+ } ;
69+
5370export async function emptyWorkspaceMemory ( root : string ) : Promise < WorkspaceMemoryStore > {
5471 return {
5572 version : 1 ,
@@ -191,7 +208,13 @@ export async function normalizeWorkspaceMemoryWithAccounting(
191208 // One-time migrations for legacy/low-quality snapshot violations.
192209 // Run quality cleanup first so hard violations receive quality audit tags
193210 // before the older P0 project-only cleanup marks progress snapshots.
194- result = runMigrationQualityCleanup ( result , nowIso ) ;
211+ const qualityCleanup = runMigrationQualityCleanup ( result , nowIso ) ;
212+ result = qualityCleanup . store ;
213+ if ( qualityCleanup . events . length > 0 ) {
214+ await appendQualityCleanupMigrationLog ( qualityCleanup . events ) . catch ( error => {
215+ console . error ( "[memory] failed to write quality cleanup migration log:" , error ) ;
216+ } ) ;
217+ }
195218 result = runMigrationP0Cleanup ( result , nowIso ) ;
196219
197220 // P0 accounting only considers active entries. Entries that were already
@@ -287,14 +310,22 @@ export function runMigrationP0Cleanup(
287310 } ;
288311}
289312
313+ async function appendQualityCleanupMigrationLog ( events : QualityCleanupMigrationLogEntry [ ] ) : Promise < void > {
314+ if ( events . length === 0 ) return ;
315+ const path = migrationLogPath ( QUALITY_CLEANUP_MIGRATION_ID ) ;
316+ await mkdir ( dirname ( path ) , { recursive : true } ) ;
317+ await appendFile ( path , events . map ( event => JSON . stringify ( event ) ) . join ( "\n" ) + "\n" , "utf8" ) ;
318+ }
319+
290320export function runMigrationQualityCleanup (
291321 store : WorkspaceMemoryStore ,
292322 nowIso : string ,
293- ) : WorkspaceMemoryStore {
323+ ) : { store : WorkspaceMemoryStore ; events : QualityCleanupMigrationLogEntry [ ] } {
294324 if ( store . migrations ?. includes ( QUALITY_CLEANUP_MIGRATION_ID ) ) {
295- return store ;
325+ return { store, events : [ ] } ;
296326 }
297327
328+ const events : QualityCleanupMigrationLogEntry [ ] = [ ] ;
298329 let changed = false ;
299330 const entries = store . entries . map ( entry => {
300331 if ( entry . source !== "compaction" ) return entry ;
@@ -307,6 +338,21 @@ export function runMigrationQualityCleanup(
307338 if ( hardReasons . length === 0 ) return entry ;
308339
309340 changed = true ;
341+ events . push ( {
342+ migrationId : QUALITY_CLEANUP_MIGRATION_ID ,
343+ timestamp : nowIso ,
344+ workspaceKey : store . workspace . key ,
345+ workspaceRoot : store . workspace . root ,
346+ entryId : entry . id ,
347+ type : entry . type ,
348+ source : entry . source ,
349+ text : entry . text ,
350+ reasons : quality . reasons ,
351+ hardReasons,
352+ beforeStatus : "active" ,
353+ afterStatus : "superseded" ,
354+ } ) ;
355+
310356 const tags = new Set ( [
311357 ...( entry . tags ?? [ ] ) ,
312358 "quality_cleanup" ,
@@ -322,10 +368,13 @@ export function runMigrationQualityCleanup(
322368 } ) ;
323369
324370 return {
325- ...store ,
326- entries,
327- migrations : [ ...( store . migrations ?? [ ] ) , QUALITY_CLEANUP_MIGRATION_ID ] ,
328- updatedAt : changed ? nowIso : store . updatedAt ,
371+ store : {
372+ ...store ,
373+ entries,
374+ migrations : [ ...( store . migrations ?? [ ] ) , QUALITY_CLEANUP_MIGRATION_ID ] ,
375+ updatedAt : changed ? nowIso : store . updatedAt ,
376+ } ,
377+ events,
329378 } ;
330379}
331380
0 commit comments