dsh-prime-memory 0.22.2 → 0.24.1

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.
Files changed (104) hide show
  1. package/CHANGELOG.en.md +421 -0
  2. package/CHANGELOG.ja.md +416 -0
  3. package/CHANGELOG.ko.md +416 -0
  4. package/CHANGELOG.md +120 -0
  5. package/dist/client.js +1266 -845
  6. package/dist/config.d.ts +131 -1341
  7. package/dist/config.js +89 -4
  8. package/dist/conflict-service.d.ts +7 -1
  9. package/dist/conflict-service.js +7 -1
  10. package/dist/contract.d.ts +211 -0
  11. package/dist/domain-gate.d.ts +11 -0
  12. package/dist/domain-gate.js +15 -0
  13. package/dist/hooks/obsolete-notice.d.ts +26 -0
  14. package/dist/hooks/obsolete-notice.js +64 -0
  15. package/dist/hooks/recall.d.ts +39 -0
  16. package/dist/hooks/recall.js +110 -30
  17. package/dist/index.d.ts +1 -1127
  18. package/dist/index.js +52 -2
  19. package/dist/memory-types.d.ts +69 -0
  20. package/dist/memory-types.js +165 -0
  21. package/dist/memport/format.d.ts +6 -1
  22. package/dist/memport/format.js +42 -2
  23. package/dist/pipeline/l1.d.ts +44 -2
  24. package/dist/pipeline/l1.js +163 -9
  25. package/dist/pipeline/md-sync.d.ts +65 -0
  26. package/dist/pipeline/md-sync.js +456 -0
  27. package/dist/pipeline/ruminate.d.ts +46 -1
  28. package/dist/pipeline/ruminate.js +109 -0
  29. package/dist/pipeline/runner.d.ts +10 -0
  30. package/dist/pipeline/runner.js +13 -2
  31. package/dist/pipeline/workspace-relink.d.ts +60 -0
  32. package/dist/pipeline/workspace-relink.js +73 -0
  33. package/dist/projection/recall.d.ts +51 -13
  34. package/dist/projection/recall.js +47 -12
  35. package/dist/prompts/examples.d.ts +73 -0
  36. package/dist/prompts/examples.js +178 -0
  37. package/dist/prompts/l1-dedup.d.ts +9 -3
  38. package/dist/prompts/l1-dedup.js +34 -13
  39. package/dist/prompts/l1-extraction.d.ts +5 -3
  40. package/dist/prompts/l1-extraction.js +49 -4
  41. package/dist/settings.d.ts +8 -0
  42. package/dist/settings.js +16 -1
  43. package/dist/stats.d.ts +12 -0
  44. package/dist/stats.js +352 -17
  45. package/dist/store/embedding-source.d.ts +8 -0
  46. package/dist/store/embedding-source.js +39 -2
  47. package/dist/store/file-versions.d.ts +2 -0
  48. package/dist/store/file-versions.js +2 -0
  49. package/dist/store/graph-store.d.ts +14 -0
  50. package/dist/store/graph-store.js +35 -0
  51. package/dist/store/l1-snapshot.d.ts +26 -1
  52. package/dist/store/l1-snapshot.js +53 -8
  53. package/dist/store/l1.d.ts +94 -3
  54. package/dist/store/l1.js +169 -4
  55. package/dist/store/memory-md-archive.d.ts +84 -0
  56. package/dist/store/memory-md-archive.js +155 -0
  57. package/dist/store/memory-md-disable.d.ts +74 -0
  58. package/dist/store/memory-md-disable.js +302 -0
  59. package/dist/store/memory-md-export.d.ts +50 -0
  60. package/dist/store/memory-md-export.js +227 -0
  61. package/dist/store/memory-md-ledger.d.ts +94 -0
  62. package/dist/store/memory-md-ledger.js +243 -0
  63. package/dist/store/memory-md-orphan.d.ts +27 -0
  64. package/dist/store/memory-md-orphan.js +153 -0
  65. package/dist/store/memory-md-parse.d.ts +35 -0
  66. package/dist/store/memory-md-parse.js +85 -0
  67. package/dist/store/memory-md-run.d.ts +48 -0
  68. package/dist/store/memory-md-run.js +151 -0
  69. package/dist/store/memory-md-skeleton.d.ts +18 -0
  70. package/dist/store/memory-md-skeleton.js +61 -0
  71. package/dist/store/memory-md-sync.d.ts +59 -0
  72. package/dist/store/memory-md-sync.js +152 -0
  73. package/dist/store/memory-md-types.d.ts +243 -0
  74. package/dist/store/memory-md-types.js +10 -0
  75. package/dist/store/memory-md.d.ts +117 -0
  76. package/dist/store/memory-md.js +230 -0
  77. package/dist/store/obsolete-notices.d.ts +39 -0
  78. package/dist/store/obsolete-notices.js +153 -0
  79. package/dist/store/recall-feedback.d.ts +75 -0
  80. package/dist/store/recall-feedback.js +213 -0
  81. package/dist/store/recall-usage.d.ts +11 -0
  82. package/dist/store/recall-usage.js +29 -0
  83. package/dist/store/session-recalled.d.ts +38 -3
  84. package/dist/store/session-recalled.js +156 -9
  85. package/dist/store/sqlite.d.ts +66 -0
  86. package/dist/store/sqlite.js +233 -15
  87. package/dist/store/supersede.d.ts +1 -1
  88. package/dist/store/supersede.js +1 -1
  89. package/dist/tools/index.d.ts +4 -0
  90. package/dist/tools/index.js +26 -7
  91. package/dist/tools/slots.d.ts +2 -1
  92. package/dist/tools/slots.js +2 -1
  93. package/dist/types.d.ts +24 -7
  94. package/dist/types.js +10 -2
  95. package/dist/util/maturity.d.ts +20 -3
  96. package/dist/util/maturity.js +18 -1
  97. package/dist/util/md-name.d.ts +129 -0
  98. package/dist/util/md-name.js +366 -0
  99. package/dist/util/md-normalize.d.ts +23 -0
  100. package/dist/util/md-normalize.js +131 -0
  101. package/dist/workspace-route.d.ts +22 -0
  102. package/dist/workspace-route.js +66 -0
  103. package/dsh.plugin.json +1 -1
  104. package/package.json +6 -5
package/CHANGELOG.ko.md CHANGED
@@ -9,6 +9,422 @@
9
9
 
10
10
  이 파일은 **0.12.0** 릴리스 노트와 현재 **미출시** 변경 사항의 한국어판입니다. 전체 이력은 [CHANGELOG.md](./CHANGELOG.md)(中文)를 참조하세요.
11
11
 
