hypomnema 1.10.0 → 1.10.2
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +31 -0
- package/README.ko.md +5 -5
- package/README.md +5 -5
- package/commands/crystallize.md +21 -15
- package/commands/upgrade.md +13 -2
- package/docs/ARCHITECTURE.md +127 -63
- package/docs/CONTRIBUTING.md +7 -11
- package/hooks/base-store.mjs +31 -17
- package/hooks/hypo-auto-minimal-crystallize.mjs +39 -6
- package/hooks/hypo-auto-stage.mjs +123 -63
- package/hooks/hypo-close-guard.mjs +3 -37
- package/hooks/hypo-compact-guard.mjs +9 -6
- package/hooks/hypo-file-watch.mjs +18 -10
- package/hooks/hypo-personal-check.mjs +137 -55
- package/hooks/hypo-session-start.mjs +17 -6
- package/hooks/hypo-shared.mjs +1507 -569
- package/hooks/hypo-stop.mjs +8 -0
- package/hooks/session-views.mjs +9 -3
- package/package.json +2 -1
- package/scripts/crystallize.mjs +1 -1
- package/scripts/doctor.mjs +160 -9
- package/scripts/init.mjs +60 -22
- package/scripts/lib/crystallize-close-apply.mjs +156 -22
- package/scripts/lib/crystallize-close-check.mjs +1 -1
- package/scripts/lib/extensions.mjs +2 -2
- package/scripts/lib/git-hooks-dir.mjs +228 -31
- package/scripts/lib/plugin-detect.mjs +57 -4
- package/scripts/lib/settings-write.mjs +254 -0
- package/scripts/lib/wikilink.mjs +10 -1
- package/scripts/uninstall.mjs +3 -2
- package/scripts/upgrade.mjs +94 -23
- package/templates/Home.md +1 -1
- package/templates/hypo-config.md +1 -1
- package/templates/hypo-guide.md +60 -22
- package/templates/session-state.md +0 -12
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,37 @@ All notable changes to Hypomnema are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.10.2] - 2026-10-29
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
#### English
|
|
12
|
+
- Session close judges uncommitted files by this session's own write record, so another terminal's edit no longer blocks a close. When the record cannot be trusted (a session opened before this version, an unreadable record, a write that could not be recorded, a call without a session id) the whole project folder being closed blocks instead. A check that fails by a real error withholds the receipt and names the command to run; `hypomnema doctor` verifies receipt evidence against the commit it names; the gate runs one `git status`; a session that was already open keeps the older rule until a new session starts. ([#322](https://github.com/sk-lim19f/Hypomnema/pull/322))
|
|
13
|
+
#### 한국어
|
|
14
|
+
- 세션 close가 미커밋 파일을 이 세션의 쓰기 기록으로 판정해, 다른 터미널의 편집이 더는 close를 막지 않습니다. 기록을 믿을 수 없을 때(이 버전 전에 연 세션, 읽지 못하는 기록, 기록하지 못한 쓰기, session id 없는 호출)는 닫는 프로젝트 폴더 전체를 막습니다. 진짜 오류로 끝난 검사는 영수증을 보류하고 돌릴 명령을 안내하며, `hypomnema doctor`가 영수증 근거를 그 커밋과 대조하고, 게이트는 `git status`를 한 번만 돌립니다. 이미 열려 있던 세션은 새 세션을 열기 전까지 옛 규칙을 씁니다. ([#322](https://github.com/sk-lim19f/Hypomnema/pull/322))
|
|
15
|
+
|
|
16
|
+
### Changelog
|
|
17
|
+
- [#322](https://github.com/sk-lim19f/Hypomnema/pull/322) fix(close-gate): judge uncommitted files by the session's own write record
|
|
18
|
+
Contributors: @sk-lim19f
|
|
19
|
+
|
|
20
|
+
## [1.10.1] - 2026-10-27
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
#### English
|
|
24
|
+
- The 1.10.0 migration no longer loses a `.gitignore` or staged change made during its window, keeps an earlier run's archive record on undo, and reports git's own error when it stops. The vault pre-commit hook now runs the enabled plugin's worker over a stale sidecar (`hypomnema upgrade --apply` rewrites older hooks). `settings.json` stays a symlink, keeps its mode and survives a failed write. init no longer pushes without the hook guard and no longer copies a root `session-state.md`. ([#320](https://github.com/sk-lim19f/Hypomnema/pull/320))
|
|
25
|
+
#### 한국어
|
|
26
|
+
- 1.10.0 이행이 그 사이에 생긴 `.gitignore` 변경과 stage 변경을 잃지 않고, 되돌릴 때 앞 실행의 보관 기록을 남기며, 멈추면 git 의 오류를 그대로 알립니다. 볼트 pre-commit 훅이 오래된 sidecar 대신 켜진 플러그인의 worker 를 돌립니다(옛 훅은 `hypomnema upgrade --apply` 가 새로 씁니다). `settings.json` 이 symlink 로 남고 mode 를 지키며 쓰기가 실패해도 온전합니다. init 이 훅 가드 없이 push 하지 않고 루트 `session-state.md` 를 복사하지 않습니다. ([#320](https://github.com/sk-lim19f/Hypomnema/pull/320))
|
|
27
|
+
|
|
28
|
+
### Chores
|
|
29
|
+
#### English
|
|
30
|
+
- The vault guide template is now version 4, and `hypomnema upgrade` reports an older guide until you merge it by hand and raise its `version:` stamp (`--apply` never overwrites it). The architecture and contributor guides now describe both install channels. ([#319](https://github.com/sk-lim19f/Hypomnema/pull/319))
|
|
31
|
+
#### 한국어
|
|
32
|
+
- 볼트 안내 템플릿이 버전 4 가 됐습니다. 예전 안내는 손으로 병합하고 `version:` 스탬프를 올릴 때까지 `hypomnema upgrade` 가 경고합니다(`--apply` 는 덮어쓰지 않습니다). 아키텍처 문서와 기여자 문서가 두 설치 채널을 설명합니다. ([#319](https://github.com/sk-lim19f/Hypomnema/pull/319))
|
|
33
|
+
|
|
34
|
+
### Changelog
|
|
35
|
+
- [#320](https://github.com/sk-lim19f/Hypomnema/pull/320) fix: keep local changes through the 1.10.0 migration, run the enabled plugin's hook worker, write settings.json through links
|
|
36
|
+
- [#319](https://github.com/sk-lim19f/Hypomnema/pull/319) docs: bring the architecture, contributor and vault guides in line with 1.10.0
|
|
37
|
+
Contributors: @sk-lim19f
|
|
38
|
+
|
|
8
39
|
## [1.10.0] - 2026-10-15
|
|
9
40
|
|
|
10
41
|
> [!IMPORTANT]
|
package/README.ko.md
CHANGED
|
@@ -76,7 +76,7 @@ hypomnema
|
|
|
76
76
|
|
|
77
77
|
> `init`은 `~/.zshrc`(또는 `~/.bashrc`)에 `claude()` 셸 함수도 추가합니다. `# hypo-managed:shell-setup:start`와 `:end` 마커 사이에 들어가며, `cd`만 한 세션에도 프로젝트 컨텍스트가 주입되게 합니다. `--no-shell`로 건너뛰거나 `--shell-config=<path>`로 대상 파일을 바꿀 수 있습니다. `uninstall --apply`는 이 블록을 지웁니다(기본으로 `~/.zshrc`와 `~/.bashrc` 둘 다 확인하고, `--shell-config`로 지정한 파일이 있으면 그것만 봅니다). 다만 마커를 손으로 편집해서 중복되거나 순서가 뒤바뀐 상태라면, 무엇을 지워야 할지 안전하게 판단할 수 없으므로 블록을 그대로 둡니다. `--keep-shell`을 주면 이 셸 rc 정리만 건너뛰고 나머지 uninstall 은 그대로 돕니다.
|
|
78
78
|
|
|
79
|
-
> `init`은 위키 저장소에 pre-commit 훅도 설치합니다(`<위키>/.git/hooks/pre-commit`, `# hypo-managed:pre-commit:*` 마커). `.hypoignore`에 걸리는 파일이 스테이지되면 커밋을 막습니다. 해당 파일을 unstage 하거나 `git commit --no-verify`로 우회하세요. 이 훅은 설치 경로를 박아 두지 않습니다. 커밋 시점에 `~/.claude/
|
|
79
|
+
> `init`은 위키 저장소에 pre-commit 훅도 설치합니다(`<위키>/.git/hooks/pre-commit`, `# hypo-managed:pre-commit:*` 마커). `.hypoignore`에 걸리는 파일이 스테이지되면 커밋을 막습니다. 해당 파일을 unstage 하거나 `git commit --no-verify`로 우회하세요. 이 훅은 설치 경로를 박아 두지 않습니다. 커밋 시점에 후보 둘을 봅니다. 하나는 `~/.claude/settings.json`에서 켜 둔 플러그인(그 플러그인의 `~/.claude/plugins/installed_plugins.json` 항목, user scope 우선)이고, 다른 하나는 `~/.claude/hypo-pkg.json`의 `pkgRoot`입니다. 둘 다 찾히면 버전이 더 높은 쪽을 쓰고, 같으면 플러그인을 씁니다. 둘 다 안 되면 레지스트리의 `hypo@hypomnema` 항목이나 옛 `hypomnema@hypomnema` 항목을 user scope 우선으로 봅니다. 후보로 인정하는 조건은 절대 경로이고, 그 디렉터리의 `package.json` 이름이 `hypomnema`이며 버전이 있고, 실제로 부를 스크립트가 거기 있는 것입니다. 이보다 느슨하면 당신이 쓰지 않은 `pkgRoot`가 위키에서 실행될 수 있습니다. 아무것도 못 찾으면 `.hypoignore` 가드를 조용히 건너뛰는 대신, 무엇을 확인했고 어떻게 고치는지 출력하며 커밋을 거부합니다. 이렇게 해석하는 값으로 단계마다 Node 기동이 한 번 더 들어가 약 80ms가 붙고, `--lint-strict` 위키는 커밋당 대략 165ms를 냅니다. 예전 릴리스가 설치한 훅은 여전히 버전 고정 형태(`init` 시점에 절대 설치 경로가 박히고, 옮기거나 재설치할 때마다 낡는 형태)일 수 있는데, `hypomnema upgrade --apply`(또는 플러그인 설치라면 `/hypo:upgrade`에서 apply 단계 확인)가 스스로 경로를 찾는 새 형태로 옮겨 줍니다. 박힌 경로가 디스크에 이미 없어도 마찬가지로 옮겨 줍니다. 1.8.4부터 1.10.0까지가 설치한 훅은 예전 순서(`pkgRoot` sidecar를 먼저 보는 순서라, 낡은 sidecar가 켜 둔 플러그인을 이깁니다)로 해석합니다. `doctor`가 이를 경고하고, 같은 `upgrade --apply`가 지금 순서로 다시 써 줍니다. 본문 자체를 알아볼 수 없는 훅(손으로 고쳤거나 손상된 경우)은 다릅니다. `doctor`와 `upgrade` 모두 손대지 않고 경고만 하며, 이때 고치는 명령은 `upgrade --apply`가 아니라 `hypomnema init --force-commands`(또는 플러그인의 `/hypo:init --force-commands`)로 가드를 다시 설치하는 것입니다. 패키지가 아니라 위키 자체를 옮겼다면 여전히 `hypomnema init`을 새로 돌려야 합니다. `upgrade`는 설치 루트 해석만 고치고, `--lint-strict` 훅에 이미 박힌 `--hypo-dir`은 건드리지 않기 때문입니다. `uninstall --apply`는 이 훅을 지웁니다. 위키 위치는 `init`과 같은 방식으로 찾고, `--hypo-dir=<path>`로 직접 지정할 수도 있습니다. 다만 마커는 있지만 그 바깥에 다른 내용이 붙은 훅처럼 더 이상 온전히 Hypomnema 소유가 아닌 훅은 손대지 않고 그대로 둡니다. `--keep-wiki-hook`을 주면 이 위키 훅 정리만 건너뛰고 나머지 uninstall 은 그대로 돕니다.
|
|
80
80
|
|
|
81
81
|
> 두 번째 기기에서는 그냥 `hypomnema`를 돌리지 마세요. 원격과 무관한 새 위키가 만들어집니다. 기존 위키를 클론하는 쪽을 쓰세요. `hypomnema --from-remote=<git-url>`이 위키 루트로 클론한 뒤 실제 Hypomnema 위키인지 확인하고, 새 git 히스토리를 만들지 않은 채 훅과 슬래시 커맨드만 설치합니다. 대상 디렉터리가 이미 있으면 거부합니다.
|
|
82
82
|
|
|
@@ -254,7 +254,7 @@ Hypomnema는 청크가 아니라 페이지를 지식 단위로 봅니다. 새
|
|
|
254
254
|
| `/hypo:graph` | 위키링크 의존성 그래프 생성 | 구조가 어떻게 자랐는지 보고 싶을 때 |
|
|
255
255
|
| `/hypo:rename` | 페이지·디렉터리 이름 변경 + 인바운드 `[[위키링크]]` 갱신 | 페이지나 프로젝트 폴더 이름을 바꿀 때 |
|
|
256
256
|
|
|
257
|
-
### 라이프사이클 훅 (
|
|
257
|
+
### 라이프사이클 훅 (12개)
|
|
258
258
|
|
|
259
259
|
| 훅 | 이벤트 | 역할 |
|
|
260
260
|
|---|---|---|
|
|
@@ -266,7 +266,7 @@ Hypomnema는 청크가 아니라 페이지를 지식 단위로 봅니다. 새
|
|
|
266
266
|
| `hypo-cwd-change.mjs` | `CwdChanged` | cwd에 맞는 프로젝트 `hot.md`로 알림을 만든다(아래 전송 안내 참조) |
|
|
267
267
|
| `hypo-file-watch.mjs` | `FileChanged` | 바뀐 위키 파일로 알림을 만든다(`.hypoignore` 준수). 이 패키지는 감시 경로를 등록하지 않아 기본 설치에서는 이벤트가 발생할 계기 자체가 없다(아래 전송 안내 참조) |
|
|
268
268
|
| `hypo-auto-stage.mjs` | `PostToolUse(Write/Edit/MultiEdit)` | 위키 파일 자동 stage |
|
|
269
|
-
| `hypo-stop.mjs` | `Stop` | 등록된 유일한 `Stop` 훅이다. 네 단계를 병렬이 아니라 순서대로 돌린다: 세션 현황 파일 재생성(`SessionStart`도 같은 생성기를 부른다), observability 점수와 auto-resume 신호용 세션 메타데이터 기록, commit + pull + push, 그리고 사용자가 마무리를 신호한 뒤 의미 있는 작업을 했는데 마무리가 확인되지 않으면 Stop을 막고 `crystallize.mjs --mark-session-closed` 명령을 돌려준다(미커밋
|
|
269
|
+
| `hypo-stop.mjs` | `Stop` | 등록된 유일한 `Stop` 훅이다. 네 단계를 병렬이 아니라 순서대로 돌린다: 세션 현황 파일 재생성(`SessionStart`도 같은 생성기를 부른다), observability 점수와 auto-resume 신호용 세션 메타데이터 기록, commit + pull + push, 그리고 사용자가 마무리를 신호한 뒤 의미 있는 작업을 했는데 마무리가 확인되지 않으면 Stop을 막고 `crystallize.mjs --mark-session-closed` 명령을 돌려준다(이 세션이 쓴 미커밋 파일이나 진행 중인 작업이 있으면 명령 대신 지금 닫을지 되묻는다. 다른 세션의 미커밋 파일로는 묻지 않는다. 업그레이드 전에 연 세션이나, 쓰기 기록이 한 번이라도 실패했거나 경로를 잃은 세션은 예외다) |
|
|
270
270
|
| `hypo-personal-check.mjs` | `PreCompact` | session-close 미완, 위키 커밋/푸시 누락, hot.md 구조 위반, lint 블로커를 `systemMessage`로 알린다. `/compact`는 여기서 막지 않는다. 세션 마무리 강제는 다른 자리에 남아 있다: `hypo-stop.mjs`의 crystallize 단계가 마무리 확인 없는 의미 있는 세션을 막고, `--mark-session-closed`는 여전히 red 게이트에서 마커를 거부한다 |
|
|
271
271
|
| `hypo-session-end.mjs` | `SessionEnd` | SessionEnd 마커를 기록해 다음 SessionStart가 `source=clear` 복구를 감지하게 한다 |
|
|
272
272
|
| `hypo-web-fetch-ingest.mjs` | `PostToolUse(WebFetch/WebSearch)` | WebFetch/WebSearch 뒤 `additionalContext`에 `/hypo:ingest` 권유를 주입한다(URL의 query/hash/userinfo는 제거) |
|
|
@@ -426,8 +426,8 @@ E. 멈춘 프로젝트 재개.
|
|
|
426
426
|
|
|
427
427
|
| 설치 경로 | 슬래시 커맨드 위치 |
|
|
428
428
|
|---|---|
|
|
429
|
-
| 플러그인 (Path A) | Claude Code 플러그인 캐시. `/plugin marketplace update hypomnema` 후 `/reload-plugins`로 갱신. 캐시 디렉터리 이름이 매니페스트 버전이라, 그 버전이 바뀌어야 갱신이 반영된다. 버전을 그대로 둔 채 머지한 커밋은 이미 설치된 곳에 닿지 않는다. |
|
|
430
|
-
| npm CLI (Path B) | `~/.claude/commands/hypo/`. `hypomnema upgrade --apply`로 갱신, 파일별 SHA 추적. 사용자 수정본까지 덮어쓰려면 `--force-commands`(원본은 `.bak`으로 보존) |
|
|
429
|
+
| 플러그인 (Path A) | Claude Code 플러그인 캐시. `/plugin marketplace update hypomnema` 후 `/reload-plugins`로 갱신. 캐시 디렉터리 이름이 매니페스트 버전이라, 그 버전이 바뀌어야 갱신이 반영된다. 버전을 그대로 둔 채 머지한 커밋은 이미 설치된 곳에 닿지 않는다. 업데이트 전에 열려 있던 세션은 reload 뒤에도 이전 마무리 규칙(닫는 프로젝트 폴더에 미커밋 파일이 있으면 마무리를 막는다)을 그대로 쓴다. 세션마다 자기가 쓴 파일만 따지는 새 규칙은 새 세션을 시작해야 적용된다. |
|
|
430
|
+
| npm CLI (Path B) | `~/.claude/commands/hypo/`. `hypomnema upgrade --apply`로 갱신, 파일별 SHA 추적. 사용자 수정본까지 덮어쓰려면 `--force-commands`(원본은 `.bak`으로 보존). 업그레이드 전에 열려 있던 세션은 여기서도 이전 마무리 규칙을 그대로 쓴다. 새 세션이 받는 기록 스탬프는 설치 경로와 상관이 없다. 세션마다 자기가 쓴 파일만 따지는 새 규칙은 새 세션을 시작해야 적용된다. |
|
|
431
431
|
|
|
432
432
|
---
|
|
433
433
|
|
package/README.md
CHANGED
|
@@ -250,7 +250,7 @@ Nine commands cover the full capture → retrieval → consolidation cycle.
|
|
|
250
250
|
| `/hypo:graph` | Generates a wikilink dependency graph | When you want to see how the structure grew |
|
|
251
251
|
| `/hypo:rename` | Renames a page or directory and rewrites inbound `[[wikilinks]]` | When a page or project folder needs a new name |
|
|
252
252
|
|
|
253
|
-
### Lifecycle hooks (
|
|
253
|
+
### Lifecycle hooks (12)
|
|
254
254
|
|
|
255
255
|
| Hook | Event | Role |
|
|
256
256
|
|---|---|---|
|
|
@@ -262,7 +262,7 @@ Nine commands cover the full capture → retrieval → consolidation cycle.
|
|
|
262
262
|
| `hypo-cwd-change.mjs` | `CwdChanged` | Build a notice from the matching project's `hot.md` (see the transmission note below) |
|
|
263
263
|
| `hypo-file-watch.mjs` | `FileChanged` | Build a notice for a changed wiki file (honors `.hypoignore`); nothing in this package registers watch paths, so on a stock install the event has no trigger here at all (see the transmission note below) |
|
|
264
264
|
| `hypo-auto-stage.mjs` | `PostToolUse(Write/Edit/MultiEdit)` | Auto-stage wiki-file edits |
|
|
265
|
-
| `hypo-stop.mjs` | `Stop` | The only registered `Stop` hook. Runs four stages in order, not in parallel: rebuild the generated session views (`SessionStart` calls the same generator), record session metadata for the observability score and auto-resume signaling, commit + pull + push, then, after the user signals wrap-up, block `Stop` on a substantial session with no verified close and hand back the `crystallize.mjs --mark-session-closed` command (with uncommitted
|
|
265
|
+
| `hypo-stop.mjs` | `Stop` | The only registered `Stop` hook. Runs four stages in order, not in parallel: rebuild the generated session views (`SessionStart` calls the same generator), record session metadata for the observability score and auto-resume signaling, commit + pull + push, then, after the user signals wrap-up, block `Stop` on a substantial session with no verified close and hand back the `crystallize.mjs --mark-session-closed` command (with an uncommitted file this session wrote, or work still in flight, it asks whether to close now instead; another session's uncommitted file does not trigger that question, except in a session opened before the upgrade or one whose record of a write failed or lost paths) |
|
|
266
266
|
| `hypo-personal-check.mjs` | `PreCompact` | Surfaces an unfinished session-close, an uncommitted or unpushed wiki, malformed `hot.md`, or lint blockers as a `systemMessage`; it never blocks `/compact`. Session close is still enforced elsewhere: `hypo-stop.mjs`'s crystallize stage blocks on a substantial session with no verified close, and `--mark-session-closed` still refuses the marker on a red gate |
|
|
267
267
|
| `hypo-session-end.mjs` | `SessionEnd` | Write a SessionEnd marker so SessionStart can detect `source=clear` recovery |
|
|
268
268
|
| `hypo-web-fetch-ingest.mjs` | `PostToolUse(WebFetch/WebSearch)` | Inject a `/hypo:ingest` nudge into `additionalContext` after a WebFetch (URL redacted of query/hash/userinfo) or a WebSearch |
|
|
@@ -406,7 +406,7 @@ Place a `hypo-config.md` at the wiki root to make it portable across machines wi
|
|
|
406
406
|
|
|
407
407
|
`.hypoignore` controls which paths the hooks ignore (default: `*.pdf`, `*.zip`, `*.pem`, `.env*`, `*credentials*`, `*secret*`, `*token*`, `*password*`, `*passwd*`, …). Edit it directly; there is no privacy mode flag. One file, one source of truth.
|
|
408
408
|
|
|
409
|
-
> `init` also installs a pre-commit hook into the wiki repo at `<wiki>/.git/hooks/pre-commit`, marked `# hypo-managed:pre-commit:*`. It refuses any commit that stages a path matching `.hypoignore`; unstage the file, or override with `git commit --no-verify`. The hook does not bake in an install path: it resolves the active Hypomnema install itself, at commit time
|
|
409
|
+
> `init` also installs a pre-commit hook into the wiki repo at `<wiki>/.git/hooks/pre-commit`, marked `# hypo-managed:pre-commit:*`. It refuses any commit that stages a path matching `.hypoignore`; unstage the file, or override with `git commit --no-verify`. The hook does not bake in an install path: it resolves the active Hypomnema install itself, at commit time. It looks at two candidates: the plugin enabled in `~/.claude/settings.json` (that plugin's row in `~/.claude/plugins/installed_plugins.json`, user-scope first) and `~/.claude/hypo-pkg.json`'s `pkgRoot`. When both resolve, the newer version wins, and the plugin wins a tie. When neither does, it falls back to any `hypo@hypomnema` or legacy `hypomnema@hypomnema` registry entry, user-scope first. A candidate only counts when it is an absolute path whose `package.json` names `hypomnema` and carries a version, and whose script is actually there — anything looser and a `pkgRoot` you did not write could run in your vault. When nothing resolves, it refuses the commit and prints what it checked and how to fix it, rather than silently skip the `.hypoignore` guard. Resolving this way costs a second Node start per step, about 80ms, so a `--lint-strict` vault pays roughly 165ms per commit. A hook an older release wrote may still carry the version-pinned form (an absolute install path baked in at `init` time, which goes stale on every move or reinstall); `hypomnema upgrade --apply` (or `/hypo:upgrade`, confirming the apply step, on a plugin install) migrates it onto the self-resolving form, even when the baked-in path no longer exists on disk. A hook written by 1.8.4 through 1.10.0 resolves in the older order (the `pkgRoot` sidecar first, so a stale sidecar wins over the enabled plugin); `doctor` warns about it and the same `upgrade --apply` rewrites it onto the current order. A hook whose body cannot be recognized at all (hand-edited, corrupted) is different: `doctor` and `upgrade` both warn about it instead of touching it, and the fix is `hypomnema init --force-commands` (or the plugin's `/hypo:init --force-commands`) to reinstall the guard, not `upgrade --apply`. Moving the vault itself, not just the package, still needs a fresh `hypomnema init`: `upgrade` only ever fixes the install-root resolution, never a `--hypo-dir` a `--lint-strict` hook already baked in. `uninstall --apply` removes this hook, resolving the vault the same way `init` does (`--hypo-dir=<path>` to override); it only removes a hook that still carries the marker and no content outside it, so a hook you have since edited by hand is left in place. Pass `--keep-wiki-hook` to skip this wiki hook cleanup (the rest of the uninstall still runs).
|
|
410
410
|
|
|
411
411
|
> The credential-style patterns above are substring globs, so an ordinary page such as `pages/oauth-token-refresh.md` is matched too: it is never injected by any hook, and the wiki pre-commit hook refuses to commit it. If you write about these topics, narrow the patterns or rename the page.
|
|
412
412
|
|
|
@@ -426,8 +426,8 @@ Place a `hypo-config.md` at the wiki root to make it portable across machines wi
|
|
|
426
426
|
|
|
427
427
|
| Install path | Slash commands served from |
|
|
428
428
|
|---|---|
|
|
429
|
-
| Plugin (Path A) | Claude Code's plugin cache; updated via `/plugin marketplace update hypomnema` then `/reload-plugins`. The cache directory is named after the manifest version, so an update lands only when that version has changed. A commit merged under an unchanged version does not reach an existing install. |
|
|
430
|
-
| npm CLI (Path B) | `~/.claude/commands/hypo/`; updated via `hypomnema upgrade --apply` with per-file SHA tracking. Pass `--force-commands` to overwrite hand-edits (creates `.bak`). |
|
|
429
|
+
| Plugin (Path A) | Claude Code's plugin cache; updated via `/plugin marketplace update hypomnema` then `/reload-plugins`. The cache directory is named after the manifest version, so an update lands only when that version has changed. A commit merged under an unchanged version does not reach an existing install. A session that was already open before the update keeps the older close rule (an uncommitted file in the project folder being closed blocks) even after the reload; start a new session to get the rule that follows each session's own record of its writes. |
|
|
430
|
+
| npm CLI (Path B) | `~/.claude/commands/hypo/`; updated via `hypomnema upgrade --apply` with per-file SHA tracking. Pass `--force-commands` to overwrite hand-edits (creates `.bak`). A session that was already open before the upgrade keeps the older close rule here too, since the record stamp a new session gets does not depend on the install path; start a new session to get the rule that follows each session's own record of its writes. |
|
|
431
431
|
|
|
432
432
|
---
|
|
433
433
|
|
package/commands/crystallize.md
CHANGED
|
@@ -63,8 +63,8 @@ Optional fields, which stay out of the JSON template above:
|
|
|
63
63
|
|
|
64
64
|
- `date`: defaults to today (local). If supplied, it must be a real calendar date in `YYYY-MM-DD` form; a day that does not exist, such as `2026-09-31`, fails the apply before any write.
|
|
65
65
|
- `new: true` on a track element starts a track the project has not registered yet. An unregistered id without it is refused (`track-unknown`, and the error lists the registered ids). `new: true` on a registered id is refused as well (`track-exists`).
|
|
66
|
-
- `done: true` on a track element ends the track. It may be sent without a body: `{ "id": "<track-id>", "done": true }`. It ends only the heads this session knew about (the ones it was shown in full or as a pointer line). A head another session wrote to the same track at the same time stays active. To end that one too, read that entry first, then send `done` again in a later close.
|
|
67
|
-
- `supersedes` on a track element names the earlier heads of that track this update replaces. Leave it out and the update replaces every head of the track this session was told about. When you name them, use a full close id as shown in the injection or the view, or a prefix that is unique within that track. A close id that does not exist, or an ambiguous prefix, is refused (`supersedes-unknown`).
|
|
66
|
+
- `done: true` on a track element ends the track. It may be sent without a body: `{ "id": "<track-id>", "done": true }`. It ends only the heads this session knew about (the ones it was shown in full or as a pointer line). The baseline heads made from the old `session-state.md` that this session was shown before the vault moved to session entries count too. A head another session wrote to the same track at the same time stays active. To end that one too, read that entry first, then send `done` again in a later close.
|
|
67
|
+
- `supersedes` on a track element names the earlier heads of that track this update replaces. Leave it out and the update replaces every head of the track this session was told about (baseline heads made from the old `session-state.md` it was shown before the vault moved included). When you name them, use a full close id as shown in the injection or the view, or a prefix that is unique within that track. A close id that does not exist, or an ambiguous prefix, is refused (`supersedes-unknown`).
|
|
68
68
|
- Never put the same track id in `tracks` twice (`track-duplicate`). A normal update plus a separate `done` for one track is the usual way to hit it: send one element for that track instead.
|
|
69
69
|
- `log.entry`: a custom line for `<hypo-root>/log.md`. Omit it by default: apply derives the canonical `## [date] session | <project>` line from your `sessionLog` heading. Supply it only for a custom line, which must still be a canonical `session | <project>` heading, or the apply fails at `stage='pre-apply-verification'`.
|
|
70
70
|
- `openQuestions.content`: the full body of `pages/open-questions.md`. Include it only when this session actually changed that file. It is the one overwrite left in a close, so it goes through the base protection and the approval path described below and in Step 4 (`proposal-pending`).
|
|
@@ -115,19 +115,24 @@ and nothing on disk, in three cases:
|
|
|
115
115
|
A refusal is not a failure to route around. It means the close should not happen: ask
|
|
116
116
|
the user, and re-run only after they say so.
|
|
117
117
|
|
|
118
|
-
On a verified close (`ok: true`,
|
|
118
|
+
On a verified close (`ok: true`, and no uncommitted file this session recorded writing)
|
|
119
119
|
the apply first files a commit-backed close checkpoint receipt at
|
|
120
120
|
`HYPO_DIR/.cache/sessions/<id>/close-receipt.json`, then writes the per-session compat marker
|
|
121
121
|
`HYPO_DIR/.cache/session-closed-<id>.marker` as a projection of it. If the marker does not land,
|
|
122
122
|
the receipt is withdrawn again, so a run that reports the marker as failed never leaves a valid
|
|
123
|
-
receipt behind.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
123
|
+
receipt behind. One git check runs for every caller: this checkpoint gate, `--mark-session-closed`, `--check-session-close`, PreCompact and the Stop hook. An uncommitted file blocks only when this session's record of its writes names it (the record the Write and Edit hook fills, and the close's own writes), or when a record-failure flag names it, whatever its bytes are now. If any record of this session failed (a failure flag exists at all, even one that names only a path outside the project) or the record lost paths it cannot name, every uncommitted file in the project folder being closed (`projects/<project>/`) blocks until the flag is removed or the session ends, because the record can no longer be trusted to say which of them are this session's. An empty `index.md` that HEAD does not have, in the project folder being closed, blocks whatever the record says: it is a project index whose create was cut between creating the file and writing it, and whose create it was cannot be told. When a close finds that another session already created the project's `index.md`, this session's claim on that file stays, so it blocks this session's close as its own write until it is committed. A session that began before 1.10.2 is treated the same way: SessionStart leaves a record stamp (`record-stamp.json`, in `.cache/sessions/<id>/`) for a session that starts on 1.10.2 or later, and a session without one may have missed writes with no flag to say so, so it keeps the older rule (every uncommitted file in the project folder being closed blocks). Reloading plugins does not stamp it; only a new session does. A call with no `--session-id` has no record to read, so it is treated the same way: with `--project` the whole project folder blocks, without it every uncommitted file under `projects/` does. Any other uncommitted file is a notice (type `unresolved`), not a blocker: another session's, one of unknown ownership, or one this session wrote through a shell command (Bash leaves no record, so such a file shows up only as a notice). The transcript is not ownership evidence here; it only widens the lint scope. Neither a blocker nor a notice stops `/compact`: the PreCompact hook only shows a notice.
|
|
124
|
+
|
|
125
|
+
How to get out of a project-folder block:
|
|
126
|
+
|
|
127
|
+
- The session has a failure flag (a record failed, or lost paths): check that no uncommitted file `git status` lists in the vault is one this session wrote, then delete the whole `<vault>/.cache/sessions/<id>/touched-paths-failed.d/` directory, or open a new session and close from there. The blocker's reason names that directory by its absolute path. The flag never clears on its own.
|
|
128
|
+
- The session has no record stamp (`record-stamp.json`; it began before 1.10.2), or has one that cannot be read: the blocked files may be another session's, so do not revert one that is another terminal's work. Commit or revert only the files you have confirmed are this session's; otherwise open a new session and close from there. There is no flag file to remove.
|
|
129
|
+
- The record is corrupt: look at the file the reason names (`.cache/sessions/<id>/touched-paths.json`, or `touched-hashes.json` beside it), check with `git status` that what this session wrote is committed, then delete it.
|
|
130
|
+
- The record's lock could not be created (permissions or disk space on `.cache/sessions/<id>/`): deleting a file does not help. Fix the permissions or free disk space, then re-run.
|
|
131
|
+
- A file that the failure flag names blocks whenever it is uncommitted, whatever its bytes. If it is this session's, commit or revert it. If it may be another session's, do not revert it: clear the flag as above.
|
|
132
|
+
- An empty `index.md` in the project folder being closed: if this session's close made it, fill it in or delete it and close again. If another session left it half made (that session died), delete the file. A new session sees the same empty file and blocks again, so starting one does not help. An `index.md` with content is not blocked.
|
|
133
|
+
- A call with no `--session-id`: pass the id.
|
|
134
|
+
|
|
135
|
+
The checkpoint receipt is the thing the
|
|
131
136
|
Stop-chain Layer 3 hook (`hypo-auto-minimal-crystallize`) actually checks: it proves only that
|
|
132
137
|
the file versions it names are in a specific commit, never that every change this session made
|
|
133
138
|
is saved. (It has nothing to do with the `close-receipt-failed` result of `proposal resolve`
|
|
@@ -199,7 +204,7 @@ Both gates judge only the **payload files**: this close's session entry, the ses
|
|
|
199
204
|
>
|
|
200
205
|
> Three refusals come before the receipt proof and carry no `reason`. A gate refusal (`blockers[]` plus `missing` and `stale`, with `error: 'session-close gate not satisfied'`) means the same close-file, lint or feedback checks as `--check-session-close` found something this session owns: fix it and re-run. A `skipReason` of `no-user-close-signal` (with `gateReason` when the gate has one) or `no-attribution-evidence` means the transcript shows no close request, or nothing ties this session to a project (pass `--project=<slug>` or `--log-only`). A bare `error` with none of these fields means the prior receipt or marker could not be set aside; fix the permission or disk problem under `.cache/` it names.
|
|
201
206
|
>
|
|
202
|
-
> The close checkpoint is recorded once the receipt is issued: `ok: true` from this command, and `close_state: closed` from `--check-session-close --session-id=<id>`. The Stop hook
|
|
207
|
+
> The close checkpoint is recorded once the receipt is issued: `ok: true` from this command, and `close_state: closed` from `--check-session-close --session-id=<id>`. The Stop hook runs the same git check and has the final say. The final "Close check" line of `--check-session-close` uses that check too, but a call without `--session-id` has no record to read and blocks the whole project folder (or every uncommitted file under `projects/` when no project is named), so another session's uncommitted file can keep a check without the id red after your close has landed. A red line never stops `/compact`: PreCompact only shows a notice.
|
|
203
208
|
|
|
204
209
|
---
|
|
205
210
|
|
|
@@ -214,7 +219,7 @@ The result JSON of `--apply-session-close` includes a `stage` field when `ok: fa
|
|
|
214
219
|
| `track-duplicate` | The same track id appears in `tracks` more than once (typically a normal update plus a separate `done` for one track). Caught **before** any write. | Merge the elements into one per track id, then re-run. No payload bytes were written. |
|
|
215
220
|
| `track-unknown` | A `tracks` element names an id the project has no track for and does not carry `new: true`. The `error` lists the registered ids. Caught **before** any write. | If it is a typo, use the registered id. If it is a new track, add `"new": true`. Re-run. Nothing was written. |
|
|
216
221
|
| `track-exists` | A `tracks` element carries `new: true` for an id the project already has. Caught **before** any write. | Drop `"new": true` to update the track (or pick another id for a new one), then re-run. Nothing was written. |
|
|
217
|
-
| `supersedes-unknown` | A `supersedes` value is not a close id of an update of that track, or is a prefix that matches more than one. The `error` lists the track's updates. Caught **before** any write. | Use a full close id or a prefix unique within that track, or drop `supersedes` to take the default (every head this session was told about), then re-run. Nothing was written. |
|
|
222
|
+
| `supersedes-unknown` | A `supersedes` value is not a close id of an update of that track, or is a prefix that matches more than one. The `error` lists the track's updates. Caught **before** any write. | Use a full close id or a prefix unique within that track, or drop `supersedes` to take the default (every head this session was told about, baseline heads made from the old `session-state.md` it was shown before the vault moved included), then re-run. Nothing was written. |
|
|
218
223
|
| `payload-reserved-marker` | The summary or a track body holds a line that is exactly an entry marker of this close (the whole line, carrying this close's id). Caught **before** any write. | Change or remove that line, then re-run. Other HTML comments and similar text do not trip it. Nothing was written. |
|
|
219
224
|
| `invalid-entry` | The entry could not be formatted for a reason other than a marker or a duplicate track: a value that does not fit the entry format (for example a malformed session id or project name). The `error` names it. Caught **before** any write. | Fix the value the `error` names, then re-run. Nothing was written. |
|
|
220
225
|
| `entry-project-mismatch` | This close request already published its session entry in another project (or recorded the intent to), and this payload names a different `project`, typically a retry after a failed commit with the project changed. One close request writes one entry in one project. The `error` names the existing entry and its project. Caught **before** any write; `committed: null`. | Re-run with `project` set to the project the `error` names, which finishes that close. Close the other project afterwards, with a new close request. |
|
|
@@ -268,7 +273,8 @@ If `markerWritten: true`: ask: "The close checkpoint is recorded. Would you like
|
|
|
268
273
|
|
|
269
274
|
If the apply succeeded but `markerWritten: false`, do NOT say "session closed." Branch on `markerSkipReason`, which carries one of three values here. Four more (`marker-did-not-land`, `receipt-proof-mismatch`, `receipt-write-failed`, `close-resolution-failed`) no longer reach this branch: when every other precondition cleared and the close still could not certify itself, that is a failure and not a policy withhold, so the result comes back `ok: false` with that value as `stage` and exit 1. Each has its own row in the stage table above.
|
|
270
275
|
|
|
271
|
-
- `compact-gate-not-ok`: surface the reason verbatim and address it (resolve the gate blocker) before re-running. The JSON's `gateBlockers[]` names what the checkpoint gate refused on; read the cause there, not from `--check-session-close
|
|
276
|
+
- `compact-gate-not-ok`: surface the reason verbatim and address it (resolve the gate blocker) before re-running. The JSON's `gateBlockers[]` names what the checkpoint gate refused on; read the cause there, not from `--check-session-close` run without `--session-id`, which cannot read this session's record and blocks the whole project folder. A `compact-gate-not-ok` blocker of type `known-session-write` is an uncommitted file this session recorded writing (or one whose record failed to land): save it in a vault commit or revert it. A blocker of type `git` that names a file is a project-folder block because the record lost paths, the session has a failure flag, the session has no record stamp, or the call has no session id, or it is an empty `index.md` in the project folder being closed (a call without `--session-id` blocks the whole project folder too); its reason carries the way out (see "How to get out of a project-folder block" in Step 3). **Never commit or revert another session's `projects/<p>/sessions/` entry, nor a session-log shard another session is appending to.** The entry is the only copy of that session's close summary, the appended heading is that session's log line, and that session's own close commits both. Neither blocks this close: a new entry waiting for its commit shows up as a `foreign-entry` notice, and a committed session-log shard whose only change is what other sessions' closes appended after its committed bytes (each close records its append) shows up as a `foreign-append` notice. If one stays uncommitted (that session's commit failed), it stays unsynced until that session runs its close again. A shard this session itself appended to and has not committed still blocks, because the record names it. A shard whose committed part was changed, one that gained any line no other close recorded (a hand edit, even a dated heading), or one with a staged edit or deletion blocks only under the rule in Step 3: the record names it, a failure flag names it, or the session is under the project-folder rule (no record stamp, a failure flag, a record that lost paths, or a call with no `--session-id`). With a complete record that does not name the shard, it is an `unresolved` notice. **The re-run needs no fresh close phrase from the user for these two.** The resolution that spends a close signal is written only once the marker lands, so a run denied its marker at this stage leaves that signal unspent and the retry is authorized by the same one.
|
|
277
|
+
A `compact-gate-not-ok` blocker of type `check-skipped` (with `check: 'lint'` or `'feedback'`) means that check died by an error and has no verdict: lint crashed, a spawn failed, a child process was killed from outside, or its output did not parse (or carried an `error` field). It comes from all five callers (apply, `--mark-session-closed`, `--check-session-close`, PreCompact, Stop), so no receipt is filed while it stands. `reason` names the cause, and the way out is to run `lint` or `feedback-sync --check` by hand, fix what it reports, and run the close again. A check that only ran out of time is different: it adds a notice (the `skippedWhy` text says the time limit) and does not block the receipt, so on a large vault a close can finish with lint unconfirmed. The same goes for the migration classification inside the global close check: when the gate deadline cuts it short, a project whose session-log has a close heading but no entry dated today gets a `close-legacy-unclassified` notice instead of a `close-legacy` one. It never blocks; run the check again to have the vault classified.
|
|
272
278
|
**A commit failure is `ok: false` with `stage: 'commit-failed'` (see the stage table), not a withheld marker. Re-run the apply, not `--mark-session-closed`.** Nothing of this close is committed yet, so `--mark-session-closed` has nothing to certify. The failed attempt took its session-log and `log.md` appends back, and `appliedUncommitted` lists only what is still on disk (the entry and its index side effects). The retry finds the entry by its close id and appends again, then commits both. An append file that another write changed since then (another session appended to the same shard, say) is left alone and keeps blocking the marker. If a retry reports `commit-failed:` or `compact-gate-not-ok` a second time on the same close, or comes back with `stage: 'receipt-proof-mismatch'` naming those close files, stop retrying and look at `git status` in the vault: stage and commit the close files yourself, or ask the user to, then run the close again.
|
|
273
279
|
- `transcript-unresolved`: the marker writer could not resolve any transcript for this `--session-id` at all, so it never got as far as checking for a close signal. Passing the exact same id again changes nothing; get the main conversation's real session id (not a background-task or Agent-thread uuid) and re-run.
|
|
274
280
|
- `no-user-close-signal`: a transcript was found, but it carries no close signal the gate recognizes. This is the one branch where the re-run DOES need a fresh signal: confirm intent once with `AskUserQuestion` (the same 세션 마무리 flow described under Step 3's `no-user-close-signal` table above), then re-run only after the user confirms. **Read `markerGateReason` first.** It carries the same three values as the authority refusal's `gateReason` (`no-open`, `transcript-rewrite-detected`, `no-new-open-since-resolution`), and only `no-open` means the user never asked. It also names a queued item shaped like an unregistered host tag when one retracted the close, which is a stale allowlist rather than a change of mind: report that name to the user instead of asking them to close again.
|
|
@@ -368,7 +374,7 @@ otherwise omit it.
|
|
|
368
374
|
|
|
369
375
|
An unrecognized flag exits 2 instead of being ignored.
|
|
370
376
|
|
|
371
|
-
Its
|
|
377
|
+
Its git check is the one every close caller runs. With `--session-id` it blocks only a file this session recorded writing (or a project folder whose record lost paths or has a failure flag, or whose session began before 1.10.2 and has no record stamp, and an empty `index.md` in the project folder being closed). Without the id it has no record to read and blocks the whole project folder, so it can stay red on another session's uncommitted file after your own close has landed. With `--session-id` for a session whose close was log-only, the project-close checks are skipped. Whether this session is closed is decided by the close checkpoint receipt: pass `--session-id=<id>` and read `close_state` (`closed` once the receipt is valid, or `legacy-closed` for an older marker written before receipts existed), not the vault-wide verdict. The Stop hook runs the same git check and has the final say.
|
|
372
378
|
|
|
373
379
|
Its required lines are this close's session entry (`projects/<project>/sessions/<date>-<close id>.md`), the session-log shard and `log.md`; the generated `session-state.md` and `hot.md` are not among them. It reports any file as `missing` or `stale`. For an actual close, prefer `--apply-session-close --payload=<path>` (Step 3): it bundles freshness and lint into one gate and is the documented dogfood path. `parseArgs` only accepts the `--payload=<path|->` spelling (a path, or `-` for stdin); a space-separated `--payload <path>` is rejected outright with exit 2, not silently dropped.
|
|
374
380
|
|
package/commands/upgrade.md
CHANGED
|
@@ -32,7 +32,7 @@ node ${CLAUDE_PLUGIN_ROOT}/scripts/upgrade.mjs [--hypo-dir="<path>"]
|
|
|
32
32
|
|
|
33
33
|
Show the output verbatim.
|
|
34
34
|
|
|
35
|
-
> **Plugin installs**: if the output begins with `ℹ Plugin install detected`, the core hooks, slash commands, and `settings.json` wiring are managed by the Claude Code plugin loader
|
|
35
|
+
> **Plugin installs**: if the output begins with `ℹ Plugin install detected`, the core hooks, slash commands, and `settings.json` wiring are managed by the Claude Code plugin loader, **not** by `/hypo:upgrade`. Do **not** run `--apply` expecting it to update them (it intentionally skips those to avoid double-registering every hook). To upgrade the plugin itself: `/plugin marketplace update hypomnema`, then `/reload-plugins` in **every session that is already open**, not only the one where you ran the update. A session that has not reloaded keeps running the old hooks with the new commands, and in that mix a failed write record leaves no trace. If a session's close is then blocked under the old project-folder rule (every uncommitted file in the project folder being closed blocks), end that session and open a new one: a session that was open before the upgrade has no record stamp (`record-stamp.json`), reloading does not give it one, and only a session started after the upgrade gets the rule that follows each session's own record of its writes. `--apply` alone never changes the plugin-managed hooks. **Then run `--apply` anyway**: hooks written by 1.8.4 through 1.10.0 read the sidecar before the enabled plugin, and older ones bake an install path, so reloading the plugin does not move them. `--apply` rewrites both onto the current resolver. `--apply` in plugin mode applies vault-side migrations (SCHEMA, `.hypoignore`), refreshes package metadata, and still syncs any vault extensions, but does **not** install the core hooks/commands/settings (the plugin provides those).
|
|
36
36
|
|
|
37
37
|
> **Note**: A major SCHEMA bump is only **detected** in this step. The informational `MIGRATION-vX.Y.md` file is written later by `--apply` (Step 4) and only on a major bump. `SCHEMA.md` is never auto-overwritten.
|
|
38
38
|
|
|
@@ -44,6 +44,15 @@ Show the output verbatim.
|
|
|
44
44
|
- `⚠` — minor update available (stale hook or missing settings entry)
|
|
45
45
|
- `✗` — major version bump or missing hook files (action required)
|
|
46
46
|
|
|
47
|
+
If a `⚠ hypo-guide.md` line shows a version change (`vN → vM`) or says the installed copy has no version stamp, `--apply` will not fix it: it never overwrites the vault's guide. Tell the user to finish it by hand, and offer to do the merge. A line that says the installed copy is ahead of the package, or that the versions cannot be compared, is not drift: leave the stamp alone and only report it.
|
|
48
|
+
|
|
49
|
+
1. Run the `diff` command printed on that line to compare the package template with the vault's `hypo-guide.md`.
|
|
50
|
+
2. Merge the changed parts into the vault copy and keep the user's own edits.
|
|
51
|
+
3. Set `version:` in the vault copy's frontmatter to the package version shown on that line.
|
|
52
|
+
4. Run the check again. The warning clears.
|
|
53
|
+
|
|
54
|
+
The stamp only records that the template was reviewed. It does not prove the contents match, so do not raise it before the merge is done.
|
|
55
|
+
|
|
47
56
|
For a **major SCHEMA bump**: warn the user that `--apply` will additionally write a `MIGRATION-vX.Y.md` informational file in their Hypomnema root and that they must manually merge the SCHEMA diff after applying.
|
|
48
57
|
|
|
49
58
|
---
|
|
@@ -68,6 +77,8 @@ If there is anything to update, ask the user:
|
|
|
68
77
|
|
|
69
78
|
After applying, recommend running `/hypo:doctor` to verify the installation is healthy.
|
|
70
79
|
|
|
80
|
+
On an npm install the same holds for a session that was already open before the upgrade: it has no record stamp (`record-stamp.json`), so its close keeps the old project-folder rule. If that blocks its close, end the session and open a new one.
|
|
81
|
+
|
|
71
82
|
---
|
|
72
83
|
|
|
73
84
|
## `--migrate-sessions` (a vault with a git remote)
|
|
@@ -82,7 +93,7 @@ This moves the vault from the tracked `hot.md` and `session-state.md` files to p
|
|
|
82
93
|
|
|
83
94
|
1. loses the session view in what it injects at session start,
|
|
84
95
|
2. stalls its whole sync if it holds an unpushed commit that edits `hot.md` or `session-state.md`, and
|
|
85
|
-
3. if it uses the plugin channel, is not refreshed on its own (run `/plugin marketplace update hypomnema`, then `/reload-plugins`).
|
|
96
|
+
3. if it uses the plugin channel, is not refreshed on its own (run `/plugin marketplace update hypomnema`, then `/reload-plugins` in every session that is already open; Step 2 says what to do when a session opened before the upgrade is still blocked at close).
|
|
86
97
|
|
|
87
98
|
**When to ask.** If the user typed `/hypo:upgrade --migrate-sessions` directly, run it without asking again: typing the flag is their statement that all machines are upgraded. Only when you are the one proposing this flag, or about to run it during another flow, first ask the user whether every machine that uses the same vault has been upgraded, because the command pushes the migration commit at once.
|
|
88
99
|
|