leerness 1.36.185 → 1.36.186
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/CHANGELOG.md +4 -0
- package/README.ko.md +5 -0
- package/README.md +9 -4
- package/bin/leerness.js +50 -3
- package/docs/state-paths-api.md +92 -0
- package/docs/state-scopes.md +264 -0
- package/lib/pure-utils.js +20 -0
- package/lib/state-git.js +138 -0
- package/lib/state-inspect.js +39 -0
- package/lib/state-inventory.js +69 -0
- package/lib/state-paths.js +59 -0
- package/lib/workspace-dir.js +22 -9
- package/package.json +6 -5
- package/scripts/command-flags-probe.js +1 -1
- package/scripts/false-claim-probe.js +3 -0
- package/scripts/file-lease-probe.js +1 -1
- package/scripts/state-inspect-cli-probe.js +209 -0
- package/scripts/state-scopes-probe.js +590 -0
- package/scripts/workspace-selection-probe.js +230 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.36.186 — 2026-09-05
|
|
4
|
+
|
|
5
|
+
- T-0174: add read-only five-scope state inspection with worktree/project isolation and honest legacy inventory; reuse workspace snapshots, skip unused npm skill discovery and bookkeeping, preserve all existing state.
|
|
6
|
+
|
|
3
7
|
## 1.36.185 — 2026-09-04
|
|
4
8
|
|
|
5
9
|
- T-0165/T-0166/T-0167/T-0171/T-0172: adds honest provider-capacity observation, exact-file TTL leases, strict Role/Agent/Routing v2 schemas, a fail-closed legacy role-store loader, and availability-aware role fallback with revision-bound execution provenance. Compatibility fields remain explicit, high-risk reviewer independence requires concrete model identities, corrupt state is preserved without provider execution, and all new surfaces are covered by adversarial regression probes.
|
package/README.ko.md
CHANGED
|
@@ -162,6 +162,11 @@ leerness mcp serve # JSON-RPC over stdio, 98개 도구
|
|
|
162
162
|
|
|
163
163
|
## 아키텍처 (외부 리뷰 검증)
|
|
164
164
|
|
|
165
|
+
`leerness state inspect . --json`으로 현재 상태 위치와 Project / Worktree /
|
|
166
|
+
Common-Control / Immutable-Record / Generated-View의 제안 경로를 확인할 수 있습니다.
|
|
167
|
+
읽기 전용 진단이며 데이터 이동·runtime 활성화·모델 호출은 하지 않습니다.
|
|
168
|
+
[진단 API](docs/state-paths-api.md)와 [단계별 구조 전환 계획](docs/state-scopes.md)을 참고하세요.
|
|
169
|
+
|
|
165
170
|
- **런타임 의존성 0 / install-script 0** — `package.json` 의 dependencies/optional/peer 가 전부 비어 있고 postinstall 도 없습니다. 순수 Node stdlib(`fs`/`path`/`child_process`/`readline`). 공급망 공격면 최소. `leerness install-safety` 로 확인 가능.
|
|
166
171
|
- **canonical JSON 단일 진실소스 + 마크다운 projection** — 메모리는 JSON 으로 저장하고 사람이 읽는 `.md` 는 파생물. 파이프(`|`)·개행·백틱·이모지·한글이 마크다운 테이블에서도 안전(셀 이스케이프 + round-trip).
|
|
167
172
|
- **원자적 UTF-8 쓰기** — temp + rename 으로 부분쓰기 손상 방지, BOM 자동 strip.
|
package/README.md
CHANGED
|
@@ -93,6 +93,11 @@ Resolution never silently executes a fallback. Each applied choice is bound to t
|
|
|
93
93
|
|
|
94
94
|
## Make it enforced, not optional
|
|
95
95
|
|
|
96
|
+
Before changing a multi-agent project's storage, inspect its actual workspace and proposed
|
|
97
|
+
Git-private/common scopes with `leerness state inspect . --json`. It is read-only: no data
|
|
98
|
+
migration, runtime activation, or provider calls. See [the state inspection API](docs/state-paths-api.md)
|
|
99
|
+
and [the staged architecture plan](docs/state-scopes.md).
|
|
100
|
+
|
|
96
101
|
By default leerness is **cooperative**: your AI agent runs the commands because CLAUDE.md / AGENTS.md tell it to. A determined agent could skip them. To turn the guideline into a guardrail:
|
|
97
102
|
|
|
98
103
|
```bash
|
|
@@ -156,7 +161,7 @@ MIT
|
|
|
156
161
|
<!-- leerness:project-readme:start -->
|
|
157
162
|
## Leerness Project Harness
|
|
158
163
|
|
|
159
|
-
이 프로젝트는 Leerness v1.36.
|
|
164
|
+
이 프로젝트는 Leerness v1.36.186 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
|
|
160
165
|
|
|
161
166
|
### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
|
|
162
167
|
|
|
@@ -210,7 +215,7 @@ leerness memory restore decision <date|title>
|
|
|
210
215
|
|
|
211
216
|
### MCP server (외부 AI 통합)
|
|
212
217
|
|
|
213
|
-
Leerness v1.36.
|
|
218
|
+
Leerness v1.36.186는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **98개 도구**를 노출:
|
|
214
219
|
|
|
215
220
|
```jsonc
|
|
216
221
|
// 카테고리별
|
|
@@ -231,7 +236,7 @@ Leerness v1.36.185는 stdio JSON-RPC MCP server를 내장합니다 — Claude Co
|
|
|
231
236
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
232
237
|
1) 다음 라운드 후보 선정 → 2) 코드 변경 → 3) 회귀 테스트 갱신 → 4) 전체 e2e 스위트 통과 → 5) npm publish + git tag → 6) main push → 7) session close → 8) 다음 라운드 예약.
|
|
233
238
|
|
|
234
|
-
현재 누적: **v1.9.x → 1.36.
|
|
239
|
+
현재 누적: **v1.9.x → 1.36.186 릴리스 태그 이력** (수백 라운드) · _reports/는 비공개 보존.
|
|
235
240
|
|
|
236
241
|
### 성능 가이드
|
|
237
242
|
|
|
@@ -269,5 +274,5 @@ leerness release pack --close --auto-main-push
|
|
|
269
274
|
- `.leerness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
|
|
270
275
|
- `.leerness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
|
|
271
276
|
|
|
272
|
-
Last synced by Leerness v1.36.
|
|
277
|
+
Last synced by Leerness v1.36.186: 2026-09-05
|
|
273
278
|
<!-- leerness:project-readme:end -->
|
package/bin/leerness.js
CHANGED
|
@@ -49,7 +49,7 @@ const {
|
|
|
49
49
|
migrateLegacyWorkspace,
|
|
50
50
|
} = require('../lib/workspace-dir');
|
|
51
51
|
|
|
52
|
-
const VERSION = '1.36.
|
|
52
|
+
const VERSION = '1.36.186';
|
|
53
53
|
|
|
54
54
|
// MCP lifecycle 주소 표식은 현재 CLI 호출 한 번에만 유효하다. CLI bootstrap에서 즉시 env에서
|
|
55
55
|
// 떼어 두어 `--no-record`/hook처럼 presence 기록 함수에 도달하지 않는 경로도 후속 child에 유출하지 않는다.
|
|
@@ -190,8 +190,8 @@ function _loadSkillCatalog() {
|
|
|
190
190
|
|
|
191
191
|
// 1.9.341 (UR-0025 심층): BUILTIN_CATALOG (9 내장 스킬 catalog) 는 lib/catalogs.js 로 이전 (import).
|
|
192
192
|
|
|
193
|
-
//
|
|
194
|
-
|
|
193
|
+
// Catalog initialization is below the parser declarations so metadata-only
|
|
194
|
+
// inspection can skip irrelevant skillpack discovery (including npm root -g).
|
|
195
195
|
|
|
196
196
|
const routes = {
|
|
197
197
|
planning: { read: ['.leerness/plan.md','.leerness/progress-tracker.md','.leerness/project-brief.md','.leerness/current-state.md','.leerness/guideline.md'], update: ['.leerness/plan.md','.leerness/progress-tracker.md','.leerness/current-state.md','.leerness/session-handoff.md'] },
|
|
@@ -882,8 +882,13 @@ function _errorUiLang(root, command) {
|
|
|
882
882
|
const parsed = nonFlagArgs();
|
|
883
883
|
const resolvedCommand = command || parsed[0];
|
|
884
884
|
if (resolvedCommand === 'mode' && has('--json')) return 'ko';
|
|
885
|
+
if (resolvedCommand === 'state' && parsed[1] === 'inspect') return _inspectionUiLang();
|
|
885
886
|
return _uiLang(resolvedCommand === 'mode' ? _modeRoot(parsed, root) : root);
|
|
886
887
|
}
|
|
888
|
+
function _inspectionUiLang() {
|
|
889
|
+
const language = String(arg('--language', process.env.LEERNESS_LANG || 'ko')).toLowerCase();
|
|
890
|
+
return language === 'en' ? 'en' : 'ko';
|
|
891
|
+
}
|
|
887
892
|
// ko/en 쌍에서 해석된 UI 언어로 선택 (Phase 1: 첫 화면 한정 사용).
|
|
888
893
|
function _tx(lang, ko, en) { return lang === 'en' ? en : ko; }
|
|
889
894
|
function fm(role, readWhen, updateWhen, body, lang = 'ko') {
|
|
@@ -11487,6 +11492,7 @@ function commandsCmd(root) {
|
|
|
11487
11492
|
{ cmd: 'roles list|set|unset|catalog|suggest|verify|validate', desc: '모델별 역할 부여 + fail-closed 저장소 검증 — 1.9.270/T-0171', descEn: 'assign roles and validate the role store fail-closed — 1.9.270/T-0171' },
|
|
11488
11493
|
{ cmd: 'capabilities [--json]', desc: '권한·보안 표면 공개 (무엇을 하는지 + opt-out + 주의 명령) — 1.9.272', descEn: 'disclose the permission/security surface (what it does + opt-outs + commands to watch) — 1.9.272' },
|
|
11489
11494
|
{ cmd: 'state show|start|record|verify|handoff', desc: '.leerness/ JSON 상태 substrate (에이전트 간 인수인계 표준) — 1.9.278', descEn: '.leerness/ JSON state substrate (cross-agent handoff standard) — 1.9.278' },
|
|
11495
|
+
{ cmd: 'state inspect [path] [--json]', desc: '5-scope 현재/제안 경로와 기존 상태 목록 — 읽기 전용, 이동·활성화 없음', descEn: 'five-scope current/proposed paths and legacy inventory — read-only, no migration or activation' },
|
|
11490
11496
|
{ cmd: 'adapter <tool>|list [--dry-run]', desc: '도구별 지침/.mcp.json 선택 생성 (claude/cursor/codex/goose/...) — 1.9.280', descEn: 'generate per-tool instructions/.mcp.json selectively (claude/cursor/codex/goose/...) — 1.9.280' },
|
|
11491
11497
|
{ cmd: 'ci init [path] [--force]', desc: 'PR 마다 leerness gate 실행하는 GitHub Actions 워크플로 생성 (.github/workflows/leerness-gate.yml) — 1.9.444', descEn: 'create a GitHub Actions workflow that runs leerness gate on every PR (.github/workflows/leerness-gate.yml) — 1.9.444' },
|
|
11492
11498
|
{ cmd: 'export|prompt --target <agent>', desc: 'adapter 별칭 — 도구별 지침/계약 파일 생성 (claude/cursor/codex/agents-md/...) — 1.9.448', descEn: 'adapter alias — generate per-tool instruction/contract files (claude/cursor/codex/agents-md/...) — 1.9.448' },
|
|
@@ -12181,6 +12187,7 @@ const _OBSERVATION_ONLY_SUBCOMMANDS = new Map([
|
|
|
12181
12187
|
['lease', new Set(['list', 'check'])],
|
|
12182
12188
|
['roles', new Set(['validate'])],
|
|
12183
12189
|
['role', new Set(['validate'])],
|
|
12190
|
+
['state', new Set(['inspect'])],
|
|
12184
12191
|
]);
|
|
12185
12192
|
function _cliMutationClass(args, cmd) {
|
|
12186
12193
|
if (cmd === 'mcp') return 'observation-only'; // server startup must not mutate its incidental cwd
|
|
@@ -15563,6 +15570,7 @@ const _STRICT_COMMAND_FLAGS = {
|
|
|
15563
15570
|
'lease list': { allowed: ['--session', '--all'], globals: [..._COMMON_COMMAND_FLAGS, '--json'], usage: 'lease list [--session <key>] [--all] [--json]' },
|
|
15564
15571
|
'lease check': { allowed: ['--session'], globals: [..._COMMON_COMMAND_FLAGS, '--json'], usage: 'lease check <file> [--session <key>] [--json]' },
|
|
15565
15572
|
'roles validate': { allowed: [], globals: [..._COMMON_COMMAND_FLAGS, '--json'], usage: 'roles validate [--path .] [--json]' },
|
|
15573
|
+
'state inspect': { allowed: [], globals: [..._COMMON_COMMAND_FLAGS, '--json'], usage: 'state inspect [path] [--path <path>] [--json]' },
|
|
15566
15574
|
'roles set': { allowed: ['--provider', '--to', '--model', '--model-family', '--policy', '--fallback-policy', '--candidate', '--candidate-family', '--remove-candidate', '--clear-candidates', '--persona', '--force'], globals: [..._COMMON_COMMAND_FLAGS, '--json'], usage: 'roles set <role> --provider <id> [--model <id>] [--policy strict|balanced|continuity]' },
|
|
15567
15575
|
'roles unset': { allowed: [], globals: [..._COMMON_COMMAND_FLAGS, '--json'], usage: 'roles unset <role> [--path .] [--json]' },
|
|
15568
15576
|
'roles suggest': { allowed: ['--apply'], globals: [..._COMMON_COMMAND_FLAGS, '--json'], usage: 'roles suggest [--apply] [--path .] [--json]' },
|
|
@@ -15644,6 +15652,13 @@ function _validateRolesCommandShape(cmd, args) {
|
|
|
15644
15652
|
}
|
|
15645
15653
|
|
|
15646
15654
|
function _validateCommandFlags(cmd, args) {
|
|
15655
|
+
if (cmd === 'state' && args[1] === 'inspect') {
|
|
15656
|
+
if (!_rejectDuplicateValueFlags(['--path'])) return false;
|
|
15657
|
+
if (args.length > 3) {
|
|
15658
|
+
failJson(has('--json'), 'too_many_arguments', 'Usage: state inspect [path] [--path <path>] [--json]');
|
|
15659
|
+
return false;
|
|
15660
|
+
}
|
|
15661
|
+
}
|
|
15647
15662
|
if (cmd === 'lease' && !_rejectDuplicateValueFlags(['--session', '--ttl', '--note', '--path'])) return false;
|
|
15648
15663
|
if (cmd === 'agents' && args[1] === 'route'
|
|
15649
15664
|
&& !_rejectDuplicateValueFlags(['--tier', '--approved-by', '--reason', '--path'])) return false;
|
|
@@ -15656,6 +15671,7 @@ function _validateCommandFlags(cmd, args) {
|
|
|
15656
15671
|
if (cmd === 'intent' && args[1] === 'expand') route = 'intent expand';
|
|
15657
15672
|
if (cmd === 'lease') route = `lease ${args[1] == null ? 'list' : args[1]}`;
|
|
15658
15673
|
if (roleSub) route = `roles ${roleSub === 'remove' || roleSub === 'rm' ? 'unset' : roleSub}`;
|
|
15674
|
+
if (cmd === 'state' && args[1] === 'inspect') route = 'state inspect';
|
|
15659
15675
|
const cfg = _STRICT_COMMAND_FLAGS[route];
|
|
15660
15676
|
if (!cfg) return true;
|
|
15661
15677
|
return _rejectUnknownFlags(cfg.allowed, cfg.usage, { globals: cfg.globals });
|
|
@@ -28893,6 +28909,21 @@ function _updateRun(root, id, mutator) {
|
|
|
28893
28909
|
}
|
|
28894
28910
|
// 1.9.328 (UR-0025): _splitList → lib/pure-utils.js 로 이동 (순수 콤마 리스트 분할, require 사용).
|
|
28895
28911
|
|
|
28912
|
+
// Inspect is intentionally independent of all legacy state loaders and writers.
|
|
28913
|
+
function stateInspectCmd(root) {
|
|
28914
|
+
try {
|
|
28915
|
+
const { inspectState, formatStateInspection } = require('../lib/state-inspect');
|
|
28916
|
+
const report = inspectState(root);
|
|
28917
|
+
if (has('--json')) return log(JSON.stringify(report, null, 2));
|
|
28918
|
+
// Do not read a project manifest merely to render a metadata-only diagnostic.
|
|
28919
|
+
return log(formatStateInspection(report, _inspectionUiLang()));
|
|
28920
|
+
} catch (error) {
|
|
28921
|
+
const known = error.name === 'StatePathError' || error.name === 'WorkspaceDirectoryError';
|
|
28922
|
+
return failJson(has('--json'), known ? error.code : 'state_inspection_failed',
|
|
28923
|
+
known ? error.message : 'State inspection failed; no files were changed.');
|
|
28924
|
+
}
|
|
28925
|
+
}
|
|
28926
|
+
|
|
28896
28927
|
// leerness state <show|start|record|verify|handoff>
|
|
28897
28928
|
// 1.9.292 (UR-0031): get_project_context — 외부 에이전트 온보딩용 단일 집약 컨텍스트.
|
|
28898
28929
|
// 1콜로 현재 작업/미답 요청/최근 결정/활성 룰/next-actions/memory/프로젝트 의도를 구조화 회수.
|
|
@@ -31793,6 +31824,7 @@ SETUP & UPDATE
|
|
|
31793
31824
|
|
|
31794
31825
|
STATUS & DIAGNOSTICS
|
|
31795
31826
|
status [path] Install status (files present)
|
|
31827
|
+
state inspect [path] [--json] Read-only five-scope paths/inventory; no migration or runtime activation
|
|
31796
31828
|
health [path] | doctor [--json] health: project state · doctor: CLI install/environment (no path arg)
|
|
31797
31829
|
verify [path] Required-file verification
|
|
31798
31830
|
which [--json] Resolve current binary/version (npm cache conflicts)
|
|
@@ -31874,6 +31906,7 @@ function help() {
|
|
|
31874
31906
|
log(`Leerness v${VERSION}\n\nUsage:\n leerness init [path] [--language auto|ko|en] [--skills recommended|all|a,b]\n leerness migrate [path] [--dry-run] [--force]\n leerness update [path] [--check|--yes|--force|--from <tarball>]\n leerness auto-update install [path]\n leerness status [path]\n leerness verify [path]\n leerness debug [path]\n leerness audit [path]\n leerness check [path]\n leerness scan secrets [path]\n leerness encoding check [path]\n leerness lazy detect [path]\n leerness memory search "query" [--limit 5]\n leerness handoff [path] [--all-apps] [--include p1,p2] [--since 24h|3d] [--compact] [--json] # 1.9.17-22 워크스페이스 (--compact: LLM 시스템 프롬프트용 1줄 요약)\n leerness orchestrate "<목표>" [--agents N] [--model qwen2.5:7b-instruct] [--retry-on-fail K] # 1.9.22 Ollama opt-in (LEERNESS_OLLAMA_BASE_URL 필요)\n leerness llm-bench record --score N --model X [--label L] [--tokens T] # 1.9.22 LLM 벤치 히스토리 누적\n leerness deps <capability> [--run-tests] [--json] # 1.9.24 depends-on 역방향 추적 + 자동 회귀 sweep\n leerness memory search "키" [--include-code] # 1.9.25 소스 코드 본문도 검색 (모순 감지 핵심)\n leerness brainstorm "주제" [--include-code] # 1.9.25 코드 본문 hits 포함\n leerness register-pending "<요청>" [--agent X] [--note Y] # 1.9.25 다중 세션 in-progress 즉시 등록\n leerness optimism-check <T-ID> [--json] # 1.9.26/27 낙관적 표시 감지 (1.9.27: 10 카테고리 + URL/메서드 매핑 + 신뢰도 점수)\n leerness persona list|show <id>|add <id> # 1.9.29 페르소나 카탈로그 (보안/성능/UX/testing/docs 5종 내장)\n leerness review <file> --persona <id1,id2,...> # 1.9.29 도메인 페르소나 리뷰 프롬프트 자동 생성\n leerness agents list|check|quota # 외부 AI CLI 다축 가용성 확인\n leerness agents resolve \"<task>\" --role <role> [--preset strict|balanced|continuity] # 역할 유지 + 대체 선택지\n leerness agents fallback provider|session|direct|hold \"<task>\" --role <role> [--session-provider P --session-model M --session-model-family F] # 명시 선택·기록\n leerness agents record completed|failed|reviewed|validated \"<summary>\" [--task T-ID --role R --to P --model M --model-family F --agent P --agent-model M --agent-model-family F --evidence E]\n leerness agents history [--limit 20] # 실행자·검수자·결과 provenance\n leerness agents dispatch \"<task>\" --to <id> # 활성 CLI 대상 실행 명령 생성 (실 호출 X, 사용자 실행)\n leerness agents multi "<task>" [--only c1,c2] [--write] [--execute] [--timeout 60] # 1.9.152/156 활성 N개 일괄 dispatch (--execute: 실 spawn + consensus)\n leerness provider list|add|remove [args] # 1.9.157 Provider Registry — 사용자 정의 CLI provider 동적 추가 (OpenRouter/Bedrock 흡수)\n leerness agents dispatch "<task>" --multi # 1.9.152 multi 모드 alias (또는 --to all)\n leerness setup-agents [path] [--yes|--no-setup-agents] # 1.9.32 sub-agent CLI 인터랙티브 설정 (.env + 미설치 자동 설치)\n leerness init [path] [--no-stale-check] # 1.9.33 npx 캐시 함정 — 옛 버전 자동 경고 (끄려면 --no-stale-check)\n leerness which [--json] # 1.9.164 진단: 현재 실행 경로/버전 + npm 캐시 + PATH 후보 (구버전 충돌 해결)\n leerness selftest [--json] # 1.9.258 코어 함수 무결성 자가 검증 (설치 손상/부분설치 감지, CI 친화 exit 1)\n leerness shell-guard "<command>" [--json] # 1.9.260 터미널 명령 셸 호환성 린터 (PowerShell 5.1 && 미지원 등 실행 전 감지, UR-0020)\n leerness shell-guard --record --cmd "..." --exit N # 1.9.260 실패한 터미널 명령 기록 → 다음 분석 시 회수\n leerness path-setup [--apply] [--json] # 1.9.254 leerness CLI PATH 자동 등록 (npm global bin 미등록 시)\n leerness web check|screenshot|extract <url> [--out file.png] [--selector "css"] # 1.9.165 playwright bridge (opt-in: npm i -g playwright + permissions.browser)\n leerness pc check|click|type|screenshot [--x N --y N] [--text "s"] [--out f.png] # 1.9.166 robotjs/nut-tree bridge (opt-in: npm i -g robotjs + permissions.mouse/keyboard, ⚠ full 모드 권장)\n leerness lsp check|symbols|references <file/name> [--in dir] [--json] # 1.9.167 LSP 어댑터 MVP (typescript opt-in + regex fallback, 코드 인텔리전스)\n leerness review-request "<request>" [--json] # 1.9.176 사용자 요청 사전 검토 (충돌/재사용/효율/권장 단계 — 사용자 명시)\n leerness contract verify <spec.md> <impl.js> [--json] # 1.9.35 명세 ↔ 구현 일치 검사 (함수/필드)\n leerness reuse autodetect [path] [--apply] [--json] # 1.9.35 src/*.js의 module.exports → reuse-map 후보 등록\n leerness audit [path] [--fix] # 1.9.35 --fix: session-handoff/current-state 자동 갱신\n leerness verify-claim <T-ID> ... [--strict-claims] # 1.9.26 verify-claim에 낙관적 표시 자동 검사 통합
|
|
31875
31907
|
leerness lens [${_lensDomainList()}] [--json] # 1.18.3/1.36.97 분야별 자기질문 품질 렌즈 (database·contract·recovery·observability 심화 · axes 8축 경량 — 완료 선언 전 자가 점검)\n leerness library [show|page] [path] [--json] [--ai] # 1.36.98 재사용 인벤토리 — 컴포넌트·디자인 토큰 추출 (page: 오프라인 HTML · --ai: 에이전트용 압축)\n leerness reuse-map [path] [--all-apps] [--include p1,p2] [--strict-elements] [--json] # 1.9.18 중복/잠재중복/depends-on\n leerness verify-claim <T-ID> [--path .] [--run-tests] [--json] # 1.9.18-20 evidence 자동 검증 (1.9.20: scenes/scripts 등 도메인 폴더 + jest/mocha 파싱)\n leerness verify-code [path] [--build] [--bench] # 1.9.20 --bench: scripts.bench 추가 실행 + evidence 누적\n leerness session close [path]\n leerness route <task-type>\n leerness self check [path]\n leerness readme sync [path]\n leerness consistency check [path]\n leerness consistency merge-design-guide [path]\n leerness plan show|init|add|drop|progress|sync [args]\n leerness task list|add|update|drop|fix-evidence|relink [args]\n leerness skill list|info <name>\n leerness skill learn <id> --doc <url> --command "..." --capability "..." [--note ...]\n leerness skill use <id> [--note ...]\n leerness skill optimize <id> --before "..." --after "..." [--note ...]\n leerness skill remove <id>\n leerness skill consolidate [--threshold 0.3]\n leerness gate [path] # verify+audit+scan+encoding+lazy
|
|
31876
31908
|
leerness lease acquire|release|list|check [file|lease-id] [--session KEY] [--ttl SEC] [--json] # exact-file opt-in 협업 lease
|
|
31909
|
+
leerness state inspect [path] [--json] # 5-scope 경로/목록 읽기 전용 진단 — 이동·활성화 없음
|
|
31877
31910
|
leerness retro [path] [--days 7] [--all-apps] [--include p1,p2] [--json] # 회고 (1.9.13~1.9.16)
|
|
31878
31911
|
leerness insights [path] [--all-apps] [--include p1,p2] [--json] # 누적 통계 (1.9.13~1.9.16)
|
|
31879
31912
|
leerness brainstorm "<주제>" [--all-apps] [--include p1,p2] [--json] # 브레인스토밍 (1.9.13~1.9.16)
|
|
@@ -31910,6 +31943,12 @@ async function main() {
|
|
|
31910
31943
|
if (!_rejectUnregisteredFlags(cmd)) return;
|
|
31911
31944
|
if (!_rejectMalformedFlagForms(cmd, args)) return;
|
|
31912
31945
|
if (!_validateCommandFlags(cmd, args)) return;
|
|
31946
|
+
// Exact early dispatch is both a no-write boundary and a latency boundary:
|
|
31947
|
+
// no workspace migration/guard rescans, usage locks, presence, or stale checks.
|
|
31948
|
+
if (cmd === 'state' && args[1] === 'inspect') {
|
|
31949
|
+
const explicit = arg('--path', null);
|
|
31950
|
+
return stateInspectCmd(explicit !== null ? explicit : args[2] === undefined ? process.cwd() : args[2]);
|
|
31951
|
+
}
|
|
31913
31952
|
// 값이 존재하지만 의미가 잘못된 --keep 도 legacy migration/write 전에 거부한다.
|
|
31914
31953
|
// preview serve 의 --keep 은 route별 boolean 계약이므로 숫자 검증 대상이 아니다.
|
|
31915
31954
|
if (!_commandBooleanValueFlags(cmd, args[1]).has('--keep')) {
|
|
@@ -33091,6 +33130,14 @@ async function main() {
|
|
|
33091
33130
|
return;
|
|
33092
33131
|
}
|
|
33093
33132
|
|
|
33133
|
+
const skillCatalog = (() => {
|
|
33134
|
+
const args = nonFlagArgs();
|
|
33135
|
+
if (require.main === module && args[0] === 'state' && args[1] === 'inspect') {
|
|
33136
|
+
return _withBuiltinSource(BUILTIN_CATALOG);
|
|
33137
|
+
}
|
|
33138
|
+
return _loadSkillCatalog();
|
|
33139
|
+
})();
|
|
33140
|
+
|
|
33094
33141
|
// 1.9.4 B: main 종료 후 exitCode를 명시적으로 process.exit으로 강제 (셸/wrapper 차 무시).
|
|
33095
33142
|
// 1.9.255 (UR-0019 2단계): require.main 가드 — `require('harness.js')` 시 main() 미실행 (init 부작용 차단 + 내부 함수 단위 테스트 가능).
|
|
33096
33143
|
// CLI 직접 실행 (node harness.js / npx leerness) 시에만 main() 호출. 기존 동작 100% 보존.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# State inspection API v1
|
|
2
|
+
|
|
3
|
+
Available in v1.36.186. T-0174 / P-0020 is an additive inspection-only foundation.
|
|
4
|
+
It does not move data or activate a new backend. The complete staged design is in
|
|
5
|
+
[State scopes and migration](state-scopes.md).
|
|
6
|
+
|
|
7
|
+
## CLI
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
leerness state inspect
|
|
11
|
+
leerness state inspect ./project --json
|
|
12
|
+
leerness state inspect --path ./project --language en
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The target must be an existing directory. `--path` wins over the optional positional
|
|
16
|
+
target; otherwise the target is the current directory. Blank paths, extra positionals,
|
|
17
|
+
duplicate `--path`, unknown options and unsupported write flags fail before discovery.
|
|
18
|
+
Text defaults to Korean; `--language` or `LEERNESS_LANG` chooses English/Korean without
|
|
19
|
+
reading a project manifest. JSON is one document, with `ok: true` on success, or the
|
|
20
|
+
standard `ok: false` / `code` / `error` envelope and exit 1 on failure.
|
|
21
|
+
|
|
22
|
+
## Module export
|
|
23
|
+
|
|
24
|
+
- `resolveStatePaths(root, options)` from `lib/state-paths.js`: read-only resolution;
|
|
25
|
+
default root is cwd. `options.env` supplies the child Git environment and workspace
|
|
26
|
+
override; `options.envValue` overrides workspace selection. Neither is persisted.
|
|
27
|
+
|
|
28
|
+
`inspectState(root, options)` from `lib/state-inspect.js` adds `ok` and the fixed-list
|
|
29
|
+
inventory; `formatStateInspection(report, language)` renders the already resolved
|
|
30
|
+
snapshot without more I/O. The resolver does not initialize the CLI or discover skills.
|
|
31
|
+
|
|
32
|
+
## Fields
|
|
33
|
+
|
|
34
|
+
- schemaVersion: 1.
|
|
35
|
+
- schema: `leerness.state-paths/v1` (inspection: `leerness.state-inspection/v1`).
|
|
36
|
+
- activeLayout: `legacy`; existing writers remain authoritative.
|
|
37
|
+
- runtimeActivated: false.
|
|
38
|
+
- migrationAvailable: false.
|
|
39
|
+
- projectRoot: canonical absolute directory of the requested project, not necessarily the Git root.
|
|
40
|
+
- projectKey: `project-` plus SHA-256 of the canonical repository-relative project path.
|
|
41
|
+
- projectRelativePath: forward-slash relative path, or `.` for a repository root/non-Git project.
|
|
42
|
+
- git: absolute worktreeRoot/gitDir/gitCommonDir and linkedWorktree, or null for genuine non-Git.
|
|
43
|
+
- workspace: selected name/path, canonical path, detectedPaths, legacy presence and recognition status.
|
|
44
|
+
- scopes: exactly project, worktree, commonControl, immutableRecord and generatedView.
|
|
45
|
+
- warnings: explicit non-Git, unrecognized/foreign and legacy-workspace observations.
|
|
46
|
+
|
|
47
|
+
All `proposedPath` / `proposedPaths` values are future addresses, **not created, activated,
|
|
48
|
+
validated as writable, or authorization to write**. Project scope separately reports
|
|
49
|
+
`currentPath`. Inventory rows report the actual legacy path, classification, proposed
|
|
50
|
+
semantic scope, migrationAvailable=false and lstat metadata (including links/errors).
|
|
51
|
+
They are not a move plan. Mixed task/context/queue documents must be split by field;
|
|
52
|
+
human intent is not disposable generated state.
|
|
53
|
+
|
|
54
|
+
Inventory covers the selected path plus any other existing canonical/legacy workspace,
|
|
55
|
+
with workspacePath and selectedWorkspace on each row. A legacy reader selection must not
|
|
56
|
+
hide canonical runtime, nor may a canonical selection hide residual legacy evidence.
|
|
57
|
+
Text output prefixes rows with the workspace directory name; contents are never merged.
|
|
58
|
+
|
|
59
|
+
Project identity is independent of branch name and session. Matching repository-relative
|
|
60
|
+
projects in linked worktrees share a common-control proposal, but never private runtime.
|
|
61
|
+
Siblings differ. Separate clones/hosts do not share live control. Non-Git proposals use
|
|
62
|
+
project-local `cache/state-runtime`, with commonControl unavailable, not an invented shared store.
|
|
63
|
+
These keys are namespaces, not credentials. Moving a project inside a repo changes its key.
|
|
64
|
+
|
|
65
|
+
## Failures and limits
|
|
66
|
+
|
|
67
|
+
Path errors distinguish invalid/not-found/not-directory/unreadable targets. Git errors
|
|
68
|
+
distinguish missing, unsupported, bare, metadata-directory, timeout, output-limit,
|
|
69
|
+
unreadable, ambiguous output and repository discovery failure. Filesystem
|
|
70
|
+
errors in Git-returned paths or the discovery marker walk remain Git-specific;
|
|
71
|
+
they never misreport an existing inspection target as missing. Existing workspace
|
|
72
|
+
conflict/override/link errors retain their codes; strict inspection adds workspace_unreadable.
|
|
73
|
+
Git failure never silently falls back to a different backend. Existing discovery ceilings
|
|
74
|
+
are respected; nonexistent ceilings are ignored as Git does. Git location/config injection
|
|
75
|
+
overrides are sanitized through the existing gateway, not through a duplicate subprocess wrapper.
|
|
76
|
+
|
|
77
|
+
One Git topology query has a 5-second process timeout and 64-KiB output cap (Windows executable
|
|
78
|
+
discovery is a separate bounded gateway step). Git must support `--path-format=absolute`.
|
|
79
|
+
Workspace discovery reads immediate directory names; inventory uses a fixed set of leaves
|
|
80
|
+
and cached parent metadata without contents or recursive traversal. Links are not followed
|
|
81
|
+
for inventory. This is an observational, non-transactional filesystem snapshot; it is not
|
|
82
|
+
record validation, tracking detection, filesystem locking or a migration-integrity certificate.
|
|
83
|
+
|
|
84
|
+
## Verification
|
|
85
|
+
|
|
86
|
+
`npm run test:state-scopes` runs selector equivalence, topology/metadata and real CLI tests.
|
|
87
|
+
The CLI test instruments normal execution without internal/no-migration/no-stale bypasses:
|
|
88
|
+
one Git query, no npm/provider command, no project content read, no write; it also compares
|
|
89
|
+
all fixture/cwd/Git file bytes and mtimes. Windows may perform trusted Git location and
|
|
90
|
+
console encoding operations. No new runtime dependency, persistent config or MCP tool is added.
|
|
91
|
+
Invalid option diagnostics are instrumented too: neither target nor unrelated cwd
|
|
92
|
+
manifest contents are read merely to choose an error language.
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# State scopes and migration design
|
|
2
|
+
|
|
3
|
+
Status: scope inspection implemented; runtime architecture remains proposed (2026-09-05).
|
|
4
|
+
Request: UR-0097; implementation/optimization approval: UR-0098. Audit: T-0173.
|
|
5
|
+
First implementation: T-0174 / approved P-0020. See [the inspection API](state-paths-api.md).
|
|
6
|
+
|
|
7
|
+
**Only path resolution and `state inspect` are implemented; the stores and migration below are designs.**
|
|
8
|
+
The first implementation covers path resolution and read-only inspection only. Existing writers,
|
|
9
|
+
data locations, task IDs, CLI/MCP contracts and approval gates remain unchanged until an
|
|
10
|
+
explicitly approved migration passes its compatibility tests.
|
|
11
|
+
|
|
12
|
+
## 1. Evidence from the current implementation
|
|
13
|
+
|
|
14
|
+
Audited baseline: v1.36.185, commit `c84ab03b5b5d385985ee2797a95c02c7d4004be4`.
|
|
15
|
+
|
|
16
|
+
| Surface | Existing source / storage | Structural consequence |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Project directory | `lib/workspace-dir.js` resolves `.leerness` and migrates `.harness` | The baseline had no scope resolver. T-0174 adds one without replacing this migration; a pure selector reuses the existing inspection snapshot. |
|
|
19
|
+
| Session presence | `lib/session-presence.js` predicates and `bin/leerness.js` I/O use `.leerness/cache/sessions/` | Sessions have addresses and ignored runtime files already, but different worktrees cannot see each other's presence through this store. |
|
|
20
|
+
| Handoff freshness | `bin/leerness.js`: `.leerness/cache/handoffs/` | Session-bound freshness markers are consumed by handoff/enforcement; migrate those consumers together. |
|
|
21
|
+
| REPL conversation sessions | `bin/leerness.js`: `.leerness/cache/agent-sessions/<sessionId>.jsonl` | This is a separate local conversation surface, not presence. Do not promote raw conversations into public audit records. |
|
|
22
|
+
| Source-file lease | `lib/file-leases.js`: `.leerness/cache/file-leases.json` | Exact-file TTL coordination is local to a checkout; it is not a repository-wide task claim. |
|
|
23
|
+
| State substrate | `bin/leerness.js`: `.leerness/state.json`, `runs/run-N.json`, `handoff/` | Session ownership fixes same-checkout cross-talk, but counters and mutable run files remain local branch files rather than globally unique completion records. |
|
|
24
|
+
| Decisions and lessons | `_saveDecisions` / `_saveLessons`: JSON arrays plus Markdown projections | Valid JSON arrays take precedence. Current readers silently fall back to Markdown for invalid JSON/non-arrays; migration must not. Both the array and its projection are rewritten, so independent records still change the same tracked files on separate branches. |
|
|
25
|
+
| Task progress | `readProgressRows` / `writeProgressRows`: `.leerness/progress-tracker.md` | The table is currently authoritative, not an event-derived view. Reclassifying it without migrating all readers and writers would lose task updates. |
|
|
26
|
+
| Session close | `lib/session-close.js` reads task rows, writes handoff, updates auto-marked current-state lines, invokes further bookkeeping | File locks serialize local writes, not Git merges or a whole multi-file snapshot. The first in-progress task selects the recommendation, which need not be the current session's task. |
|
|
27
|
+
| Execution provenance | `lib/role-fallback.js`: `.leerness/execution-ledger.jsonl` | Revision-bound role/fallback evidence exists, but this checkout ignores the ledger. It is not a durable Git audit record. |
|
|
28
|
+
| Runtime-like tracked files | `git ls-files` includes `active-wakeups.json`, `auto-resume-plan.json`, `next-action-queue.json`, `pre-wake-report.json`, `last-handoff.json`, `routing-log.json` | Ignoring `cache/` alone does not remove mutable operational state from merge inputs. Classification must be per surface, not extension-based. |
|
|
29
|
+
| Integrity checks | `lib/state-integrity.js` inspects only immediate `.leerness/*.json` parseability | Moving to nested stores needs explicit schema/record coverage; a green legacy scan cannot attest to new scopes. |
|
|
30
|
+
|
|
31
|
+
Some fields mix intent with execution: a queued next action or resume plan may contain
|
|
32
|
+
user-authored instructions. Preserve those as project/task inputs, and move only execution
|
|
33
|
+
status/cursors to runtime. Never classify an entire mixed file as disposable cache.
|
|
34
|
+
|
|
35
|
+
## 2. Three storage layers, five semantic scopes
|
|
36
|
+
|
|
37
|
+
The three physical layers are **versioned project data**, **worktree-private execution**,
|
|
38
|
+
and **repository-common coordination**. Immutable-Record and Generated-View are semantic
|
|
39
|
+
scopes within that topology, not two additional database servers.
|
|
40
|
+
|
|
41
|
+
| Scope | Proposed location | Authority and writer |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| Project | `<project>/.leerness/` and optional `config/` | Versioned policy, role definitions, project identity, task specifications and human-authored context; explicit project mutations only. |
|
|
44
|
+
| Worktree | `<gitDir>/leerness/projects/<projectKey>/runtime/` | Per-session/per-run execution state. One owner per mutable session record; no branch name as identity. |
|
|
45
|
+
| Common-Control | `<gitCommonDir>/leerness/control/projects/<projectKey>/` | Claims, leases, dependencies, registered worktrees, fencing revisions and run status. All writes pass the same ControlStore transaction boundary. |
|
|
46
|
+
| Immutable-Record | `<project>/.leerness/memory/{decisions,lessons}/` and `.leerness/records/runs/<runId>/` | Individual durable knowledge/result/review records. Exclusive creation; corrected decisions link `supersedes`, never silently overwrite. |
|
|
47
|
+
| Generated-View | `.leerness/generated/` for canonical projections; private runtime `views/` for live session views | Deterministic output with input digests and schema/generator versions. Canonical summary is written only by the consolidator. Live view is local and ignored. |
|
|
48
|
+
|
|
49
|
+
The main worktree commonly has `gitDir === gitCommonDir`. Distinct `runtime/` and
|
|
50
|
+
`control/` namespaces are therefore mandatory even there. A session subdirectory is still
|
|
51
|
+
required: two agents can operate in one worktree.
|
|
52
|
+
|
|
53
|
+
The audited repository confirmed the linked checkout's Git directory ends in
|
|
54
|
+
`.git/worktrees/t0165-convergence`, while the main checkout and both common-directory
|
|
55
|
+
queries resolve to the main `.git`. This matches the [Git worktree documentation](https://git-scm.com/docs/git-worktree#_details).
|
|
56
|
+
Use Git's [absolute path queries](https://git-scm.com/docs/git-rev-parse#_options_for_files),
|
|
57
|
+
not string concatenation with `<project>/.git`, which can be a gitfile.
|
|
58
|
+
|
|
59
|
+
### Identity and unsupported environments
|
|
60
|
+
|
|
61
|
+
- Resolve topology through the existing `lib/git.js` environment-sanitizing, shell-free
|
|
62
|
+
gateway. Resolve the worktree root separately from the Leerness project root.
|
|
63
|
+
- Preserve existing workspace markers and foreign-directory detection. A directory with
|
|
64
|
+
only new `config/memory/records/generated` children is not automatically recognized by
|
|
65
|
+
today's workspace classifier; a versioned marker/compatibility update is required.
|
|
66
|
+
- P-0020 uses a deterministic project key derived from the canonical repository-relative
|
|
67
|
+
project path (`.` for the root); identical subprojects across linked worktrees match,
|
|
68
|
+
sibling monorepo projects do not. A future explicit project ID can preserve identity
|
|
69
|
+
across project moves. Neither ID is a credential or an authorization boundary.
|
|
70
|
+
- Resolve Windows case/alias/junction and symlink containment before writing. Reject
|
|
71
|
+
ambiguous mappings instead of silently sharing or relocating another project's state.
|
|
72
|
+
- Bare repositories, broken gitfiles, permission failures, unsupported Git and Git absence
|
|
73
|
+
must be distinguishable. Git errors do not silently select a different storage backend.
|
|
74
|
+
- True non-Git projects retain an explicitly labelled, ignored project-local runtime
|
|
75
|
+
fallback; they have no cross-worktree coordination guarantee. Inspection creates nothing.
|
|
76
|
+
- Submodules use their own Git topology. Separate clones and separate hosts do not share
|
|
77
|
+
`gitCommonDir`; remote coordination is out of scope. Network filesystems need a separate
|
|
78
|
+
locking guarantee before they are supported for common control.
|
|
79
|
+
|
|
80
|
+
## 3. Module boundaries and reuse
|
|
81
|
+
|
|
82
|
+
The inspection modules exist now. Store/facade/view names below remain planned contracts:
|
|
83
|
+
|
|
84
|
+
| Module | Responsibility | Existing component to reuse |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `lib/state-paths.js`, `lib/state-git.js` (implemented) | Five-scope map, canonical project namespace and read-only Git topology; no activation | `lib/git.js`, one `lib/workspace-dir.js` snapshot + pure selection without migration |
|
|
87
|
+
| `lib/state-inventory.js`, `lib/state-inspect.js` (implemented) | Fixed known-surface metadata inventory and CLI JSON/text projection; no recursive content scan | Existing workspace discovery; CLI exact early dispatch skips usage/migration/stale checks and unused skillpack/npm discovery |
|
|
88
|
+
| `lib/state-manager.js` | Narrow orchestration facade, schema dispatch, explicit migration status; no giant all-purpose store | Existing CLI/MCP adapters delegate to this facade incrementally |
|
|
89
|
+
| `lib/worktree-state.js` | Addressed sessions, runs, handoffs and resumable execution; explicit ownership | Session key and freshness contracts in `lib/session-presence.js`; existing run ownership rules |
|
|
90
|
+
| `lib/control-store.js` | Repository-scoped task claim, revision CAS and fencing; transaction ordering | Existing lock primitives after common-root/multi-process validation; no duplication of Git execution |
|
|
91
|
+
| `lib/memory-store.js` | Strict, bounded immutable record load/create; supersession/index projection | Existing decision/lesson parsers and renderers as legacy import adapters |
|
|
92
|
+
| `lib/run-record.js` | Validate execution/review envelopes, finalize and durable receipt | `lib/role-agent-schema.js`, `lib/role-store.js`, `lib/role-fallback.js` provenance contracts |
|
|
93
|
+
| `lib/state-views.js` | Pure deterministic rendering from an explicitly consistent snapshot | Existing Markdown renderers and dashboard serialization, preserving handwritten text |
|
|
94
|
+
|
|
95
|
+
Dependency direction: CLI/MCP → StateManager → domain stores / views → path, Git and I/O
|
|
96
|
+
primitives. Read-only topology inspection must not call initialization, workspace migration,
|
|
97
|
+
telemetry, presence registration, lease creation or providers. Avoid reusing a convenient
|
|
98
|
+
helper whose discovery path has write side effects.
|
|
99
|
+
|
|
100
|
+
Runtime dependencies remain zero, Node >=18. Start with bounded JSON records and existing
|
|
101
|
+
file-lock primitives, not a new YAML parser or SQLite package. `ControlStore` is a storage
|
|
102
|
+
interface; a database backend is a future evidence-driven option, not an MVP dependency.
|
|
103
|
+
|
|
104
|
+
## 4. Concurrency and conflict rules
|
|
105
|
+
|
|
106
|
+
1. A session owns its mutable runtime files; other sessions submit records/messages through
|
|
107
|
+
a store operation, never directly edit its progress or handoff.
|
|
108
|
+
2. Runtime is not a Git merge input. Moving files alone is insufficient: migrate ignore
|
|
109
|
+
rules, old writers, hooks, telemetry, integrity checks, packaging and cleanup too.
|
|
110
|
+
3. Persistent knowledge is record-oriented. Use built-in random UUIDs or an equivalently
|
|
111
|
+
collision-resistant scheme, not a branch-local maximum counter. Preserve legacy IDs as
|
|
112
|
+
qualified import metadata; never merge two unrelated `T-0174`/`run-0001` by label alone.
|
|
113
|
+
4. Same ID + same canonical payload is an idempotent replay. Same ID + different payload is
|
|
114
|
+
`record_id_conflict` and changes no records. Create atomically without overwrite.
|
|
115
|
+
5. Generated-file conflicts are resolved by regenerating from accepted inputs. Preserve
|
|
116
|
+
human-written sections as project context before deprecating old mixed documents.
|
|
117
|
+
6. Semantic decision disagreement creates a separate decision/review, not `merge=union`.
|
|
118
|
+
7. A single logical consolidator publishes canonical project summaries. A role label alone
|
|
119
|
+
is insufficient: validate owner, lease generation and expected revision at publication.
|
|
120
|
+
Expired writers cannot publish after a successor acquires ownership (fencing).
|
|
121
|
+
8. Physical source-file leases and repository task claims are distinct. Two worktrees can
|
|
122
|
+
edit their separate copies of the same path. Intentional duplicate task execution is
|
|
123
|
+
coordinated by a repository task key; source leases remain keyed by physical identity.
|
|
124
|
+
9. Common control transactions cannot rely on a thread holding a lock elsewhere. Define
|
|
125
|
+
lock ordering, bounded critical sections, stale-owner recovery and byte/row limits;
|
|
126
|
+
verify concurrent acquisition and crash recovery with real processes.
|
|
127
|
+
Existing lock ownership must not be stolen solely because a PID looks absent or a TTL
|
|
128
|
+
expires. A new task-claim lease and a local write mutex have different recovery contracts.
|
|
129
|
+
10. Visibility is not authority: provider observations can be unknown or stale. A role or
|
|
130
|
+
policy revision change requires revalidation; cross-worktree policy divergence cannot
|
|
131
|
+
be resolved by taking whichever branch updated control most recently.
|
|
132
|
+
|
|
133
|
+
Presence currently records handoff/close observations, not a heartbeat protocol or proof
|
|
134
|
+
that a process is alive. Common control must distinguish observed presence, a held lease,
|
|
135
|
+
an expired heartbeat and unknown availability. Retain existing session identity validation.
|
|
136
|
+
|
|
137
|
+
## 5. Run/review provenance and finalize
|
|
138
|
+
|
|
139
|
+
Each run has separate implementer, reviewer and terminal result records. Fields include:
|
|
140
|
+
schema version, globally unique run/task/agent identity, role, requested and actual
|
|
141
|
+
provider/model, observed identity source, resolution/fallback chain and reasons,
|
|
142
|
+
role/routing/availability revisions, branch/base/source commit, changed-file or diff digest,
|
|
143
|
+
commands with exit results, tests/evidence references, issues, timestamps, and next role.
|
|
144
|
+
Unknown model identity is explicitly unknown; never claim a specific model because it was
|
|
145
|
+
requested. Existing strict high-risk review-independence policy remains intact.
|
|
146
|
+
|
|
147
|
+
A review binds to the exact reviewed source commit (or explicitly identified uncommitted
|
|
148
|
+
diff digest). Approval of one commit does not approve a later commit or a conflict-resolved
|
|
149
|
+
merge. Dirty work is not represented as clean `HEAD` evidence. Record source commit and
|
|
150
|
+
publication commit separately to avoid a self-referential commit hash inside its own record.
|
|
151
|
+
|
|
152
|
+
Proposed lifecycle:
|
|
153
|
+
|
|
154
|
+
```text
|
|
155
|
+
running → execution-terminal → review/gate evaluated → finalize-prepared
|
|
156
|
+
→ immutable records durable → integrated/accepted → cleanup-eligible
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`failed`, `blocked` and `rejected` are valid terminal records; persisting them is not a
|
|
160
|
+
successful task-completion claim. A record on an unmerged branch is provisional evidence,
|
|
161
|
+
not canonical accepted project state. Consolidation uses accepted records from the selected
|
|
162
|
+
integration revision plus an explicit control snapshot, not arbitrary branch-local files.
|
|
163
|
+
|
|
164
|
+
Proposed `finalize` must be bounded, idempotent and crash-recoverable: validate owner/revision,
|
|
165
|
+
freeze input digests, persist records, verify them by reading back, then update the control
|
|
166
|
+
receipt. A missing/different record or changed source revision prevents cleanup. Do not mark
|
|
167
|
+
finalized before the durable writes succeed. Recovery uses the journal/receipt to complete
|
|
168
|
+
or reject a partial attempt without recreating a different result under the same ID.
|
|
169
|
+
|
|
170
|
+
**A file written in a worktree is not yet safe against worktree removal.** Cleanup eligibility
|
|
171
|
+
requires records/evidence referenced by an accepted retained Git revision (or an explicitly
|
|
172
|
+
configured durable store); merely staged/untracked records are insufficient. Local ignored
|
|
173
|
+
logs must be promoted or have their missing evidence represented before that receipt.
|
|
174
|
+
|
|
175
|
+
Leerness can enforce finalize on its own adapter cleanup path, not intercept arbitrary
|
|
176
|
+
external `git worktree remove --force`, manual deletion or `git prune`. There is no promise
|
|
177
|
+
of an unbypassable Git removal hook. Worktree locks may reduce accidental pruning but are
|
|
178
|
+
not a substitute for finalization and user-authorized cleanup. Never delete a worktree or
|
|
179
|
+
its metadata as a side effect of inspect, handoff or migration.
|
|
180
|
+
|
|
181
|
+
## 6. Staged delivery and migration order
|
|
182
|
+
|
|
183
|
+
| Stage | Tracking | Deliverable / gate |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| Audit and design | T-0173 | Actual state map, reuse inventory, structural risks, compatibility order and preview; no runtime activation. |
|
|
186
|
+
| Scope foundation | M-0014 / T-0174 / P-0020 | Resolver and `state inspect` read-only CLI contract; current/proposed locations distinguished; no stores moved and no new execution adapter. |
|
|
187
|
+
| Private runtime | M-0015 / T-0175 | Explicit inventory/preview/confirm migration of session/runtime surfaces; per-session ownership; interrupted migration recovery. |
|
|
188
|
+
| Common control | M-0016 / T-0176 | Task identity, claims, owner generations, version/revision compatibility and bounded ControlStore transactions. |
|
|
189
|
+
| Durable records | M-0017 / T-0177 | Append-only memory import, run/review records and finalize durability receipts; old evidence retained. |
|
|
190
|
+
| Views and adapters | M-0018 / T-0178 | Accepted-input snapshot, single-writer summaries, integrity/claims/CLI/MCP/cleanup integration; legacy window closed only with evidence. |
|
|
191
|
+
|
|
192
|
+
M-0010's existing v2 validators stay usable and legacy runtime writes stay authoritative.
|
|
193
|
+
Finalize its storage/migration design against M-0014 before implementing another independent
|
|
194
|
+
path migration. Runtime activation must also satisfy M-0015 compatibility and M-0016 control
|
|
195
|
+
contracts. M-0011 dispatch, M-0012 live visualization and M-0013 UI consume the same State API.
|
|
196
|
+
Do not prematurely move the already documented role files into `config/`: filename aliases,
|
|
197
|
+
CLI/MCP compatibility and the migration manifest must be specified first.
|
|
198
|
+
|
|
199
|
+
Migration rules:
|
|
200
|
+
|
|
201
|
+
- Inventory all writers/readers and classify fields, not just filenames. Snapshot original
|
|
202
|
+
bytes, inventory digest, source schema and Git tracking before asking to apply.
|
|
203
|
+
- Legacy memory import must distinguish missing JSON from corrupt/invalid JSON. Existing
|
|
204
|
+
permissive loaders may fall back to stale Markdown; reuse parsers/renderers only behind a
|
|
205
|
+
strict source selection/validation boundary. Invalid canonical data stops migration and
|
|
206
|
+
preserves all original bytes, rather than silently promoting a Markdown fallback.
|
|
207
|
+
- Quiesce all participating writers; a new lock cannot stop an old CLI that does not know it.
|
|
208
|
+
Detect active legacy clients and refuse activation; do not claim mixed-version safety
|
|
209
|
+
from a new manifest alone. Old clients must be upgraded/stopped before the boundary moves.
|
|
210
|
+
- Stage copied data and validate digests, IDs, counters and provenance before activating one
|
|
211
|
+
versioned manifest. Use single-authority writes; do not create two independent writable
|
|
212
|
+
JSON/Markdown or legacy/v2 truths during a compatibility window.
|
|
213
|
+
- Preserve originals as explicitly labelled legacy archives. Never blanket-untrack or delete
|
|
214
|
+
`.leerness`. Index/ignore changes and project instructions are part of an approved migration.
|
|
215
|
+
- Pre-activation rollback preserves bytes. Post-activation rollback must first reconcile
|
|
216
|
+
newer events/records; copying an old backup over new writes is not rollback.
|
|
217
|
+
- Default handoff and inspect remain tracked-file-read-only. Explicit project memory,
|
|
218
|
+
consolidation and finalization are the durable-write boundaries.
|
|
219
|
+
|
|
220
|
+
## 7. Acceptance matrix
|
|
221
|
+
|
|
222
|
+
- Main + two real linked worktrees: private paths differ, common paths match, sibling
|
|
223
|
+
subprojects remain separate; branch switch/detached HEAD do not change runtime identity.
|
|
224
|
+
- Same worktree, two sessions: no current-run/evidence cross-talk; absent/invalid explicit
|
|
225
|
+
session identity fails safely for concurrent mutation instead of silently borrowing ownership.
|
|
226
|
+
- Non-Git, Git missing, bare, broken gitfile, moved worktree, submodule, spaces, Unicode,
|
|
227
|
+
Windows alias/case/junction, permission errors and inherited Git location overrides.
|
|
228
|
+
- Inspect/show: byte and mtime snapshots of project and Git metadata remain identical, no
|
|
229
|
+
cache/usage/salt/lease/lock creation, no subprocess provider calls, bounded stdout/JSON errors.
|
|
230
|
+
- Multi-process claim/lease races, owner expiry/replacement, clock movement, duplicate IDs,
|
|
231
|
+
truncated JSONL, corrupt/future schema, failed writes and interruption at every commit point.
|
|
232
|
+
- Independent records merge without shared counter edits; conflicting IDs/policy revisions
|
|
233
|
+
fail closed; decision disagreement stays explicit; generated views reproduce from digests.
|
|
234
|
+
- Finalize replay, record durability failure, stale review commit, rejected review, failed
|
|
235
|
+
tests, missing provenance, evidence outside retained Git and worktree-removal preflight.
|
|
236
|
+
- Legacy fixtures, handoff/MCP presence, file-lease and role-fallback probes plus installed
|
|
237
|
+
cleanroom and supported Node/Windows/POSIX release tests before enabling new runtime writes.
|
|
238
|
+
|
|
239
|
+
## Inspection optimization and verification boundaries
|
|
240
|
+
|
|
241
|
+
Each valid inspection makes one Git topology query through the existing gateway. On Windows
|
|
242
|
+
the gateway also runs the trusted System32 executable locator; the normal console encoding
|
|
243
|
+
bootstrap is unchanged. Inspection skips unrelated `npm root -g` skillpack discovery and
|
|
244
|
+
does not call providers, register presence, take usage locks, or write caches.
|
|
245
|
+
Workspace discovery enumerates the two immediate workspace directories once. Inventory covers
|
|
246
|
+
known leaves in both existing locations (selected/unselected are explicit), with cached parent
|
|
247
|
+
metadata: it does not read state contents,
|
|
248
|
+
enumerate runtime descendants, query Git tracking, or certify record/schema integrity.
|
|
249
|
+
No long-lived path cache is used: moving a worktree or changing the target refreshes topology.
|
|
250
|
+
|
|
251
|
+
`npm run test:state-scopes` checks selector compatibility, real main/two linked worktrees,
|
|
252
|
+
project namespaces, moved/detached worktrees, actual submodule topology, aliases, Git failure
|
|
253
|
+
taxonomy, and CLI byte/mtime preservation with normal bookkeeping enabled. The scope probe
|
|
254
|
+
also passed on Node 18/Windows; POSIX execution remains a separate CI check, not implied by it.
|
|
255
|
+
Later concurrency/finalize/adapter acceptance bullets above do not apply to the implemented
|
|
256
|
+
inspection-only stage and are not reported complete by its tests.
|
|
257
|
+
|
|
258
|
+
## Not included in P-0020
|
|
259
|
+
|
|
260
|
+
No automatic migration, control database, task dispatch, paid provider calls, source lease
|
|
261
|
+
relocation, decision import, finalize command, worktree deletion or dashboard is implied by
|
|
262
|
+
the path-inspection preview. Existing review/release rules still apply to implementation.
|
|
263
|
+
Later stages require their own
|
|
264
|
+
implementation scope and verification; this design does not mark them complete.
|
package/lib/pure-utils.js
CHANGED
|
@@ -275,6 +275,23 @@ function _parseSlashFromHelp(text, invoke = 'slash') {
|
|
|
275
275
|
// 1.9.283 (UR-0025 2단계): 권한 등급(permission tiers) 순수 로직 — capabilities/policy 공유.
|
|
276
276
|
const PERMISSION_TIERS = ['read-only', 'safe-write', 'project-write', 'shell-read', 'shell-write', 'git-write', 'network', 'publish'];
|
|
277
277
|
function _tierRank(t) { const i = PERMISSION_TIERS.indexOf(String(t || '')); return i < 0 ? PERMISSION_TIERS.length : i; }
|
|
278
|
+
// A narrow literal-text check for the inspection exemption, not a shell parser.
|
|
279
|
+
// Quoted path punctuation is data; operators outside quotes and expansion syntax
|
|
280
|
+
// retain the ordinary conservative classification below.
|
|
281
|
+
function _isLiteralInspectionText(text) {
|
|
282
|
+
let quote = null;
|
|
283
|
+
for (let i = 0; i < text.length; i++) {
|
|
284
|
+
const ch = text[i], next = text[i + 1] || '';
|
|
285
|
+
if (/[\r\n]/.test(ch)) return false;
|
|
286
|
+
if (quote) {
|
|
287
|
+
if (ch === quote) quote = null;
|
|
288
|
+
else if (quote === '"' && (ch === '`' || (ch === '$' && /[a-z0-9_{(]/i.test(next))
|
|
289
|
+
|| (ch === '\\' && next === '"'))) return false;
|
|
290
|
+
} else if (ch === '"' || ch === "'") quote = ch;
|
|
291
|
+
else if (/[;&|<>()$`^]/.test(ch) || (ch === '\\' && /["']/.test(next))) return false;
|
|
292
|
+
}
|
|
293
|
+
return quote === null && !/%[^%\r\n]+%/.test(text);
|
|
294
|
+
}
|
|
278
295
|
// 명령/capability → 요구 등급 (순수 매핑)
|
|
279
296
|
function _requiredTier(cmd) {
|
|
280
297
|
const c = String(cmd || '').toLowerCase();
|
|
@@ -284,6 +301,9 @@ function _requiredTier(cmd) {
|
|
|
284
301
|
const leaseCommand = c.match(/^\s*(?:(?:npx(?:\s+(?:-y|--yes))?\s+)?leerness(?:@[^\s]+)?\s+)?lease\s+(list|check|acquire|release)(?:\s|$)/);
|
|
285
302
|
if (leaseCommand && (leaseCommand[1] === 'list' || leaseCommand[1] === 'check')) return 'read-only';
|
|
286
303
|
if (leaseCommand) return 'safe-write';
|
|
304
|
+
// This exemption describes one inspect invocation, never composite shell text.
|
|
305
|
+
if (/^\s*(?:(?:npx(?:\s+(?:-y|--yes))?\s+)?leerness(?:@[^\s]+)?\s+)?state\s+inspect(?:\s|$)/.test(c)
|
|
306
|
+
&& _isLiteralInspectionText(c)) return 'read-only';
|
|
287
307
|
if (/release\s+publish|npm\s+publish|\bpublish\b/.test(c)) return 'publish';
|
|
288
308
|
if (/\bweb\b/.test(c)) return 'network';
|
|
289
309
|
if (/git\s+push|sync-main/.test(c)) return 'git-write';
|