12
+ ## [0.24.1] — 2026-10-14
13
+
14
+ ### 추가
15
+ - **투영 접미사 불변식 수렴(`convergeDisabledSuffix`)**: `.memory.md` / `.memory.md.disable` 는 "이 기록이 라이브러리에서 퇴장했는지"로만 결정된다. `name` 과도, 어떤 경로로 퇴장시켰 과도 무관. 각 항목을 "파일 존재 + 현재 접미사 + 지문 일치" 세 조건으로 맞춘다. 사람이 만든 `.disable` 는 파일 측 퇴장 증표이므로 ``both`` 로 보고만 하고 손대지 않는다.
16
+ - **라이브러리→파일 고리를 `L1Store` 에 수구(`store/memory-md-sync.ts`)**: `appendNew` / `retire` / `restore` / `rename` 네 합류점에서 `setFileSyncHook` 을 점화한다. 이전처럼 8 개 호출점에 흩어진 이벤트 고리는 실측 920 건 퇴장 중 8 건만 `.disable` 를 만들었다(리콜 패널 한 경로). 기계 측 퇴장(병합 치환, 충돌 패배)과 모델 `memory_add` 도 자동으로-follow 한다.
17
+ - **이름 바꾸기 추적**: `rename()` 이후 투영 파일을 새 이름으로 옮기고(`.disable` 도 함께) 대장 키를 갈아치운다. 옛 경로에 파일이 없으면 새 이름으로 쓰고, 목적지가 이미 쓰여 있으면 덮어쓰지 않고 보고만 한다.
18
+ - **장시간 작업의 굴림 실행과 진행 표시(`store/memory-md-run.ts`, 새 엔드포인트 `dsh-memory/mem-run-status`)**: 한 번의 클릭으로 내보내기 커서를 표 끝까지, 이어서 수렴 커서를 표 끝까지 밀고, 가로막는 종합 한도(`maxPasses` / `totalBudgetMs`) 때문에 남은 분량은 `notice` 에 그대로 남긴다.
19
+ ### 변경
20
+ - **1 회 클릭 파일 갱신 예산**: `memExport.maxRecords` 기본값 300 → **2000**, 실행 중 돌리기 `live.mdExportMaxRecords` 추가(0 = 배포 기본값, 재시작 불요). 패널의 이름 맞지 않던 "단차 동작 가드레일" 을 **"단차 파일 갱신 예산"** 으로, 단위를 "개 파일/회" 로, 표시는 서버가 실제로 쓴 `appliedMaxRecords` 를 쓴다.
21
+ ### 수정
22
+ - 예산이 "순회 건수"를 셌다(변화 없음도 할당 소비) → 1210 건 복원에 클릭 4 회가 필요했다.
23
+ - 항목별 대장 flush = O(n²)(300 건 = 통째 JSON 재작성 300 회) → 루프에서는 더티 표시만, 마무리에서 1 회 flush.
24
+ - 겹쳐 점화한 두 작업이 서로 밟았다 → 투영 작업을 뒷에서 **직렬화**. 종래에는 `appendNew` 직후 `retire` 에서 두 번째 수렴기가 "파일이 없다" 고 판해 퇴장 기록이 `.memory.md` 에 남았다.
25
+ - 복권 후 영구 이중화 → "대장 동일 id + `.disable` 가 기록 후 손대지 않음 + `.memory.md` 가 현재 렌더와 동일" 세 조건이 갖춰질 때만 오래된 증표를 지운다.
26
+
27
+ ## [0.24.0] — 미출시(로컬 라인 `0.24.0-local.0…3`, 2026-10-13)
28
+
29
+ ### 새로 추가
30
+
31
+ - **타입 어휘의 단일 진실 출처(`src/memory-types.ts`)**: 호스트 기억 분류 29류의 원명 + 플러그인 구 7류 + `unclassified` 폴백, 합계 37류. 투영 디렉터리 화이트리스트, `memory_add` 쓰기 화이트리스트, 패널 라벨, 추출 및 중복제거 prompt의 열거 문자열, memport의 TS/Python 이중 구현 타입 코드 **전부가 여기서 파생된다**. 종전에는 "타입 전집"을 네 곳에 손으로 옮겨 적어 각자 진화했다: `memory_add`는 외부 분류를 **조용히 `episodic`으로 고쳐썼고**, memport 문구는 "있는 그대로 통과"를 약속했다 — 같은 사안에 규격이 두 개였다.
32
+ - **이름 증류(`cfg.naming`)**: 추출이 그 자리에서 `title`을 산출하며, `content`와 **분리 정의**한다(title는 "이 항목은 무엇을 다루는가"에 답하는 명사구, content는 완전한 서술 본문). 중복제거 merge는 `merged_title`도 함께 산출한다. 저장 시 `title`은 단지 **이름 후보**일 뿐이며 반드시 `validateTitle` 형태 게이트를 통과해야 하고, 통과 못 하면 여전히 작명기에 넘긴다 — 이름 유일성의 진실 출처가 출처가 하나 늘었다고 판정 기준을 두 벌로 갈라서는 안 된다.
33
+ - **few-shot 표준 형식 예시집(`src/prompts/examples.ts`)**: 유형별 모범 예 + 반례, 총길이에 예산 상한이 있다(예시는 매 라운드 증류마다 지불하는 고정 비용). 모범 예 스스로 반드시 게이트를 통과해야 하고 골격이 갖춰져 있어야 하며, 테스트 케이스는 판정 기준을 손으로 베끼지 않고 `auditFewShot()`을 직접 쓴다.
34
+ - **골격 판정 기준의 실행화(`SKELETON_ELEMENTS` + `scoreSkeleton`)**: "전후 인과가 없고 다 못 쓰면 쓸 수 있는 것이 아니다"를 프롬프트 속 소망에서 측정 가능하고 기본값 결정에 쓸 수 있는 판정 기준으로 바꿨다. 한계 선언: 키워드 휴리스틱이며 같은 입력의 두 팔 상대 비교에만 쓸 수 있고, 절대 품질 점수가 아니다.
35
+ - **디렉터리 골격 + 유형별 `_index.md`(`memory-md-skeleton.ts`)**: 각 유형 디렉터리에 인덱스 파일 하나(류명 + 라이브러리 항목 수). 빈 디렉터리는 git과 압축 패키지에서 사라져 버리는데, "이 디렉터리가 없다"와 "이 유형은 0건"은 사람의 눈에 같은 모양으로 보인다. 멱등: 내용이 그대로면 쓰지 않아 mtime이 갱신되지 않는다. 투영 면이 꺼져 있으면 동작이 전혀 없다.
36
+
37
+ ### 변경
38
+
39
+ - **세 곳의 기본값을 A/B 실측으로 뒤집었다**(`narrativeTemplates.enabled` / `naming.enabled` / `naming.fewShot`). 근거는 **방향이 정반대**인 두 실측: 실제 세션 증류 경로에서 예시 없을 때 골격 완비 **0.000**(0/23), 예시 있을 때 **0.703**(26/37). 반면 기존 항목 이름을 바꾸는 작명 태스크는 예시를 넣으니 오히려 **−0.113**(답 누락 18/160). 결론: **예시는 태스크를 넘어 쓸 수 없다** — 추출 프롬프트에만 넣고 작명 스크립트에는 넣지 않는다.
40
+ - **md 투영 기본 켜기**(`memExport.enabled`): 계획 전체의 전제가 "파일이 내용의 권위 + 라이브러리가 인덱스"인데, 기본 끄기는 기본 미사용과 같다. `mdSync.enabled`는 **여전히 기본 끔** — 이는 라이브러리에 쓰므로 리스크가 비대칭이다.
41
+ - **workspace 귀속 설정 면의 이름 변경 `workspaceHints` → `workspaceSource`**: 호환 별명 없이 단명으로 바꾼다(이 키는 어떤 릴리스로도 외부 발송된 적이 없어, 별명을 두면 두 번째 사실 출처를 기르는 셈). 기본 켜기. 라우팅 `path`는 **workspace 루트**를 저장한다(구값은 `E:\test\rewrite-agently\.agents`로 적혔는데 `targetFor`가 `.agents/memory`를 한 번 더 이어 붙여 `.agents\.agents\memory\` 고아 트리가 나왔다. "루트가 없어야 단일 루트로 되돌린다"는 가드가 이 형태를 막지 못한다). 실머신 기존분 채우기에서 **724/1166**건이 귀속을 받았고, `hashRecords`는 한 글자도 변하지 않았다.
42
+
43
+ ### 수정
44
+
45
+ - **장부 로더가 절대 키를 `relPath`에 덮어썼다**: N10 이후 장부 키는 절대 경로인데, 로드 시 "일치하지 않으면 키를 기준으로 다시 쓴다"는 저 한 문장이 로드마다 반드시 발동했다. 실측 오염 **1167/1241**건. 하류 증상은 고아 아카이브가 콜론을 포함한 불법 디렉터리를 조립하고, 75건 이동이 전부 ENOENT. `root` + 키 유도 방식으로 고쳐(신규 `deriveRelPath`) 아카이브 측은 저장된 `relPath`를 더 이상 신뢰하지 않는다.
46
+ - **`--revert`에 hooks 폴백이 없었다**: `findArchivedOld`가 `deps.hooks.exists`를 바로 만지는데, `runCli`가 revert를 호출할 때 hooks를 넘기지 않는다(apply 쪽에는 `deps.hooks ?? defaultHooks`가 있고 revert 쪽에서 빠졌다). 샌드박스에서 첫 롤백을 돌렸을 때 74건 전부 오류. 요점은 피해의 비대칭이다: apply에서 사고가 나면 `--revert`로 물러나는데, **롤백 자체가 망가져 있으면 백업이 유일한 퇴로라는 뜻**이다.
47
+ - **루트 간 중복 파일이 "양쪽 다 변했으면 손대지 않는다"를 우회했다**: `roots = 기록 착지 루트 ∪ 장부가 기록한 루트`라 같은 기록이 두 루트 아래 각 한 부씩 파일을 가질 수 있다. 판정은 구 루트 쪽을, 디스크 쓰기는 `targetFor`가 계산한 신 루트 쪽을 취한다 — 그래서 구 루트에서 사람이 한 편집이 권위 쪽을 덮어쓰는데, 보고는 완전히 적합해 보인다. 이제 현재 착지 루트만 인정하고 나머지는 신규 필드 `staleRootFiles`에 집계해 패널에 표시한다.
48
+ - **이름 충돌이 기억 한 통째를 검색 라이브러리에서 밀어냈다**: `{ ...구기록, id: 새 id }` 같은 작성 방식은 "이미 다른 행에 속한 이름"을 달고 DB에 쓴다 → 부분 유니크 인덱스에 충돌 → 일괄 쓰기가 실패해 건별 회수해도 여전히 실패 → 해당 merge 산물이 **조용히 사라진다**(구기록은 이미 퇴장했으므로 이번 치환은 아무것도 남기지 않는다). 작명 판정을 "name이 비어 있음"에서 **"이 이름이 그에게 속하지 않음"**으로 바꿨다.
49
+ - **벡터 "채울 N건"이 영영 0이 되지 않고 "인덱스 재구축 할 일 없음"**: 표시 면의 `missing = total − embedded` 분모에 퇴장 행이 포함되는데, 퇴장 행은 설계상 벡터가 없는 것이 합법이다. 채움을 구동하는 두 SQL 모두 퇴장 행을 제외하므로 같은 화면에 "채울 114"와 "할 일 없음"이 동시에 뜬다. 분모 규격을 활성 행으로 통일했다.
50
+ - **투영 가드가 센 것은 "순회"이지 "동작"이 아니었다**: `maxRecords`는 "변화 없음" 판정 항목까지 할당에 넣는다. 두 번째 클릭도 같은 앞 N건을 다시 훑는다 — 패널의 "한 번 더 누르면 계속"은 거짓말이었다(실기 증상: `已写 9 · 未变 291 · 剩余 866` 이 계속 그대로). 이제 디스크를 건드린 항목만 세고, 순회 총량은 `timeBudgetMs`가 마감을 맡는다.
51
+ - **장부 디스크 쓰기 O(n²)**: `set()`가 항목마다 장부 전체 JSON을 원자적으로 다시 썼다. 1200건 동일 디스크 실측 **9442ms → 1954ms**(더티 플래그 + 배치 창 + `flush()` 강제).
52
+ - **`NAME_MAX`(40)와 `NAME_CLIP_MAX`(28)를 두 개의 수로 분리**: 문자 계산은 ASCII와 한자를 같은 값으로 친다. `dsh-agent-teams` / `Python-3.13` 한 단어가 15 슬롯을 먹고, 의미상 한두 단어짜리 정당한 이름이 `too-long` 판정을 받았다. 반면 통째로 40으로 놓으면 `deriveName`이 서술문을 문장 꼬리까지 베껴 쓴다(실측함). 김에 `validateTitle`에 하드코딩된 28도 고쳤다(같은 임계치에 사실 출처가 둘이었다).
53
+ - **이름 게이트의 세 곳 오폭**: 앞부분 잘라내는 표와 이름 거부 표를 **갈라서 관리**한다(주어만 남은 "사용자/팀"이 "사용자 npm 계정 사용자명"류 명사구를 오폭하지 않게 됐다, 실기 10건). `has-id-fragment`는 "비 ASCII 문자 뒤의 4자리 이상 숫자"로 좁혔다(`ADR-0006`, `Han-1413141`은 통과, 실기 4건).
54
+
55
+ ### 알려진 한계
56
+
57
+ - 반추 마감의 증분 투영은 `id→updatedAt` 기준 diff에 의존하는데, relabel / 퇴장은 `patchMetadata`를 타서 `updated_time`을 건드리지 않는다 ⇒ **metadata-only 재작성은 해당 라운드에 재투영되지 않는다**.
58
+ - P2 역방향 동기가 두 가지 미비: 시작 자동 훅(엔드포인트 + 버튼만 제공하는 것으로 바꿨다), `pending` 무리가 `conflicts` 재판 대기 면에 들어가지 않음.
59
+ - **8개 언어 번역본이 0.20.0 이후 따라오지 않았다**(이번에는 0.22.0 이후 항목만 채워 넣었고, 0.20/0.21 공백은 그대로다 — 별도 기록).
60
+
61
+ ## [0.23.0] — 미출시(로컬 라인 `0.23.0-local.1…8`, 2026-10-12…13)
62
+
63
+ ### 새로 추가
64
+
65
+ - **md 권위 투영 면(P0/P1/U/N10/N11)**: `l1_records.name` 컬럼(파일 신원 키 + 부분 유니크 인덱스). `renderRecord` + 이중 지문 장부(`dbDigest`/`fileDigest`) + `mem-export` / `mem-reconcile` 엔드포인트. 패널 "외부 아카이브" 절. 투영 루트를 **workspace 우선**으로 변경(`<ws>/.agents/memory/<유형>/<이름>.memory.md`). 만료 판정은 `.memory.md.disable`과 연동된다.
66
+ - **P2 역방향 동기(파일 → 인덱스)**: 장부 이중 digest로 **삼방 구획**을 그린다 — 파일만 변했으면 라이브러리로 되돌려 흘리고 정규 렌더링으로 다시 쓴다(쓰기 순환 소거), 라이브러리만 변했으면 `exportRecords`를 재사용해 파일을 다시 쓴다. **양쪽 다 변하면 양쪽 모두 손대지 않고** `pending`만 건다. `.disable`은 퇴장으로 처리하고 파일은 지우지 않는다. 출처 불명의 신규 파일은 수만 세고 라이브러리에 넣지 않는다(도입은 쓰기 커널을 통과해야 한다). 엔드포인트 `dsh-memory/mem-sync`.
67
+ - **P3 아카이브 + 물리 삭제**: `exportThenPurge` 게이트를 라이브러리 전체 해시에서 **배치 단위 투영**으로 바꿨다(동시 증류가 이번 배치와 무관한 어떤 기록이든 건드리면 구 구현은 정리가 영영 못 지우게 된다). 삭제 후 사람이 읽는 `retired.md`를 쓰고, 이 배치의 투영 파일(`.disable` 포함)을 스냅샷 디렉터리로 옮긴다. `cleanup-retired` 드라이런은 **삭제 대기 목록 미리보기**를 돌려준다. 그래프 투영 인덱스 테이블에 `forgetRecords` 신규 추가(종전 전체 저장소는 증가만 있고 감소가 없었다 — 스냅샷에서 복원된 기록은 투영 행이 남아 있어 "이미 투영됨"으로 취급되어 **영영 재투영되지 않는다**. 검색 그래프가 오류 없이 조용히 한 항목 결손).
68
+ - **P4.1 기본류 `unclassified` + P4.2 본문 형식 표준화**(`normalizeContentMd`). 멱등은 하드 요구사항이다: 멱등이 아니면 장부 `fileDigest`가 라운드마다 변하고, 쓰기 순환을 렌더러 스스로 불러온다. 형식만 하고 의미는 건드리지 않는다(산문을 bullet화하지 않고, H1을 삽입하지 않고, 본문에 메타데이터를 밀어 넣지 않는다).
69
+ - **고아 투영 파일 아카이브 `dsh-memory/mem-orphans`**: "장부가 우리가 썼다고 기록했고 지금 이 경로를 주장하는 기록이 하나도 없는" 파일만 옮긴다. 장부에 없는 출처 불명 파일은 **절대 손대지 않는다**(사람이 투영 디렉터리에 넣고 돌아오기를 기다리는 입력일 수 있다). 지우지 않고 옮기기만 해 상대 형태와 원문을 보존한다.
70
+
71
+ ### 동작 수정(호출 표시면)
72
+
73
+ - 호출 항목이 더 이상 순간 사라지지 않는다: view에 `body` / `retired`를 담고, `session_recalled` 행 제거를 멈추며, pill에 "만료 판정" 취소선 주석을 표시한다.
74
+ - 시작 시 기존 퇴장 잔재를 청소한다(이 기기 실측 725 행이 여전히 `l1_fts`에 더러움). 퇴장 불변식에 두 개의 우회 쓰기 경로를 보완("무효 표시 후 검색 면에서 부활" 수정).
75
+ - 호출 채점 엔드투엔드: `-1..5`(0..5 관련도, `-1`은 만료 선언 및 퇴장). 채점이 성숙도 지표에 들어간다(`-1/1/2`는 끌어내려 침전, `4/5`는 끌어올려 부상).
76
+ - workspace 내용 라우팅 첫 착지(dsh 생태 → `rewrite-agently`, 비 dsh → `agent-no-dsh`. **`workspace_id`만 쓰고 `scope`를 건드리지 않으며 `updated_time`도 건드리지 않는다**. `l1_fts` 행이 스스로 지닌 귀속 컬럼도 동기한다).
77
+
78
+ ### 규율
79
+
80
+ - memport 두 초거대 함수를 두 차례에 걸쳐 분리(Extract Function, 동작 보존, CLI 골든 표준 바이트 단위 회귀).
81
+ - 엔드포인트 계약 **다섯 곳 동시 변경** 규율이 계속 유효(`MEMORY_ENDPOINTS ≡ switch ≡ contract 이중 map ≡ contract-keys 카운트 ≡ config`), 카운트 체인 55→56→57→58→59.
82
+
83
+ ## [0.22.2] — 2026-10-12
84
+
85
+ ### 새로 추가
86
+
87
+ - 스킨 세 상태(auto/light/dark) + 토큰 오버라이드 블록. 패널 11 Tab 한 줄로 조이기.
88
+ - `memory_import`의 `ext_key` 멱등(`l1_records.ext_key` + 부분 유니크 인덱스 + `deduped` 영수증).
89
+ - **Memport Connect**: `.mem` 파싱 / 사전 필터 / 스캔 + 출처 레지스트리 + Connect Tab(구현 간 sha256 골든 표준 정렬).
90
+ - **스킬 결정화**: skill-store(JSON 사이드카) + 후보 감지 + 스킬 호출 주입 + 보드 스위치 `skillsEnabled`.
91
+ - G4 그래프 인과 간선(야기 / 원인 / 수정함 / 대체, `projectorVersion`을 bump하지 않음).
92
+ - G3 성숙도 읽기 시 파생(기록 카드 배지 + 모순 재판 뷰 주석, 자동 재판에는 참여하지 않음).
93
+ - G1 `usageScenario` 게이팅 엔드투엔드(추출 → `metadata.dsh_usage_scenario` 저장 → 호출 세밀 랭킹. off는 한 글자도 동일).
94
+ - G2 유형 서술 템플릿(게이팅 prompt 변형. off는 on의 접두어).
95
+ - wing / 인지 hall 두 축이 서로 섞이지 않는다(직교 접근자 + 값 범위가 겹치지 않음을 보장하는 회귀 잠금).
96
+ - **G-live 이번 세션 활성 호출 집합**: 지속 사이드카 + `session-recalled` 읽기 전용 엔드포인트 + pill 읽기 전용 목록.
97
+ - `memoryRecall` 세션 인식 투영(brief 사이드바 "이번 세션 호출" 구분의 데이터 소스, `init(header).id`).
98
+ - `cfg.slots.migrateLegacy`(기본 끔): "핀으로 상주화됐지만 내용은 이미 활성 L1이 담당하는" rule 슬롯을 소프트 퇴장한다.
99
+
100
+ ### 수정
101
+
102
+ - 반추 되돌림이 처리 대기 카운트를 되돌리거나 늘렸다(ruminate는 빈 메시지를 넘기도록 변경, `enqueueSessionSlices`와 동일 시맨틱스).
103
+ - 반추 finalize와 `doLightRefresh`가 L3를 강제로 실제로 실행하도록 수정 + 카운트 0으로 초기화 + `state.save()` 디스크화("L2/L3 반추 후에도 안 사라지고 재시작하면 다시 뜀" 수정).
104
+
105
+ ### 규율
106
+
107
+ - 신규 능력은 전부 기본 끔, off 상태 산출물과 주입이 한 글자도 동일. 플러그인 간 표시 계약(`memorySlots` 투영) 배선을 유지.
108
+
109
+ ## [0.22.1] — 2026-10-09
110
+
111
+ ### 수정
112
+
113
+ - **오버레이 하단을 불투명으로**: 입력란의 기억 오버레이(`.dsh-mem-popover`)와 드롭다운 / 필터 패널(`.dsh-mem-pop`)이 종전 호스트의 `--dsw-specific-menu`를 따랐는데, 호스트가 이를 반투명 유리 값으로 해석 ⇒ 패널이 배경을 투과시켰다. 이제 **불투명 `bg-primary` 바탕**에 별명 레이어를 얹는 underlay 방식으로 바꿔, 호스트의 alpha 값과 무관하게 패널이 항상 불투명하게 보인다.
114
+
115
+ ## [0.22.0] — 2026-10-09
116
+
117
+ ### 변경
118
+
119
+ - **선언 면 15rc 대응**: dsh peers 9건이 출시된 호스트 rc를 전부 커버(`0.1.0-rc.2..0.2.0-rc.2`, 건별 `npm view`로 증거를 확보했고 각 라인 집합이 일치), 전부 optional. `engines >= 0.1.0-rc.2`는 대략의 울타리(정밀 게이트는 peers), `package.json`과 `dsh.plugin.json`을 동기.
120
+ - **이중 매니페스트 버전 바로잡기**: `dsh.plugin.json`이 앞서 `0.20.0-beta.6-dsh0.2.0.1`에 흘러가 있었는데, 이번 배치에서 `package.json`과 한 글자까지 맞췄다(V1 게이트).
121
+ - **`dsh.client.inject`를 `ui-primitives` only로 축소**: `dsh-client-connection`은 0.1.2부터 패키지로 존재하므로 선언하면 client entry 전체가 0.1.0/0.1.1에서 로드 실패한다(`rpc.ts`는 진작 `ctx.get`으로 해당 연결을 게으르게 얻었다).
122
+
123
+ ### 규율
124
+
125
+ - build형 저장소에 `prepack: npm run build`를 전부 주입 — `npm pack`은 prepack만 발동하고 `prepublishOnly`를 발동하지 않는다. stale-build 누명의 삼중 방어선 중 하나.
126
+
127
+ ## [0.21.0] — 2026-10-04
128
+
129
+ ### 새로 추가
130
+
131
+ - **도구 그룹 봉인(기억 라이브러리 패널 → 기억 → 도구 봉인)**: 5개 그룹 스위치(room 관리 / graph 그래프 / ruminate 반추 / conflict 충돌 / mutate 쓰기·삭제, 13개 도구 커버). 끔 = 그 그룹 도구가 **등록되지 않는다**, 모델 도구 목록에서 곧장 사라진다(실행 시점 거절이 아니다); 스위치 뒤집기는 volatile 열갱신으로 즉시 발효, 재시작 불요. 핵심 읽기(memory_search / memory_read_scene / memory_receipts / conversation_search)와 슬롯 3건은 제어 밖. 배경: 대다수 그룹 도구(예컨대 Room 통치·반추)는 일상 사용 빈도가 극히 낮아, 상주 도구 목록은 주의만 허비하고 모델의 오인 호출을 부른다.
132
+ - **memory_slot_write의 mem_ 참조 존재성 검증**: refs에서 `mem_*` 형태인 L1 record_id는 쓰기 전 `getByIds` 존재성 검증을 거친다, 지어냈거나 철자가 틀린 id는 곧장 거부하고 memory_search를 거치라 안내한다 — "search가 깨운 기억"이 슬롯 참조에 들어가면 반드시 실제 기록임을 보장한다(brief 쪽 이름 약술 해석이 신뢰할 만한 입력을 얻는 근거). 파일 경로/URL류 refs는 검증하지 않는다.
133
+
134
+ ## [0.20.4] — 2026-10-04(로컬 빌드, 미출시)
135
+
136
+ ### 수정
137
+
138
+ - **투영 refViews 해석의 전면 내결함**: `resolveRefViews` 안에서 L1 `getByIds` 호출에 try/catch를 걸었다 — registry의 drive에 예외 보호가 없어, 해석 계층이 아무 예외(sqlite 점유/백엔드 강등/기록 상태 이상)든 토하면 세션 이벤트 구동 전체는 물론 호스트 프로세스까지 무너진다. 실패 시 이번 프레임은 refViews 없이 강등(프론트는 원시 refs로 회수).
139
+
140
+ ## [0.20.3] — 2026-10-03
141
+
142
+ ### 수정
143
+
144
+ - **`memory_search` 렌더 텍스트에 id 보충**: v0.20.2가 record_id를 출력 JSON과 schema에 넣었지만, 모델이 보는 것은 render 산물이지 원시 JSON이 아니다 — 렌더 줄에 id가 없으면 모델로서는 안 돌려준 것과 같다(실머신 재측에서 발견). 이제 각 결과 끝에 `— id: mem_xxx`가 붙어, 그대로 슬롯 refs에 쓸 수 있다.
145
+
146
+ ## [0.20.2] — 2026-10-03
147
+
148
+ ### 수정
149
+
150
+ - **투영 `memorySlots`의 더티 판정을 임의 이벤트로 완화**: 종전엔 `tool/result`(settled, 비-error)일 때만 store rev로 더티를 판정 — 쓰기 후 "하필 또 도구 결과가 하나 더 와야" 갱신되고, 세션 간 조회는 조회 세션 자신의 도구 호출을 더 기다려야 했다; 실머신에서 쓰기 후 사이드바가 오래 빈 프레임에 고착되는 증상이 드러났다. 임의의 확정된 이벤트에서 O(1) rev 비교를 하고 rev가 변하면 곧장 재건·push하도록 변경; isError 가드는 제거(rev는 실제 변경 후에만 증가, 이벤트는 촉발자일 뿐 데이터 소스가 아니다).
151
+ - **상주 주입을 `<system-reminder>` 태그 프레임으로 변경**(dsh-agent-instructions의 AGENTS.md 주입 house style에 정렬): user 역할 담당은 불변, 다만 태그 상자 + 권위 선언(응답 불요/재서술 말 것/시스템 프롬프트와 사용자 직접 지령을 덮지 말 것)으로 제시 — 약한 모델이 슬롯 내용을 사용자 발화로 오인해 그대로 재서술하며 정상 답변을 방해하던 문제를 고쳤다; 슬롯 본문 안의 닫는 태그는 전이(`</system-reminder>`)시켜, 슬롯 내용이 프레임 경계를 위조하는 것을 막는다.
152
+
153
+ ### 새로 추가
154
+
155
+ - **`memory_search` 결과가 `id`(L1 record_id)를 밖으로 드러냄**: "검색으로 id를 못 얻음 → 슬롯 refs / memory_receipts에 못 넣음"의 교착을 해제; schema 서술에 id의 사용처 두 곳을 명시.
156
+ - **wire view가 `refs` + `refViews`를 드러냄**: `mem_*`류 참조는 투영 프레임 구축 시 L1 `getByIds`를 거쳐 `[type] 이름 약술`(content 첫 줄, 80자 절단)로 해석되고, brief-sidebar(≥0.5.0-beta.2)가 직접 렌더하며, 경로/URL류 참조는 문자 그대로 유지; 결손/이미 퇴장한 기록은 항목을 만들지 않는다. 해석은 프레임 구축 시점에 일어난다 — 참조 집합은 슬롯 쓸 때 이미 동결되므로, L1 기록을 사후 편집해도 옛 프레임에 역류하지 않는다. 자체 구현 schema는 부가 필드에 대해 "있어야만 검증"하므로 옛 checkpoint 행은 통째로 통과(stateVersion을 건드리지 않는다).
157
+
158
+ ## [0.20.1] — 2026-10-03(정식화; beta 채널이 beta.1 이후의 전 기능을 확정)
159
+
160
+ Hall·Room 분류 관리 전 연쇄(2단계 등록부/merge/rename/retire/update/편입/기록 동반 퇴장/미등록 퇴장 시 항목 보완), Hall과 Room 각각의 독립 관리 페이지(검색 필터·기본 접힘·포인터 드래그 분류·퇴장 필터), 노화 가중치 표시 + 호출 사용 통계(topN에 들어가야 비로소 used로 셈). 정식화 직전 beta는 0.20.0-beta.26이고 내용은 0.20.1과 일치; `dsh-0.2.0-beta` 채널은 이후 beta를 계속 받는다.
161
+
162
+ ## [0.20.0-beta.26] — 2026-10-03
163
+
164
+ ### 새로 추가(Hall/Room 증가 후의 사용성)
165
+
166
+ - **Hall/Room 이름 검색 필터**: Hall 페이지에 검색 상자를 신설(slug·표시 이름·별칭, 대소문자 무시) — hall 자기 이름에 걸리면 그 하위 room을 모두 보이고, 아니라면 걸린 하위 room만 남기며, 조 전체가 안 걸리면 숨긴다; 검색 중에는 매칭된 조가 자동 펼쳐진다(접힌 상태가 더는 결과를 가리지 않음); 자체 성장 미등록 영역도 같은 필터. Room 페이지에도 같은 검색 상자, 퇴장 필터 chips와 중첩 발동.
167
+ - **Hall 그룹 접기, 기본으로 전부 접힘**: hall 헤더에는 "합계 N 건 · M개 room"만 보이고, 헤더나 ▸/▾ 단추를 눌러 하위 room을 펼치거나 접는다; "전체 펼치기/전체 접기" 일괄 단추; 펼침 상태는 localStorage에 기억(`dsh.memory.halls.open`). 분류 지정 클릭(hall 헤더를 눌러 분류 완료) 우선순위는 변하지 않는다.
168
+
169
+ ## [0.20.0-beta.25] — 2026-10-02
170
+
171
+ > 자체 성장 미등록 room 퇴장 후 상태 비가시 수정 + Hall 하위 room 표시 규칙(사용자 판정).
172
+
173
+ ### 수정/변경
174
+
175
+ - **자체 성장 미등록 room은 퇴장하면 곧 "퇴역/복원"으로 바뀐다**: 종전엔 퇴장 시 기록만 소프트 삭제했고 어휘표에 항목이 없어 바낄 것이 없었으므로 버튼은 언제나 "편입/퇴장"이었다. 이제 retire withRecords가 미등록 slug에 **자동으로 grown 등록 항목 하나를 보완하고 retired로 전환** — 패널 줄이 즉시 "퇴역" 상태로 전환, "복원"(active:true) 후 어휘표에 복귀. 영속화하며 재시작해도 사라지지 않는다.
176
+ - **Hall 페이지 하위 room은 active만 표시**: retired/퇴장한 room은 더 이상 hall 그룹 아래에 안 나온다(Room 페이지에서 본다, "퇴역" 필터 포함); 그룹 집계 계수도 따라 active 하위류만 센다.
177
+
178
+ ## [0.20.0-beta.24] — 2026-10-02
179
+
180
+ ### 수정
181
+
182
+ - **Hall 페이지 펼치기/퇴장을 Room 페이지와 정렬**: Hall 페이지의 room 줄을 펼칠 때도 활성 기록만 당긴다(`retired:false`); 퇴장 확인 후 해당 room이 펼쳐져 있으면 펼침 영역을 자동 갱신.(beta.23은 Room 페이지만 보강하고 Hall 페이지 동기화를 빠뜨렸다 — 사용자 실측 "퇴장을 눌러도 효과가 안 보인다"의 또 다른 한 군데.)
183
+
184
+ ## [0.20.0-beta.23] — 2026-10-02
185
+
186
+ > **Room 페이지 퇴장 필터 보강**(beta.22의 필터 chips는 패치 스크립트가 도중 실패해 디스크에 못 내려가고 배지만 패키지에 들어갔다 — 이번엔 항목별 Edit로 내려놓고 전부 통과 검증).
187
+
188
+ ### 수정/새로 추가
189
+
190
+ - **퇴장 필터 chips**(Room 페이지, 등록 폼 위): 전체 / 활성(어휘표 미퇴역) / 퇴역.
191
+ - **퇴장 효과가 즉시 발현**: 전개 항목은 **활성** 기록만 당긴다(`retired:false`) — 퇴장 후 펼침 영역은 곧 "기억 항목 없음"; 퇴장 확인 후 해당 room이 펼쳐져 있으면 펼침 영역을 **자동 갱신**; [퇴장됨] 배지 + 회색화 폴백(beta.22에 이미 포함).
192
+
193
+ ## [0.20.0-beta.22] — 2026-10-02
194
+
195
+ > 퇴장 효과 가시성 + Room 페이지 퇴장 필터(실측 피드백: 퇴장 확인 후 화면 변화 없음, 필터할 수도 없음).
196
+
197
+ ### 수정/새로 추가
198
+
199
+ - **퇴장 효과가 즉시 발현**: Room 페이지에서 항목을 펼치면 **활성** 기록만 당긴다(`retired:false`) — 퇴장 후 펼침 영역에 곧 "이 Room에는 기억 항목이 없습니다" 표시; 퇴장 확인 후 해당 room이 펼쳐져 있으면 항목을 **자동 갱신**; 혼배 폴백: 이미 퇴장한 항목엔 [퇴장됨] 배지를 달고 회색화.
200
+ - **퇴장 필터**: Room 페이지에 "전체 / 활성 / 퇴역" chips 신설(어휘표 퇴역 상태 필터).
201
+
202
+ ## [0.20.0-beta.21] — 2026-10-02
203
+
204
+ > **퇴역 시 기록 동반**: room 퇴역 시 **그 아래 활성 기억을 통째로 함께 퇴장**시킬 수 있다(종전엔 동사표만 있어, 특히 자체 성장 room은 처리 수단이 없었다).
205
+
206
+ ### 새로 추가
207
+
208
+ - **`retire withRecords`**(Room/Hall 페이지 "퇴장" 버튼, 빨강): 어휘표 퇴역 + 그 room 아래 **활성** 기록 일체를 배치 소프트 삭제(매배치 ≤200, records-delete와 같은 원어; reason=manual) — 기록은 기억 페이지 "퇴장됨" 영역에서 건별 복원 가능. 자체 성장 미등록 줄도 "퇴장"이 있다(기록만 퇴장, 어휘표 항목은 손댈 것이 없음). 응답에 `retiredRecords` 건수를 담는다.
209
+ - "고아 내보내기" 버튼을 **기억 질의 패널**(고아 필터 줄 안)로 되돌렸다.
210
+
211
+ ### 수정
212
+
213
+ - recall-usage 테스트 격리(영속 루프 케이스의 병렬 경합); RoomTab 퇴역 확인창의 문자 그대로 `\n` 표시 수정.
214
+
215
+ ## [0.20.0-beta.20] — 2026-10-02
216
+
217
+ > **Room 독립 탭**(Hall과 분리): room의 존재·이름·항목 각각의 관리 면이 있다; 기억 페이지는 더 이상 Room 태그를 쌓지 않는다.
218
+
219
+ ### 새로 추가
220
+
221
+ - **"Room" 탭**(Hall 뒤): 모든 room을 납작하게 나열(등록 항목 ∪ 자체 성장 미등록) — **존재**(2단계 slug 등록 / grown 편입 / 퇴역·복원), **이름**(표시 이름+분류 설명 편집; 개명은 2단계 slug 가능, 기록은 따라 이동), **항목**(▸ 펼쳐 그 room의 기억 항목 열람, 기본 접힘, 최초에 앞 10건만 당김); 고아 내보내기/고권한 스위치는 페이지를 따른다.
222
+ - **등록 후 재편집 가능**: 새 `room-admin update`(label/description 재작성, 미제공 필드는 그대로)+ store `RoomRegistryStore.update` + 도구 `memory_room_admin action=update` + 패널 "편집" 진입 — 종전엔 등록 후 label/설명은 빈칸만 채울 수 있고 재작성 불가.
223
+ - **room 줄 내용 전개**(Hall 페이지 room 줄과 동일): 기본 접힘, ▸로 펼치면 항목.
224
+ - **기억 페이지 Room 블록 분리**: slug chips/페이지네이션/전개 블록 제거(Hall/Room 페이지와 중복), "미바인딩 · N" 고아 필터와 안내 문구만 유지 — room 태그 열람/관리는 Hall/Room 페이지로.
225
+
226
+ ### 변경
227
+
228
+ - 병합/분류/개명 폼과 미리보기 sticky 스크롤 추종(hall이 많을 때 확인창을 찾느라 위아래로 밀 필요가 없다).
229
+ - 포인터 자체 드래그(ego-browser과 같은 pointer 방식, 일부 webview에서 HTML5 DnD가 안 터지는 데 대한 폴백).
230
+
231
+ ## [0.20.0-beta.19] — 2026-10-02
232
+
233
+ > 분류 상호작용 재작업(실측 피드백: HTML5 드래그가 일부 webview에서 안 터짐; hall 클릭 후 미리보기가 원본 줄에 묻혀 "반응 없음"처럼 보임).
234
+
235
+ ### 변경
236
+
237
+ - **포인터 자체 드래그**(ego-browser과 같은 순수 pointer 방식): room/자체 성장 slug를 누른 채 → 유령 chip이 손을 따라 부유 → hall 헤더에 호버하면 점선 강조 → 손을 떼면 분류. HTML5 DnD에 의존하지 않아 어떤 webview에서도 발동. 클릭 지정 분류("분류" / "담기" → 지정 상태 → hall 헤더 클릭)는 유지.
238
+ - **전역 조작 바**: 병합/분류/개명의 폼과 dryRun 미리보기가 **페이지 최상단에 상주**(빨간 확인창은 줄과 함께 스크롤되지 않고, 더 이상 원본 room 줄에 묻히지 않음) — hall을 클릭하면 반드시 보인다.
239
+ - **room 줄 레이아웃**: 버튼을 한 줄에 고정해 더 이상 줄바꿈하지 않음(내보내기가 외톨이 줄 안 됨); 이름/별칭은 왼쪽 정보 영역에서 자연 줄바꿈(별칭은 단어 끊김 허용).
240
+
241
+ ## [0.20.0-beta.18] — 2026-10-02
242
+
243
+ ### 유지 보수
244
+
245
+ - lint 정리: beta.17의 isTag→isRoomSlug 치환이 남긴 stats.ts / rooms-registry.ts 두 개의 죽은 import(출시 게이트 미검 — 파이프 후 `$?`가 tail의 종료 코드를 취함, D2 함정 재발). 기능 변경 없음.
246
+
247
+ ## [0.20.0-beta.17] — 2026-10-02
248
+
249
+ > **재시작 후 hall/room 관계 소실 수정**: `rooms-registry.json`의 로더가 여전히 `isTag`로 slug를 필터링 — 재시작 후 디스크에서 읽을 때 모든 `major/minor` 2단계 항목이 불법으로 **조용히 버려진다**(단독 hall은 통과해 남았다). 이는 isTag/isRoomSlug 혼용의 세 번째 장소; 같은 배치에서 `normTags`의 같은 지뢰도 걷어냈다.
250
+
251
+ ### 수정
252
+
253
+ - **로더가 2단계 항목을 버림**(`store/rooms-registry.ts` init): 필터를 `isRoomSlug`로 변경. **당신의 디스크 위 rooms-registry.json 데이터는 온전하다**(2단계 항목은 줄곧 파일에 있었고, 단지 부팅 시 버려진 것) — 업그레이드 후 재로딩하면 관계가 자동 복원, 어떤 복구 조작도 불요.
254
+ - **normTags가 slash 태그를 씻어냄**(`metadata-validators.ts`): "모든 tags 재작성 지점은 반드시 여기를 통과" 게이트가 isTag를 썼는데, 분류 재작성 시 넣은 `hall/minor` 태그가 다음 증류/재라벨에 닿을 때 조용히 깎였다. 이제 2단계 room slug를 통과(3단계는 여전히 거부); `isRoomSlug`의 사실 소스를 가까운 해당 파일로 옮김(의존 순환 회피), rooms-registry의 re-export는 호환 유지.
255
+ - **list-records의 2단계 tag 필터 오거부**(stats.ts tagSel와 같은 지뢰): `isRoomSlug`로 변경 — Hall 페이지에서 2단계 room을 눌러 기록 필터링하면 이전엔 400을 반환했다.
256
+ - 빨간 테스트 선행 ×3(영속 루프 2단계 보존 / normTags 통과 / 통합 2단계 tag 필터), 빨강을 확인한 뒤 고쳐 초록 복귀.
257
+
258
+ ## [0.20.0-beta.16] — 2026-10-02
259
+
260
+ > **자체 성장 미등록 slug를 곧장 hall에 편입**: 하단 "자체 성장 미등록" 영역의 각 chip을 이제 hall 헤더로 끌어다 놓을 수 있다 — 편입 + 그 하위류 지정이 한 단계로 끝난다.
261
+
262
+ ### 새로 추가
263
+
264
+ - **grown chip 드래그/클릭 분류**: 미등록 slug(예 `git-commits`)를 hall 헤더로 끌거나(chip의 "담기" 버튼을 눌러 지정 상태로 들어간 뒤 대상 hall 클릭) → dryRun 미리보기 → 확인 실달. 의미 = **제자리 편입(from, source=grown) → `hall/하위류`로 rename**: 기록 tags는 따라 이동, 옛 이름은 대상 별칭에 들어가고, 항목은 active이며 ⭐를 달지 않는다(편입 신원). 자기 hall의 2단계 slug로 되떨어짐(예 `dsh-plugin/xxx`)= 제자리 편입.
265
+ - 조합 순서를 통합 테스트에 박았다(markMered는 미등록 from에서 조기에 빠짐 — 반드시 먼저 from을 편입한 뒤 rename); execMerge는 미등록 from에 자동 편입 보완.
266
+
267
+ ## [0.20.0-beta.15] — 2026-10-02
268
+
269
+ > 분류 실측 피드백 2연속 수정: ①개명 후 항목이 잘못 "퇴역"을 달음(주석 어휘표에서 빠진다); ②HTML5 드래그가 일부 webview에서 안 먹힘 → **클릭 지정 분류**를 주 경로로 신설.
270
+
271
+ ### 수정
272
+
273
+ - **분류(개명) 후 항목에 retired 오표**: renameRoom이 merge 편성을 재사용하면서 `markMerged`의 "옛 항목을 퇴역으로 전환"까지 딸려 왔다 — 분류 끝난 새 room이 "퇴역"을 표시하고 **주석 어휘표에서 빠진다**(listActive 필터). 이제 rename 마무리에서 직전 상태를 복원(active 항목은 개명 후에도 active); 진짜 merge(합집합 의미)는 영향 없음. 빨간 테스트 선행(unit + HTTP 면 통합).
274
+
275
+ ### 변경
276
+
277
+ - **클릭 지정 분류를 주 경로로**: room 줄의 "분류" 클릭 → 지정 상태 진입(버튼이 "hall 선택…"으로 변하고 모든 hall 헤더 점선 강조) → 대상 hall 헤더 클릭 → dryRun 미리보기 → 확인 실달. 드래그는 유지(지원되는 환경에서는 여전히 쓸 수 있음), 다만 의존하지 않는다.
278
+
279
+ ## [0.20.0-beta.14] — 2026-10-02
280
+
281
+ > 기억 카드에 **노화 가중치**와 **호출 사용** 표시를 신설, 그리고 "노화 가중치 효과 통계" 데이터 축적을 시작. 기준(사용자 판정): **topN에 들어가야(컨텍스트에 주입되어야) 비로소 사용된 것**; 시도했으나 안 들어감 = 시도만 셈.
282
+
283
+ ### 새로 추가
284
+
285
+ - **카드 필드**: "노화 N%" chip(읽기 경로 감쇠 계수 `max(0.5, 0.5^(Δ일/반감기))`, 호출 정렬과 같은 식 같은 소스; ≤60%는 빨강=늙음)+ "used/attempts" 계수(title에 최근 사용/시도 시각 포함). 도구줄에 집계 "호출 사용 used/attempted(rate%)" 신설.
286
+ - **통계 면**: `RecallUsageStore`(recall-usage.json 사이드카, rooms-registry와 같은 가문 규율 — 손상 시 읽기 강등, 절대 호출 읽기 경로를 막지 않음); 매 라운드 호출을 주입/전부 억제/예산 0의 세 종착 상태에서 기록(후보 attempts++, 주입 used++, 타임스탬프는 최신); 메모리 즉시 + 디바운스 5s 원자 영속, 5만 건 초과 시 최근 시도 기준으로 폐기; 미장착 = 무비용 우회.
287
+ - 계약: UiRecord에 `agingWeight`/`usage` 추가, ListRecordsResponse에 `usageSummary` 추가(선택 필드, 옛 클라이언트는 파괴 없음); 계약 키 수는 불변.
288
+
289
+ ### 효과 통계 기준(후속 분석용)
290
+ - usageSummary: tracked(통계 있는 기억 수)/ attempted(Σ시도)/ used(Σ사용)/ rate(사용률%)
291
+ - 세밀 분석(유형/연령대별 사용 분포, 반감기 튜닝 근거) 데이터는 이 버전부터 축적, 분석 뷰는 별도 논의.
292
+
293
+ ## [0.20.0-beta.13] — 2026-10-02
294
+
295
+ > 분류 논리 보강: room을 기존 hall 아래에 넣기(종전엔 새 hall만 만들거나 다른 room에 병합해 신원을 지울 수 있었다). 패널은 **드래그 분류**를 지원 — room을 끌어 hall 헤더에 놓으면 된다.
296
+
297
+ ### 새로 추가
298
+
299
+ - **Hall 페이지 드래그 분류**: room 줄을 끌 수(hiPriv 켜면 손잡이 커서), hall 헤더가 놓기 대상(점선 강조); 손을 떼면 = 미리 채워진 개명(major/minor) + 곧장 dryRun 미리보기, 확인 후 실달 — 기록은 따라 이동, 옛 이름은 별칭, 그룹 뷰에서는 곧 그 hall 아래로 걸림. 터치스크린/드래그 미지원 환경은 room 줄의 "분류" 버튼이나 개명 폼의 hall 빠른 선택 chips로 폴백.
300
+ - 백엔드 수정: `renameSlug` 대상 검증 `isTag` → `isRoomSlug` — 종전엔 2단계 대상(major/minor)이 오거부되어 "hall 아래로 분류" 길이 사실상 통하지 않았고(또 rename 편성이 기록을 재작성한 뒤 등록부 개명 시 예외를 던져 반쪽 상태를 남김). C3 제거/변경 규율대로: 빨간 테스트 선행(unit + HTTP 면 통합 각 하나), 빨강 확인 후 고쳐 초록 복귀.
301
+
302
+ ### 수정
303
+
304
+ - 상동: `renameSlug` 2단계 대상 검증(beta.5에 들어간 결손).
305
+
306
+ ## [0.20.0-beta.12] — 2026-10-02
307
+
308
+ > lint 정리 + 제거 규율 성문(ENGINEERING-NOTES에 C3 신설). **기능 변경 없음**.
309
+
310
+ ### 유지 보수
311
+
312
+ - **eslint 정리(13 error → 0)**: longtask 제거 잔재(stats.ts 죽은 import ×2, RecordsTab 죽은 state)+ 더 이전 배치의 부채(recluster.ts 죽은 인터페이스와 let, rooms-registry.ts / ruminate.ts / room-review.ts / 테스트 파일 두 개의 죽은 import와 let). 남은 warning 4개는 기저선 기존(primitives/tools의 any).
313
+ - **ENGINEERING-NOTES에 C3 "제거도 TDD"**: 제거는 먼저 테스트로 새 기대를 표현(빨강) → 구현 제거(초록); lint는 삭제형 변경에서 반드시 돌려야 하는 문(unused import = 덜 제거된 것의 자동 탐지기).
314
+
315
+ ## [0.20.0-beta.11] — 2026-10-02
316
+
317
+ > Hall·Room **통합 테스트** 도입(`tests/hall-room.integration.test.ts`: HTTP 면 전 연쇄 × 실제 저장소의 사용자 여정, 9 케이스), 통합 첫 실행에서 단위 테스트 사각지대 하나를 잡아 고쳤다.
318
+
319
+ ### 수정
320
+
321
+ - **rooms-get 병합 뷰의 source를 `pre-registered`로 하드코딩**(beta.4 도입): 등록부 항목을 계수 목록에 합칠 때 출처를 고정해 썼는데, 편입(source=grown)된 항목이 패널 계수 chips에서 항시 ⭐ 사전등록 배지를 달았다 — 신원 왜곡. 이제 항목의 실제 출처를 씀(⭐는 사전등록에만; 편입은 grown 표시). 단위 테스트 사각지대 원인: rooms-registry 단위 테스트는 store 층만 해서, 단말 병합 줄이 grown 항목의 source를 주장하는 자가 없었다.
322
+
323
+ ### 테스트
324
+
325
+ - `hall-room.integration.test.ts`: 빈 표 자체 성장 계수 → 2단계 등록/불법 slug 거부 → 편입 → 세 kind 내보내기 → merge dryRun 영쓰기→실달(백업 영속/퇴장 행 재작성/metadata 보존/장면 재계산 큐잉 source='room-merge')→ rename 별칭 → retire/복원 → 대분류 200 상한+소분류 미점액 → 손상 읽기 강등 빨간선(rooms-get은 막지 않음, 디스크에 역작성 안 함).
326
+
327
+ ## [0.20.0-beta.10] — 2026-10-02
328
+
329
+ > **longtask(장기 작업 모드) 전면 제거**: 이 기능군의 소속은 본래 context-compression 쪽("압축 시작 제안"이 그 영역), pm에 잘못 실린 것 — beta.8에서 플로팅 공을 제거했고, 이번 버전에서 남은 백엔드를 전부 제거. pm은 순수 기억 영역으로 복귀. 장래에 "꼬리 즉시 증류" 같은 능력이 필요하면 pm 본위로 재설계해 별도 과제로 착수한다.
330
+
331
+ ### 제거
332
+
333
+ - **엔드포인트 `longtask-hint-get` / `longtask-compress-tail`**(계약 키 48 → 46)및 `compressTailTurns`/디바운스 반추 폐루프: 컨텍스트 계량(`ContextUsageStore`), todo 표류 감지(`TodoRefStore`), 제안 판정이 함께 제거, `hooks/long-task.ts`, `store/context-usage.ts`, `store/todo-ref.ts` 세 파일 삭제(todo/write 이벤트의 declare module 확장 포함).
334
+ - **`cfg.longTask.*` 설정 블록**(기본값 + Schema 두 곳): 옛 설정 파일의 longTask 키는 schema가 무시, 마이그레이션 불요.
335
+ - **세션급 `longTask` 깃발과 꼬리 압축 수위선 `lastTailCompressedTurn`**(session-modes): 옛 session-modes.json의 잔여 키는 로드 필터가 자연히 무시.
336
+ - **`session-mode-set`의 `mode`를 필수로 되돌림**(그 선택화 가능은 longTask 2택1을 위해 태어남); `longTask` 요청/응답 필드 삭제.
337
+ - 보존: beta.9의 roomRegistry RPC 배선 수정(독립 성립); Hall 관리 페이지와 Room 분류 관리 면은 불변.
338
+
339
+ ## [0.20.0-beta.9] — 2026-10-02
340
+
341
+ > 패널 RPC 전 연쇄의 deps 누락 수정:`roomRegistry`와 `longTaskDeps`가 rpcHandler 조립 때 sources에서 빠져, 패널 쪽 Room 관리/장기 작업 엔드포인트가 항시 "미장착"(도구 면은 직전달이라 영향 없음, 그래서 beta.4부터 도구는 쓸 수 있으나 패널 등록은 계속 오류).
342
+
343
+ ### 수정
344
+
345
+ - **`registerMemoryRpc`의 rpcHandler가 `roomRegistry`/`longTask`를 빠뜨림**(beta.4 도입): HTTP 분배는 `buildEndpointDeps`로 통일되지만 sources는 7가지만 담았다 — `rooms-get`은 registry를 안 내림, `room-register`/`room-admin`은 "Room 등록부 미장착(플러그인 재초기화 필요)"를 던짐, `longtask-hint-get`/`longtask-compress-tail`은 항시 disabled. 이제 sources와 함께 내림; 회귀 테스트가 HTTP 면 전 연쇄를 고정(register → rooms-get 병합 → room-admin, rpc.test.ts).
346
+
347
+ ## [0.20.0-beta.8] — 2026-10-02
348
+
349
+ > 플로팅 공 UI 제거: 부유 제안 패널의 소속은 본래 context-compression 쪽("압축 시작 제안"이 그 영역), pm에 잘못 실린 것 — beta.1부터 패키지와 함께 배포된 FAB를 이번 버전에서 제거.
350
+
351
+ ### 제거
352
+
353
+ - **플로팅 공 + 제안 카드(`client/src/longtask-fab.ts`, beta.1부터 패키지와 함께)**: 통째 제거, 클라이언트는 두 좌석으로 복귀 — `settings.section`(기억 패널)+ `conversation.input.left`(단계 pill), 더 이상 앱 전역 플로팅 UI 없음. 장기 작업 모드 본체(컨텍스트 계량/todo 참조/꼬리 turn 압축)는 영향 없음; `longtask-hint-get`/`longtask-compress-tail` 엔드포인트는 백엔드 면으로 보존, 장기 작업 스위치와 수동 압축 진입은 정식화 때 기억 설정 분절에 착지.
354
+
355
+ ## [0.20.0-beta.7] — 2026-10-02
356
+
357
+ > Room 분류 관리 세 번째 배(**관리 패널 면**): 전용 "Hall" 탭, hall(대분류)+ room(소분류)을 함께 관리 — 수동으로 Room을 보거나 관리할 때 agent 도구를 빌리지 않아도 된다.
358
+
359
+ ### 새로 추가
360
+
361
+ - **"Hall" 관리 탭**("장면" 뒤): hall 그룹 트리 — `major/minor`는 접두 대분류로, standalone slug는 자체 hall(자기 자신이 hall이자 그 아래 유일한 room); hall 헤더 줄에 집계 계수 + 일괄 "+소분류"(미리 채움 `hall/`); room 줄은 ⭐사전등록/source/상태/별칭/계수를 표시, retired는 회색화. UI 용어 Hall/Room은 그룹 뷰로서, Wing(`metadata.hall` 영역) 및 인지 hall과는 무관(ADR-0020 §7 중의 해소).
362
+ - **전 조작 UI 진입(고권한 memoryMutate 문)**: 2단계 slug 등록(제한 폼, placeholder `dsh-plugin` / `dsh-plugin/merge`); 자체 성장 slug **편입**(`source=grown`); **개명/병합**은 새 RPC로 — dryRun 미리보기(영향 건수+샘플) → "확인 실행" 실달, memory_room_admin 도구와 같은 편성(실행 전 자동 백업 + 완료 시 장면 재계산 큐잉); 퇴역/복원 한 번 클릭(복원은 2차 확인 없음, 퇴역은 확인); room별 기록 목록 CSV 내보내기; 어휘표/고아 내보내기 이 페이지로 수렴; 병합/개명에 "반추 유휴 시 실행" 안내.
363
+ - **신규 엔드포인트 `dsh-memory/room-admin`**(merge/rename/retire; memoryMutate 문, 단말 측 예외 의미): 계약 키 47 → 48. `rooms-export`에 `kind='records'` 추가(Room별 기록 목록 내보내기, `tag` 필수); `room-register`에 선택 `source` 추가(편입 시 `grown`).
364
+ - 기억 Tab Room 블록 축소: 관리 하위 패널 제거(고아 내보내기는 헤더 줄로 위로 이동), 안내는 Hall 페이지를 가리킴.
365
+
366
+ ### 수정
367
+
368
+ - `rooms-export` records 분기 kind 타입 축소(TS2322)와 과거 문자 깨짐 오류 메시지; CHANGELOG beta.1-beta.5 중복 제목 줄 수정.
369
+
370
+ ## [0.20.0-beta.6] — 2026-10-01
371
+
372
+ ### 수정
373
+
374
+ - **주석 어휘표가 등록부에 접속(beta.5 누락 보강)**: beta.5의 merge/rename/retire는 이미 착지했지만, 후보 주석기(`annotateOrphanCandidates`)의 어휘표는 여전히 순수 자체 성장 집계 — 사전등록 Room은 후보에 전혀 못 들어갔다. 이제 OrphanAnnotateIO가 registry를 담는다: 병합 어휘표 = 등록부 항목이 앞(count=0, label/description 첨부)+ 자체 성장 디렉터리, 상한 120; ruminate와 memory_room_admin annotate 두 경로 모두 인자 전달. **사전등록 Room이 이때부터 후보 주석에 실제 영향을 준다**.
375
+
376
+ ## [0.20.0-beta.5] — 2026-10-01
377
+
378
+ > Room 분류 관리 두 번째 배(**파괴적 면**). dryRun 기본 true — merge/rename은 먼저 영향면을 미리보기, 확인 후 실달; 실행 전 `rooms-merge-backups/`에 자동 백업.
379
+
380
+ ### 새로 추가
381
+
382
+ - **Room 등록부 2단계제**: slug가 `major` 또는 `major/minor` 2단계를 지원(예 `dsh-plugin`, `dsh-plugin/merge`); **대분류 상한 200**, 소분류 세분화는 대분류 점액을 쓰지 않음(총량 안전장치 2000). isTag 어휘표는 완화하지 않음(slash는 등록부 층에서만 허용).
383
+ - **merge/rename/retire(도구 `memory_room_admin`, 고권한)**: merge는 커서로 모든 매칭 기록의 tags를 재작성(쓰기 전 재읽기-병합-재작성, roomCandidates/roomReview/hall/anchors 등 키 보존; 이미 퇴장한 행 포함, 계수 기준과 일치); rename = merge 1:1 + 등록부 개명(aliases가 옛 이름을 수용); retire는 어휘표 퇴역만(기존 수량 불변). merge/rename과 반추를 병행하면 서로 덮어쓴다 — 실행 시각을 어긋나게 하라(도구 안내와 ADR-0020 모두 선언함).
384
+ - **재작성 안전 3종 세트**: dryRun은 기본 affected+미리보기만 반환하고 영쓰기; 실행 전 매칭 기록을 `rooms-merge-backups/<ts>-<slug>.json`에 자동 백업; 완료 시 자동 `invalidateRooms` + 영향받은 (family, scene)별로 장면 재계산 큐잉(`source='room-merge'`).
385
+ - **패널 Room 관리 하위 패널**(고권한 켜면 등장): 등록 폼(slug/이름/설명, 새 RPC `room-register` 거침), 고아 목록 내보내기; 병합/퇴역 제안은 agent 도구로(dryRun 미보기가 더 상세).
386
+ - 단말 면 46 → 47(`room-register`).
387
+
388
+ ## [0.20.0-beta.4] — 2026-10-01
389
+
390
+ > Room 분류 관리 첫 번째 배(**영표류 면**): 등록부가 비면 beta.3과 동작이 글자 그대로 일치. merge/rename/retire 파괴적 면은 beta.5에 따른다(dryRun 기본 true는 한 버전 보존).
391
+
392
+ ### 새로 추가
393
+
394
+ - **Room 등록부(`rooms-registry.json` 사이드카)**: 미리 기획된 Room을 사전 등록(slug/label/description); 손상/비면 → 순수 자체 성장 디렉터리로 강등 회피, 절대 패널과 주석기를 막지 않음; 상한 200으로 도배 방지. `rooms-get`은 단말 층에서 병합: 등록 항목은 count=0이어도 어휘표에 들고 `source`/`label`을 달고, 응답에 `registry` 전량 필드 신설(클라이언트는 선택 읽기).
395
+ - **내보내기 엔드포인트 `dsh-memory/rooms-export`**(kind=rooms|orphans, CSV 직출): rooms = 등록부∪자체 성장 어휘표에 계수 첨부; orphans = Room 바인딩 없는 활성 기록 목록(후보/재검 상태 열 포함).
396
+ - **도구 `memory_room_admin`**(action=list|register): list는 등록부+자체 성장 총람; register는 Room 사전 등록(고권한 문, 멱등 보완) — `memory_room_review`의 건별 재검과 조합하면, agent에게 "등록→사전 마킹→건별 확인"의 완결 분류 관리 진입이 생긴다.
397
+ - 클라이언트: Room chip에 "사전등록/자체 성장" 출처 표식; 고아 chip에 재검 대기 의미.
398
+
399
+ ## [0.20.0-beta.3] — 2026-10-01
400
+
401
+ ### 새로 추가
402
+
403
+ - **기록 패널 Room 분류 개선**: ①펼치기/접기(접기 = 헤더 + 앞 8개 고빈도 Room 미리보기, 개폐 상태 영속); ②펼침 상태 전량 페이지네이션(매 페이지 40, ◀▶ 넘김), 종전 "앞 40만 표시"의 경직 절단을 대체; ③**고아 기억 필터** — 선두 "미바인딩 · N" chip으로 Room 바인딩이 전혀 없는 기억만 봄(`list-records`에 `untagged` 필터 경로 신설, `rooms-get`에 `orphanCount` 신설, Room 계수와 같은 기준); ④필터 조건 즉시 가시 + 일괄 해제. 검색 경로에도 같은 고아 필터.
404
+
405
+ ## [0.20.0-beta.2] — 2026-10-01
406
+
407
+ > dsh-0.2.0-beta 채널 +1: 반추 완전 context 최적화(repo 소프트 울타리/장면 프로젝트별 이전/폐루프). longtask(beta.1 참조)와 같은 패키지, 실머신 피드백 병합 수렴.
408
+
409
+ ### 새로 추가
410
+
411
+ - **repo 소프트 울타리(ADR-0015, `recall.scopeFence.*`, 기본 끔)**: repoKey=basename(정규화한 cwd)을 쓸 때 계산해 L1 metadata(repo_key_name/repo_key_owner/applicability, DEFAULT는 백필 불요, FTS와 스냅샷 해시에 안 들어감)에 기록; 호출과 memory_search 출구에서 `this-repo`/`cross-project`로 소프트 감중 재정렬(×0.2 조정, **감중만 하고 배제하지 않음**, chat류와 미귀속 기록은 울타리 밖, 중복제거 경로는 센티널로 오입 방지). 영표류: 기본 끔일 때 호출 순서 sha1 대조가 일치.
412
+ - **장면 프로젝트별 이전(L2 repo 감지 + 재클러스터)**: L2 통합 prompt에 repoKeyName/applicability를 보이게 해 — this-repo 기록은 더 이상 프로젝트 횡단 장면에 안 들어감; `scene_recluster_jobs` 작업 큐 + ruminate 유휴 단계 소비자(스냅샷 → 요약 투영 삭제 → 실패 시 통째 롤백, 새 LLM 채널 0); repo 귀속 보강 후에는 repoKey로 역조회해 영향 장면을 큐잉 재계산.
413
+ - **압축 폐루프**: 꼬리 압축 큐잉 성공 후 10분 디바운스로 반추 한 번 촉발(플러싱→L2→L3→relabel→재클러스터 소비), "압축 기록이 증류 버킷에 고인다"의 끊김을 수정.
414
+
415
+ ## [0.20.0-beta.1] — 2026-10-01
416
+
417
+ > dsh-0.2.0 라인의 첫 beta 채널 출시(`dsh-0.2.0-beta` tag): 장기 작업 모드 실머신 프리뷰. 아래 내용 외에도, 이번 버전은 종전 `[Unreleased]`에 parked된 모든 main 변경(활성 슬롯/출처 앵커/증거 리더/기억 퇴장 폐루프/3축 충돌 등)을 함께 담는다 — 이미 0.1.7 라인으로 출시됐지만 main 라인에는 대응 버전 번호가 아직 없다.
418
+
419
+ ### 새로 추가
420
+
421
+ - **장기 작업 모드(`longTask.*`, 기본 끔) — "장시간 세션"의 3종 세트: 컨텍스트 계량, 작업 참조, 꼬리 압축, 그리고 비차단 플로팅 제안 패널 하나.**
422
+ - **컨텍스트 사용량(`store/context-usage.ts`)**: `assistant/message` 공식 `usage`의 입력 측(input + cache 읽기/쓰기)를 감시, **최신값** 의미를 취함(매 라운드 입력 측에 이미 전체 이력이 들어 있으니 누적하지 않음); `compaction/summary` 후 0으로 재계. 순수 메모리 per-session Map — 재시작 후 첫 assistant 메시지로 재건 가능, 영속화하면 만료 샘플만 되살린다.
423
+ - **todo 참조(`store/todo-ref.ts` + `hooks/long-task.ts`)**: 호스트 구조화 `todo/write` 이벤트(전량 스냅샷; 패키지 타입이 0.2.0-rc.1에서 미선언, 플러그인이 declare module로 자체 확장 — §E의 dsh-compaction 선례와 같음)를 감시. **장기 작업 세션에만**: 스냅샷을 pinned `todo` 슬롯 하나에 밀어 넣는다(`SlotStore.refreshByTitle` 관리 갱신 — 같은 주체가 반복 쓰려면 제자리에 갱신해야, ≤8 슬롯 상한 안에 닫힌 슬롯 잔해가 쌓임), slot-recall로 상주 주입 — 압축이 옛 컨텍스트를 밀어낸 뒤에도 작업 북극성은 남는다. 인접 스냅샷간에는 표면 Jaccard 표류 감지(같은 스냅샷 반복 쓰기는 신호를 희석하지 않음); body ≤512 바이트, 절단 시 명시.
424
+ - **꼬리 turn 압축(`longtask-compress-tail` 엔드포인트 + `compressTailTurns`)**: (압축 수위선, 최신 캡처 라운드] 구간의 최근 N 라운드를 즉시 `enqueue(force:true)`로 증류해 기억에 넣는다 — 증류 임계치를 기다리지 않고 꼬리 대화를 곧장 검색 가능 기억으로. 수위선(`SessionModeStore.lastTailCompressedTurn`)은 앞쪽으로만 전진, 반복 누름은 자연 멱등; off 등급은 완전히 투명; 앵커 없는 옛 데이터는 첫 압축(수위선 0)에만 포함 — 덜 거두되 중복 증류는 안 함.
425
+ - **제안 상태(`longtask-hint-get`)+ 플로팅 공(`client/src/longtask-fab.ts`)**: ego-browser식 vanilla overlay(FAB+패널, 드래그/위치 영속/뷰포트 클램핑), **제안은 차단하지 않음** — "컨텍스트 점유가 `contextThresholdPct`에 도달" 또는 "todo 표류가 `driftThreshold`에 도달"하고 장기 작업이 켜지지 않을 때만 숨쉬는 글로우를 점등; 패널 내: 장기 작업 스위치(임시, per-session, **mode 없음** — 스위치 전환이 사용자 단계를 리셋하지 않음), 점유/표류 진행바, 수동 꼬리 압축 버튼(장기 작업 켜야만 사용 가능), 작업 계수. 폴링 바쁠 때 2s / 한가할 때 5s, RPC 실패는 침묵; 시트 계약 불변(settings.section은 정확히 하나, body 싱글턴은 어떤 시트도 차지 않음).
426
+ - **`session-mode-set`의 `mode`가 선택으로 전환**: `longTask`와 2택1 필수 — 순수 장기 작업 스위치 전환만 하면 더 이상 단계를 강제로 안 보낸다(옛 클라이언트는 항시 mode를 전송, 동작 불변). 응답 `mode`는 현재 유효값으로 되돌림.
427
+
12
428
  ## [0.19.0] — 2026-09-29
13
429
 
14
430
  ### 변경
package/CHANGELOG.md CHANGED
@@ -17,6 +17,126 @@
17
17
  - **建议状态(`longtask-hint-get`)以纯端点提供**:判定 = 上下文占用达 `contextThresholdPct` 或 todo 漂移达 `driftThreshold`(部署配置可调);配合 `longtask-compress-tail` 作手动压缩入口。
18
18
  - **撤下浮动球 UI(`client/src/longtask-fab.ts`;beta.1 起曾随包发布)**:悬浮建议面板的归属本在 context-compression 侧(「建议启动压缩」是其领域),pm 属误载——2026-10-02 用户裁定撤下。撤下不违座位契约(settings.section 恰一个 + 输入栏 pill,body 单例不占座位),但全应用级 overlay 会与其他插件的悬浮 UI 抢占屏幕角落;长任务开关与手动压缩入口待长任务定版时落记忆设置分节。
19
19
  - **`session-mode-set` 的 `mode` 转为可选**:与 `longTask` 二选一必传——纯切长任务开关不再被迫发送档位(旧客户端恒传 mode,行为不变)。响应 `mode` 改回当前生效值。
20
+ ## [0.24.1] — 2026-10-14
21
+
22
+ ### 新增
23
+
24
+ - **投射后缀不变式收敛(`convergeDisabledSuffix`)**:`.memory.md` / `.memory.md.disable` 只由「这条记录在库里是否退场」决定,与 `name` 无关、与「是哪条路径把它退场的」也无关。逐条按「文件存在性 + 当前后缀 + 指纹一致性」三项检查对齐;人手造的 `.disable` 是文件侧退场凭证(P2 关着时库里不知道),只报 `both` 不动盘。
25
+ - **库→文件挂钩收口到 `L1Store`(`store/memory-md-sync.ts`)**:`appendNew` / `retire` / `restore` / `rename` 四个汇流口注入 `setFileSyncHook`,取代此前散布在 8 个调用点的事件挂钩——真机 920 条退场记录只有 8 个 `.disable`,全部来自召回面板那一条路径。机器侧自动退场(合并取代、冲突判负)与模型 `memory_add` 从此自动跟随,新增调用点不会漏。
26
+ - **改名跟随**:`rename()` 之后投射文件搬到新名(`.disable` 兄弟一起走)、账本跟着换键;旧路径本就没这份时按新名补写,目标已被占用时只报不覆盖。
27
+ - **投影长任务滚动执行 + 进度条(`store/memory-md-run.ts`、新端点 `dsh-memory/mem-run-status`)**:一次点击自己把导出游标推到表尾、再把后缀收敛游标推到表尾(跨轮另有 `maxPasses` / `totalBudgetMs` 两道总闸),滚不完如实写进 `notice` 而不是停在半路;面板复用反刍/重建那根进度条。
28
+
29
+ ### 变更
30
+
31
+ - **单次文件更新预算**:`memExport.maxRecords` 默认 300 → **2000**,并新增运行时旋钮 `live.mdExportMaxRecords`(0 = 跟随部署默认,改完不必重启)。面板上名不副实的「单次动作护栏」改成 **「单次文件更新预算」**,单位说成「个文件/次」,结果行回显取自服务端**实际生效**的 `appliedMaxRecords`。
32
+
33
+ ### 修复
34
+
35
+ - **护栏数的是「遍历」而不是「碰盘动作」**:判为「未变」的条也吃配额,第二次点击扫的还是同一批前 N 条——装机量级 1210 条意味着回填要点 4 次。
36
+ - **批量落盘 O(n²)**:收敛器原本每条改名都 `ledger.flush()`(账本是整本 JSON 的原子重写),300 条 = 300 次整本重写,被墙钟 `timeBudgetMs` 拦成好几次点击。现统一「循环里只记脏、收尾落一次」。
37
+ - **紧邻两次点火互踩**:投射联动改为**后台串行**。旧写法每次点火自己开一个 promise,于是「先 `appendNew` 再 `retire`」会重叠——第二个任务的收敛器在第一个还没写出文件时判「盘上没这份」(`absent`),退场记录就永久停在 `.memory.md` 上,直到下次全量导出才被抬回来。
38
+ - **恢复活跃后永久双份**:有效记录的「两份都在」格子原先只报账不动盘。现只在「账本同 id + `.disable` 自写下后未被手改 + `.memory.md` 那份正是当前渲染」三条同时成立时删掉那份陈年凭证,任一不成立仍交人裁决。
39
+
40
+ ## [0.24.0] — 未发布(本地线 `0.24.0-local.0…3`,2026-10-13)
41
+
42
+ ### 新增
43
+
44
+ - **类型词表单一真源(`src/memory-types.ts`)**:宿主记忆分类 29 类原名 + 插件旧 7 类 + `unclassified` 兜底,共 37 类。投射目录白名单、`memory_add` 写入白名单、面板标签、抽取与去重 prompt 的枚举串、memport 的 TS/Python 双实现类型码**全部由它派生**。此前「类型全集」手抄在四处各自演化:`memory_add` 把外部分类**静默改写**成 `episodic`,而 memport 文案承诺「按原样透传」——同一件事两套口径。
45
+ - **名称蒸馏(`cfg.naming`)**:抽取当场产出 `title`,与 `content` **分离定义**(title 是回答「这条讲的是什么」的名词短语,content 是完整叙述正文);去重 merge 一并产出 `merged_title`。落库时 `title` 只是**名字候选**,必须过 `validateTitle` 形状闸,不合格仍交给起名器——名字唯一性的真源不因多一个来源就分两套判据。
46
+ - **few-shot 标准格式示例集(`src/prompts/examples.ts`)**:分类型止例 + 反例,总长有预算上限(示例是每轮蒸馏都付的固定成本);止例自身必须过闸门且骨架齐备,用例直接吃 `auditFewShot()` 而不是手抄判据。
47
+ - **骨架判据可执行化(`SKELETON_ELEMENTS` + `scoreSkeleton`)**:把「没有前因后果、写不全就不算可用」从提示词里的愿望变成能测、能用来定默认值的判据。声明局限:关键词启发式,只能用于同输入两臂相对比较,不是绝对质量分。
48
+ - **目录骨架 + 每类 `_index.md`(`memory-md-skeleton.ts`)**:每个类型目录放一个索引文件(类名 + 库里条数)。空目录会被 git 与压缩包吐掉,而「没这个目录」与「这类 0 条」在人眼里长得一样。幂等:内容不变零写入不刷 mtime;投射面关着时零动作。
49
+
50
+ ### 变更
51
+
52
+ - **三处默认值按 A/B 实测翻开**(`narrativeTemplates.enabled` / `naming.enabled` / `naming.fewShot`)。依据是两条**方向相反**的实测:真会话蒸馏路径无示例时骨架齐备 **0.000**(0/23)、有示例 **0.703**(26/37);而存量改名的命名任务加了示例反而 **−0.113**(漏答 18/160)。结论:**示例不能跨任务用**,它只进抽取提示、不进命名脚本。
53
+ - **md 投射默认开**(`memExport.enabled`):整个计划的前提是「文件为内容权威 + 库为索引」,默认关等于默认不用。`mdSync.enabled` **仍默认关**——它写库,风险不对称。
54
+ - **workspace 归属配置面改名 `workspaceHints` → `workspaceSource`**:硬改名不留兼容别名(该键从未随任何发版外发,加别名等于养第二个事实源)。默认开;路由 `path` 改存 **workspace 根**(旧值写成 `E:\test\rewrite-agently\.agents`,而 `targetFor` 会再拼 `.agents/memory` → 得到 `.agents\.agents\memory\` 孤儿树,「根不存在才退回单根」的护栏拦不住这种形状)。真机存量回填 **724/1166** 条拿到归属,`hashRecords` 逐字不变。
55
+
56
+ ### 修复
57
+
58
+ - **账本加载器把绝对键覆写进 `relPath`**:N10 之后账本键是绝对路径,而加载时那句「不一致就以键为准重写」每次加载都必然触发,实测污染 **1167/1241** 条;下游症状是孤儿归档拼出含冒号的非法目录、75 条搬家全 ENOENT。改为按 `root` + 键推导(新增 `deriveRelPath`),归档侧不再信任存下来的 `relPath`。
59
+ - **`--revert` 未兜底 hooks**:`findArchivedOld` 直接摸 `deps.hooks.exists`,而 `runCli` 调 revert 时不传 hooks(apply 侧有 `deps.hooks ?? defaultHooks`,revert 侧漏了)。沙箱第一次跑回滚就 74 条全报错。重点在危害不对称:apply 出事靠 `--revert` 退回,**回滚本身是坏的等于备份是唯一退路**。
60
+ - **跨根重复文件绕过「两边都变就不动」**:`roots = 记录落点根 ∪ 账本记过的根`,同一条记录可在两个根下各有一份文件;判定拿旧根那份、写盘走 `targetFor` 算的新根那份,于是旧根里人写的编辑把权威那份覆盖掉,而报告看起来完全合规。现只承认当前落点根,其余计入新字段 `staleRootFiles` 并显示到面板。
61
+ - **撞名把整条记忆挤出检索库**:`{ ...旧记录, id: 新 id }` 的写法会带着「已属于另一行的名字」去写库 → 撞部分唯一索引 → 批量写回退逐条仍失败 → 该条合并产物**静默消失**(旧记录已退场,这次取代什么都没留下)。起名判据由「name 为空」改为「**这个名字不属于它**」。
62
+ - **向量「待补 N 条」永不归零而「重建索引无事可做」**:展示面 `missing = total − embedded` 的分母含退场行,而退场行按设计合法地无向量;驱动补齐的两条 SQL 都排除退场行,于是同一屏同时出现「待补 114」与「无事可做」。分母口径统一为活跃行。
63
+ - **投射护栏数的是「遍历」而不是「动作」**:`maxRecords` 把判为「未变」的条也计入配额,第二次点击扫的还是同一批前 N 条——面板那句「再点一次继续」是谎话(真机症状:`已写 9 · 未变 291 · 剩余 866` 恒定不变)。改为只数碰过磁盘的条,扫描总量由 `timeBudgetMs` 兜底。
64
+ - **账本落盘 O(n²)**:`set()` 每条排一次整本 JSON 原子重写。1200 条同盘实测 **9442ms → 1954ms**(脏标记 + 合批窗口 + `flush()` 强制)。
65
+ - **`NAME_MAX`(40) 与 `NAME_CLIP_MAX`(28) 拆成两个数**:字符计数把 ASCII 与汉字同价,`dsh-agent-teams` / `Python-3.13` 一个词吃掉 15 个额度,语义上只有一两个词的正当名被判 `too-long`;而统一放 40 会让 `deriveName` 把叙述句抄成句子尾巴(实测过)。顺带修掉 `validateTitle` 里硬写的 28(同阈值两个事实源)。
66
+ - **名称闸门三处误杀**:剥引子表与拒名字表**分家**(裸主语「用户/团队」不再把「用户npm账号用户名」这类名词短语误杀,真机 10 条);`has-id-fragment` 收窄为「非 ASCII 字符后的 4+ 位数字」(`ADR-0006`、`Han-1413141` 放行,真机 4 条)。
67
+
68
+ ### 已知边界
69
+
70
+ - 反刍收尾增量投射靠 `id→updatedAt` 基线 diff,而 relabel / 退场走 `patchMetadata` 不碰 `updated_time` ⇒ **metadata-only 改写不当轮重投**。
71
+ - P2 反向同步仍欠两项:启动自动钩子(已改为只给端点 + 按钮)、`pending` 那批未进 `conflicts` 待裁决面。
72
+ - **8 语译本自 0.20.0 起未跟进**(本次只把 0.22.0 起的条目补进去,0.20/0.21 的空档仍在,另记)。
73
+
74
+ ## [0.23.0] — 未发布(本地线 `0.23.0-local.1…8`,2026-10-12…13)
75
+
76
+ ### 新增
77
+
78
+ - **md 权威投射面(P0/P1/U/N10/N11)**:`l1_records.name` 列(文件身份键 + 部分唯一索引);`renderRecord` + 双指纹账本(`dbDigest`/`fileDigest`)+ `mem-export` / `mem-reconcile` 端点;面板「外部存档」节;投射根改为 **workspace 优先**(`<ws>/.agents/memory/<类型>/<名>.memory.md`);判过时联动 `.memory.md.disable`。
79
+ - **P2 反向同步(文件 → 索引)**:按账本双 digest 做**三方象限**——仅文件变回灌入库并重写成规范渲染(消写回环)、仅库变复用 `exportRecords` 重写文件、**两边都变则两侧都不动**只挂 `pending`;`.disable` 走退场不删文件;来路不明的新文件只计数不入库(导入须过写入内核)。端点 `dsh-memory/mem-sync`。
80
+ - **P3 归档 + 物理删**:`exportThenPurge` 门禁由整库哈希改**批次级投影**(并发蒸馏只要改过与本批无关的任何记录,旧实现就让清理永远删不动);删后写给人读的 `retired.md` + 把该批投射文件(含 `.disable`)搬进快照目录;`cleanup-retired` 干跑回**待删清单预览**;图投影索引表新增 `forgetRecords`(此前全仓只增不减——从快照恢复的记录因投影行仍在而被当「已投影」,**永不重投影**,检索图静默缺一条不报错)。
81
+ - **P4.1 默认类 `unclassified` + P4.2 正文版式标准化**(`normalizeContentMd`)。幂等是硬要求:不幂等就会让账本 `fileDigest` 每轮都变,写回环由渲染器自己引入。只做版式不做语义(不 bullet 化散文、不插 H1、不往正文塞元数据)。
82
+ - **孤儿投射文件归档 `dsh-memory/mem-orphans`**:只搬「账本记着我们写过、且现在没有任何记录认领这个路径」的文件;账本里没有的来路不明文件**一律不动**(那可能是人放进投射目录等着被同步回来的输入)。不删只搬,保住相对形状与原文。
83
+
84
+ ### 行为修复(召回显示面)
85
+
86
+ - 召回条目不再瞬时消失:view 带 `body` / `retired`,停止摘除 `session_recalled` 行,pill 显示「已判过时」删除线旁注。
87
+ - 启动清理存量退场残留(本机实测 725 行仍脏在 `l1_fts`);退场不变量补齐两条旁路写路径(修「标失效后检索面复活」)。
88
+ - 召回打分端到端:`-1..5`(0..5 相关度;`-1` 声明过时并退场),打分进成熟度指标(`-1/1/2` 拉低并沉底、`4/5` 抬高并上浮)。
89
+ - workspace 内容路由首次落地(dsh 生态 → `rewrite-agently`,非 dsh → `agent-no-dsh`;**只写 `workspace_id`、不动 `scope`、不动 `updated_time`**,并同步 `l1_fts` 行自带的归属列)。
90
+
91
+ ### 纪律
92
+
93
+ - memport 两个巨型函数两轮拆分(Extract Function,行为保持,CLI 金标准逐字节回归)。
94
+ - 端点契约**五处齐改**纪律持续生效(`MEMORY_ENDPOINTS ≡ switch ≡ contract 双 map ≡ contract-keys 计数 ≡ config`),计数链 55→56→57→58→59。
95
+
96
+ ## [0.22.2] — 2026-10-12
97
+
98
+ ### 新增
99
+
100
+ - 皮肤三态(auto/light/dark)+ 令牌覆写块;面板 11 Tab 单行收紧。
101
+ - `memory_import` 的 `ext_key` 幂等(`l1_records.ext_key` + 部分唯一索引 + `deduped` 回执)。
102
+ - **Memport Connect**:`.mem` 解析 / 预筛 / 扫描 + 来源注册表 + Connect Tab(跨实现 sha256 金标准对齐)。
103
+ - **技能结晶**:skill-store(JSON sidecar)+ 候选检测 + 技能召回注入 + 看板开关 `skillsEnabled`。
104
+ - G4 图谱因果边(导致 / 因为 / 修复了 / 取代,不 bump `projectorVersion`)。
105
+ - G3 成熟度读时派生(记录卡徽标 + 矛盾裁决视图旁注,不参与自动裁决)。
106
+ - G1 `usageScenario` 门控端到端(抽取 → 落 `metadata.dsh_usage_scenario` → 召回细排;off 逐字不变)。
107
+ - G2 类型叙述模板(门控 prompt 变体;off 是 on 的前缀)。
108
+ - wing / 认知 hall 两轴不串(正交访问器 + 值域不相交回归锁)。
109
+ - **G-live 本会话活召回集**:持久 sidecar + `session-recalled` 只读端点 + pill 只读列表。
110
+ - `memoryRecall` 会话感知投影(brief 侧栏「本会话召回」分区数据源,`init(header).id`)。
111
+ - `cfg.slots.migrateLegacy`(默认关):把「钉成常驻、内容已由活 L1 承载」的 rule 槽位软退场。
112
+
113
+ ### 修复
114
+
115
+ - 反刍回灌致待处理计数还原 / 变多(ruminate 改传空消息,与 `enqueueSessionSlices` 同语义)。
116
+ - 反刍 finalize 与 `doLightRefresh` 的 L3 强制真跑 + 计数清零 + `state.save()` 落盘(修「待 L2/L3 反刍后不清、重启又弹回」)。
117
+
118
+ ### 纪律
119
+
120
+ - 全部新能力默认关,off 态产物与注入逐字不变;跨插件展示契约(`memorySlots` 投影)保持接线。
121
+
122
+ ## [0.22.1] — 2026-10-09
123
+
124
+ ### 修复
125
+
126
+ - **浮层底面改为不透明**:输入栏的记忆浮层(`.dsh-mem-popover`)与下拉 / 筛选面板(`.dsh-mem-pop`)原先跟随宿主的 `--dsw-specific-menu`,而宿主把它解析成半透明玻璃值 ⇒ 面板透出背景。现改为在**不透明 `bg-primary` 底**上叠别名层(underlay 手法),无论宿主 alpha 如何面板都是实的。
127
+
128
+ ## [0.22.0] — 2026-10-09
129
+
130
+ ### 变更
131
+
132
+ - **声明面适配 15rc**:9 条 dsh peers 覆盖全部已发布宿主 rc(`0.1.0-rc.2..0.2.0-rc.2`,逐条 `npm view` 取证且各线集合一致),全部 optional;`engines >= 0.1.0-rc.2` 作粗栅栏(精确闸门是 peers),`package.json` 与 `dsh.plugin.json` 同步。
133
+ - **双清单版本回正**:`dsh.plugin.json` 此前漂移在 `0.20.0-beta.6-dsh0.2.0.1`,本批与 `package.json` 逐字对齐(V1 门禁)。
134
+ - **`dsh.client.inject` 收缩到 `ui-primitives` only**:`dsh-client-connection` 从 0.1.2 起才作为包存在,声明它会让整个 client 入口在 0.1.0/0.1.1 上挂起(`rpc.ts` 早已惰性 `ctx.get` 该连接)。
135
+
136
+ ### 纪律
137
+
138
+ - 所有 build 型仓注入 `prepack: npm run build`——`npm pack` 只触发 prepack,不触发 `prepublishOnly`;这是 stale-build 冤案的三层防线之一。
139
+
20
140
  ## [0.21.0] — 2026-10-04
21
141
 
22
142
  ### 新增