@@ -20,8 +20,8 @@ import {
2020 saveWorkspaceMemory ,
2121 updateWorkspaceMemoryWithAccounting ,
2222} from "../src/workspace-memory.ts" ;
23- import { isProgressSnapshotViolation } from "../src/memory-quality.ts" ;
24- import { reviewerCurrent28Fixture , expectedAcceptedFixtureIds } from "./fixtures/memory-quality-current-28.ts" ;
23+ import { assessMemoryQuality , isHardQualityReason , isProgressSnapshotViolation } from "../src/memory-quality.ts" ;
24+ import { reviewerCurrent28Fixture } from "./fixtures/memory-quality-current-28.ts" ;
2525
2626function entry ( id : string , text : string , type : LongTermMemoryEntry [ "type" ] = "decision" ) : LongTermMemoryEntry {
2727 const now = new Date ( ) . toISOString ( ) ;
@@ -954,7 +954,84 @@ test("runMigrationP0Cleanup marks only non-explicit project snapshots and runs o
954954 assert . equal ( twice . entries . find ( e => e . id === "project-snapshot" ) ?. updatedAt , once . entries . find ( e => e . id === "project-snapshot" ) ?. updatedAt ) ;
955955} ) ;
956956
957- test ( "quality cleanup migration supersedes low-quality compaction memories from current-28 fixture" , async ( ) => {
957+ test ( "quality cleanup migration preserves soft-only feedback and decision violations" , async ( ) => {
958+ const root = await mkdtemp ( join ( tmpdir ( ) , "wm-quality-soft-preserve-" ) ) ;
959+ try {
960+ const now = new Date ( ) . toISOString ( ) ;
961+ await saveWorkspaceMemory ( root , {
962+ version : 1 ,
963+ workspace : { root, key : await workspaceKey ( root ) } ,
964+ limits : { maxRenderedChars : LONG_TERM_LIMITS . maxRenderedChars , maxEntries : LONG_TERM_LIMITS . maxEntries } ,
965+ entries : [
966+ {
967+ id : "soft_feedback" ,
968+ type : "feedback" ,
969+ text : "UI 要統一風格:兩個表格都要 scrollable,約 20 rows" ,
970+ source : "compaction" ,
971+ confidence : 0.75 ,
972+ status : "active" ,
973+ createdAt : now ,
974+ updatedAt : now ,
975+ } ,
976+ {
977+ id : "soft_decision" ,
978+ type : "decision" ,
979+ text : "Product branding is \"OpenCode Working Memory\" without \"Plugin\" in the name" ,
980+ source : "compaction" ,
981+ confidence : 0.75 ,
982+ status : "active" ,
983+ createdAt : now ,
984+ updatedAt : now ,
985+ staleAfterDays : 45 ,
986+ } ,
987+ ] ,
988+ migrations : [ ] ,
989+ updatedAt : now ,
990+ } ) ;
991+
992+ const loaded = await loadWorkspaceMemory ( root ) ;
993+ assert . equal ( loaded . entries . find ( e => e . id === "soft_feedback" ) ?. status , "active" ) ;
994+ assert . equal ( loaded . entries . find ( e => e . id === "soft_decision" ) ?. status , "active" ) ;
995+ assert . ok ( loaded . migrations ?. includes ( "2026-04-28-quality-cleanup" ) ) ;
996+ } finally {
997+ await rm ( root , { recursive : true , force : true } ) ;
998+ }
999+ } ) ;
1000+
1001+ test ( "quality cleanup migration supersedes hard quality violations" , async ( ) => {
1002+ const root = await mkdtemp ( join ( tmpdir ( ) , "wm-quality-hard-supersede-" ) ) ;
1003+ try {
1004+ const now = new Date ( ) . toISOString ( ) ;
1005+ await saveWorkspaceMemory ( root , {
1006+ version : 1 ,
1007+ workspace : { root, key : await workspaceKey ( root ) } ,
1008+ limits : { maxRenderedChars : LONG_TERM_LIMITS . maxRenderedChars , maxEntries : LONG_TERM_LIMITS . maxEntries } ,
1009+ entries : [ {
1010+ id : "hard_progress" ,
1011+ type : "project" ,
1012+ text : "測試套件:1237 tests pass, 226 suites" ,
1013+ source : "compaction" ,
1014+ confidence : 0.75 ,
1015+ status : "active" ,
1016+ createdAt : now ,
1017+ updatedAt : now ,
1018+ staleAfterDays : 60 ,
1019+ } ] ,
1020+ migrations : [ ] ,
1021+ updatedAt : now ,
1022+ } ) ;
1023+
1024+ const loaded = await loadWorkspaceMemory ( root ) ;
1025+ const entry = loaded . entries . find ( e => e . id === "hard_progress" ) ;
1026+ assert . equal ( entry ?. status , "superseded" ) ;
1027+ assert . ok ( entry ?. tags ?. includes ( "quality_cleanup" ) ) ;
1028+ assert . ok ( entry ?. tags ?. includes ( "quality:progress_snapshot" ) ) ;
1029+ } finally {
1030+ await rm ( root , { recursive : true , force : true } ) ;
1031+ }
1032+ } ) ;
1033+
1034+ test ( "quality cleanup migration supersedes only hard violations from current fixture" , async ( ) => {
9581035 const root = await mkdtemp ( join ( tmpdir ( ) , "wm-quality-cleanup-" ) ) ;
9591036 try {
9601037 const now = new Date ( ) . toISOString ( ) ;
@@ -972,10 +1049,12 @@ test("quality cleanup migration supersedes low-quality compaction memories from
9721049 const supersededIds = new Set ( loaded . entries . filter ( entry => entry . status === "superseded" ) . map ( entry => entry . id ) ) ;
9731050
9741051 for ( const entry of reviewerCurrent28Fixture ) {
975- if ( expectedAcceptedFixtureIds . has ( entry . id ) ) {
976- assert . equal ( activeIds . has ( entry . id ) , true , ` ${ entry . id } should remain active` ) ;
977- } else {
1052+ const quality = assessMemoryQuality ( entry ) ;
1053+ const hasHardReason = quality . reasons . some ( isHardQualityReason ) ;
1054+ if ( entry . source === "compaction" && ! quality . accepted && hasHardReason ) {
9781055 assert . equal ( supersededIds . has ( entry . id ) , true , `${ entry . id } should be superseded` ) ;
1056+ } else {
1057+ assert . equal ( activeIds . has ( entry . id ) , true , `${ entry . id } should remain active` ) ;
9791058 }
9801059 }
9811060
0 commit comments