@pghoya2956/livemap 1.1.1 → 1.2.0
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 +107 -0
- package/README.md +24 -7
- package/docs/adapter-contract.md +41 -4
- package/docs/issue-codes.md +150 -0
- package/docs/migrate.md +15 -0
- package/docs/semantic-authoring.md +107 -3
- package/docs/semantic-schema.md +65 -8
- package/docs/test-results.md +189 -0
- package/package.json +1 -1
- package/site/map.css +25 -0
- package/site/map.js +7 -7
- package/src/adapters/deploy.mjs +19 -8
- package/src/adapters/router.mjs +34 -22
- package/src/adapters/tasks.mjs +322 -29
- package/src/adapters/testreport.mjs +115 -19
- package/src/adapters/tests.mjs +5 -1
- package/src/check.mjs +96 -28
- package/src/cli.mjs +68 -13
- package/src/derive.mjs +76 -14
- package/src/init.mjs +52 -3
- package/src/lib/closure.mjs +143 -0
- package/src/lib/graph.mjs +7 -3
- package/src/lib/issues.mjs +161 -0
- package/src/lib/judgments.mjs +128 -0
- package/src/lib/literals.mjs +103 -0
- package/src/lib/md-blocks.mjs +113 -0
- package/src/lib/reading.mjs +55 -0
- package/src/link.mjs +76 -0
- package/src/reporters/node-results.mjs +33 -0
- package/src/results.mjs +187 -0
- package/src/test-report.mjs +47 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,113 @@
|
|
|
2
2
|
|
|
3
3
|
버전마다 `## [X.Y.Z] - YYYY-MM-DD` 절을 둔다. 릴리스 워크플로가 태그 버전의 절이 있는지 확인한다.
|
|
4
4
|
|
|
5
|
+
## [1.2.0] - 2026-09-18
|
|
6
|
+
|
|
7
|
+
엔진이 못 읽은 곳을 0으로 세지 않고 "?"와 근거 줄이 달린 이슈로 드러내고, 에이전트나 사람이 적은 판정 파일을 원문과 대조해 값으로 받는다. 검사 결과는 러너가 낸 파일별 결과 JSON으로, 화면→API 호출은 화면 코드의 경로 리터럴로 관측한다. 엔진은 계속 LLM·네트워크를 부르지 않는다. README 「버전」이 major로 정한 항목(config 키, 여정 형식, 어댑터 계약, 명령·종료 코드, 생성물 파일 이름, export 배치, 예산 설정 경로)은 더하기만 했다. 새 설정 키는 없다. 1.1.1 생성물의 필드는 지우거나 이름을 바꾸지 않았고, 규칙이 바로잡히며 값이 바뀌는 필드는 아래 「값이 바뀌는 것」에 모았다.
|
|
8
|
+
|
|
9
|
+
### 추가
|
|
10
|
+
|
|
11
|
+
check와 이슈 계약
|
|
12
|
+
|
|
13
|
+
- `livemap check --json`: stdout에 `{ schema, engine, errors, warnings, problems[] }` JSON만 낸다. 문제마다 안정 코드, 대상(`subject`), 근거 줄(`anchors`, 원문 조각 120 코드 포인트까지), 허용 처리(`resolutions`: source·judge·config·code·engine), 판정 초안(`judgmentDraft`)이 붙는다. 텍스트 출력의 1.1.1 줄 문구·순서는 그대로다.
|
|
14
|
+
- 새 코드 13개(`tasks.*` 7, `judgment.*` 2, `router.*` 3, `journey.api-not-observed`)와 1.1.1 check 줄에 붙인 코드. 코드 표의 정본은 새 문서 `docs/issue-codes.md`다. 같은 새 코드가 6건 이상이면 텍스트 출력에서 한 줄로 묶는다.
|
|
15
|
+
- `livemap check --strict`: `tasks.*`·`judgment.*` 경고를 오류로 센다(옵트인, 배포 게이트에는 두지 않는다).
|
|
16
|
+
- `livemap check --staged`와 `livemap init`의 커밋 전 훅: `.githooks/pre-commit`(`npx --no livemap check --staged`)과 `git config core.hooksPath .githooks`. 스테이징한 작업 폴더 문서·장부·판정 파일에 걸린 `tasks.*`·`judgment.*` 문제가 있으면 종료 코드 1로 커밋을 멈추고 근거 줄과 판정 초안을 출력한다. 대상 파일이 없거나 git 저장소가 아니면 0이다. 기존 `core.hooksPath`나 훅 관리자(husky·lefthook·pre-commit), `.git/hooks/pre-commit`이 있으면 덮지 않고 넣을 한 줄을 출력한다.
|
|
17
|
+
- 어댑터 계약 `g.issue(level, label, message, detail?)`: 넷째 인자로 코드·대상·근거 줄·처리·판정 초안을 붙인다. 세 인자 호출은 1.1.0 그대로다.
|
|
18
|
+
|
|
19
|
+
작업 문서 읽기
|
|
20
|
+
|
|
21
|
+
- md 블록 읽개(`src/lib/md-blocks.mjs`): 제목·목록 항목·표 행을 줄 번호와 함께 나누고 코드 펜스 안 줄은 건너뛴다.
|
|
22
|
+
- 식별자 줄 문법: 굵은 결정 줄(`- **DEC-7**`), 임의 접두어 계획 항목(`[A-Z]{1,4}[0-9]?-[0-9]+`), 번호 없는 체크박스, 취소선 정의. 규칙이 못 읽는 줄(표로 적은 결정, 첫 칸이 번호 하나가 아닌 잔여 질문 행, 계획 파일이 불분명한 체크박스)은 `tasks.unread-*` 이슈와 `partial`로 드러낸다.
|
|
23
|
+
- 파일 대체 규칙: 계획 파일이 없으면 체크박스를 가진 루트 md 하나(단일 `spec.md` 등), `spec/final.md`가 없으면 작업 폴더 루트 `final.md`.
|
|
24
|
+
- 잔여 질문: 제목에 "잔여"·"질문"이 든 절의 표만 세고, 체크한 계획 항목 설명이 같은 번호로 시작하면 닫힌 질문으로 본다. 새 필드 `openQuestions`·`openQuestionIds`, 개요 `counts.openQuestions`.
|
|
25
|
+
- 장부 상태: 절 제목 표시어(완료·complete·done, 진행·in progress, 대기·paused, 폐기·cancel 등)로 분류한다. spec-kit 장부 절(`In Progress`·`Paused`·`Completed`)을 읽는다.
|
|
26
|
+
- 번호 참조: 같은 번호를 정의한 작업을 결정 노드 `definers`·`definedAt`에 모으고, 여럿이면 `tasks.ambiguous-ref`. 여정 `refs`에 `<작업 폴더>#<번호>` 한정 참조를 받는다.
|
|
27
|
+
- 판정 파일 `map/judgments/<작업 폴더>.json`: `planFile`, `lines[]`(definition·ignore), `questions.none`·`questions.items[]`. 근거는 줄 번호 대신 원문 한 줄 안의 20자 이상 조각으로 대조해, 한 줄이면 적용(`judged`), 0줄이면 `judgment.stale`, 여러 줄이면 `judgment.invalid`다. 판정은 체크 여부·장부 상태·단계를 바꾸지 못한다. `data.json` `judgments[]`.
|
|
28
|
+
|
|
29
|
+
읽기 상태
|
|
30
|
+
|
|
31
|
+
- 노드 `props.reading`·`props.readingNotes`: `observed`·`rule`·`judged`·`partial`·`stale`·`unknown`·`none`. 작업 `plan`·`openQuestions`·`stage`, 검사 `count`·`lastRun`, 화면 `apis`, 배포 `behind`에 붙는다. `data.json` `readings`(값별·필드별 건수), `overview.json` `counts.reading`(개요 수치의 상태만, 경로 없음).
|
|
32
|
+
- 화면: 값 뒤 "?"와 이유 분류(개요), 이유 문장·근거 줄·판정 파일(작업 상세, 더보기 > 이 상황판의 읽기 상태·판정 카드). 작업 화면에서 결정 열을 뺐다. 검사 탭은 낡은 결과의 이유 문장을 보인다(`data.json` `tests[].readingNotes`).
|
|
33
|
+
|
|
34
|
+
검사 결과
|
|
35
|
+
|
|
36
|
+
- Node 사용자 리포터 `src/reporters/node-results.mjs`(패키지 공개 경로)와 결과 JSON(`tests.report` 폴더의 `test-results.json`, 필드 이름은 CTRF). `livemap test-report`가 JUnit과 이 리포터를 함께 붙여 돌린다.
|
|
37
|
+
- `livemap test-report --import <파일> [--sha <커밋>]`: livemap 리포터 출력, Playwright JSON 리포터 출력, JUnit XML을 결과 JSON에 넣는다. 가릴 수 없으면 종료 코드 2.
|
|
38
|
+
- 최신 판정: 결과 커밋이 HEAD의 조상이고, 그 뒤 커밋과 실행 때 변경이 설정의 문서 경로(`tasks.dir`, 위키 인덱스 폴더, `semantic`, `roadmap.file`, `captures.site`, `deploy.manifest`, `map/judgments`) 밖을 건드리지 않으면 최신이다.
|
|
39
|
+
- 검사 노드 `lastRun`의 `failed`·`skipped`·`pending`·`flaky`·`runner`·`sha`·`at`·`tags`, `runCount`, `data.json` `testRuns[]`, `testreport:last`의 `signal`·`runs`. 결과 파일 경로와 검사 파일 경로를 글자 그대로 맞춘다.
|
|
40
|
+
- 문서 `docs/test-results.md`(형식, 최신 판정, 리포터·import 명령, CI 배선 예).
|
|
41
|
+
|
|
42
|
+
화면→API 관측
|
|
43
|
+
|
|
44
|
+
- router가 페이지의 import 닫힘에서 `/api/` 문자열 리터럴을 모아 화면 노드 `apiLiterals[]`에 적고, 모든 어댑터 뒤 연결 단계(`src/link.mjs`)가 API 노드에 대응해 `calls` 엣지와 `matched`를 채운다. 확장 닫힘은 리터럴 추출에만 쓰고 화면 `files`·`source`와 변경 연결은 1.1.1 그대로다.
|
|
45
|
+
- 맞는 API가 없으면 `router.unknown-api`, 여정 `apis`에만 있고 화면에서 관측되지 않은 API는 `journey.api-not-observed`. 고아 API는 관측한 화면 호출만 센다.
|
|
46
|
+
- `router.hookApi`가 있으면 1.1.1처럼 읽고(합집합) 항목마다 `router.hookapi-redundant`·`router.hookapi-only`를 알린다. 화면 노드 `hookApiKeys`.
|
|
47
|
+
|
|
48
|
+
기타
|
|
49
|
+
|
|
50
|
+
- 배포 노드 `behindManifestOnly`(뺀 매니페스트 전용 커밋 수).
|
|
51
|
+
- 문서: `docs/adapter-contract.md`(넷째 인자, 읽기 상태, 리터럴과 연결 단계), `docs/semantic-authoring.md`(작업 문서 규칙, 판정 파일, 한정 참조), `docs/semantic-schema.md`(읽기 상태, 새 필드), `docs/issue-codes.md`, `docs/test-results.md`, `docs/migrate.md` 2.0.0 예고.
|
|
52
|
+
|
|
53
|
+
### 고친 것(1.1.1 결함)
|
|
54
|
+
|
|
55
|
+
- 설정에 `semantic` 키가 없으면 `build`·`check`가 `path` TypeError로 멈췄다. 이제 여정 입력 없음으로 본다.
|
|
56
|
+
- `livemap test-report`를 `node --test` 안에서(검사·CI 래퍼) 부르면 자식 러너가 `NODE_TEST_CONTEXT`를 물려받아 검사 파일을 하나도 돌리지 않고 종료 코드 0을 냈다. 이제 그 변수를 빼고 러너를 띄운다.
|
|
57
|
+
- Node 22 JUnit 리포터가 최상위 `<testcase>`를 `<testsuite>` 밖에 써서 1.1.1 testreport가 통과한 실행을 검사 0건으로 읽었다(결과가 늘 최신 아님). 최상위·중첩 `<testcase>`를 모두 센다.
|
|
58
|
+
- 배포 뒤처짐(`behind`)이 배포 봇이 매니페스트만 바꾼 커밋까지 세어, 배포 직후에도 1로 보였다. 매니페스트 경로만 바꾼 커밋을 빼고 센다.
|
|
59
|
+
- router의 로컬 import 닫힘이 작은따옴표 import만 따라가, 큰따옴표로 import한 페이지의 데이터 출처(live·mock)와 변경 연결이 빠졌다.
|
|
60
|
+
|
|
61
|
+
### 값이 바뀌는 것
|
|
62
|
+
|
|
63
|
+
같은 입력을 1.1.1과 1.2.0으로 build·check한 값이다. "실사용 프로젝트 사본"은 화면 29·API 55·작업 24개인 제품 저장소, "스펙 작업 코퍼스"는 작업 폴더 36개뿐인 문서 저장소다. 두 사본 모두 판정 파일이 없고, 원문은 고치지 않았다.
|
|
64
|
+
|
|
65
|
+
| 값 | 대상 | 1.1.1 | 1.2.0 | 까닭 |
|
|
66
|
+
|---|---|---|---|---|
|
|
67
|
+
| 조용히 잘못 읽은 작업 문서 줄(후보인데 세지도 알리지도 않은 줄) | 실사용 프로젝트 사본 | 67줄(작업 8/24) | 0 | 굵은 결정·임의 접두어 체크박스·단일 `spec.md`를 규칙으로 읽고, 못 읽는 줄은 이슈로 알림 |
|
|
68
|
+
| | 스펙 작업 코퍼스 | 177줄(작업 16/36) | 0 | 같은 까닭과 루트 `final.md` 대체, 잔여 질문 표의 번호 하나가 아닌 행 |
|
|
69
|
+
| 개요 열린 질문 | 실사용 프로젝트 사본 | 29(`counts.oq`) | 1?(`counts.openQuestions`, 읽기 상태 partial) | 답한 `## 열린 질문` 표를 세지 않고 잔여 질문 절만 셈. 남은 1은 완료 작업의 닫히지 않은 질문(판정 파일로 0) |
|
|
70
|
+
| | 스펙 작업 코퍼스 | 92 | 28?(partial) | 같은 까닭. 체크한 계획 항목 설명이 번호로 시작하면 닫힘 |
|
|
71
|
+
| 계획 항목 완료/전체(작업 합) | 실사용 프로젝트 사본 | 149/181 | 189/221 | 계획 파일 없는 작업의 체크박스 40줄(단일 `spec.md` 세 작업 포함)과 임의 접두어 항목을 셈. 네 작업의 0/0이 4/4·6/6·5/5·6/6 |
|
|
72
|
+
| | 스펙 작업 코퍼스 | 768/897 | 771/901 | 번호 없는 체크박스와 루트 `final.md` 작업 |
|
|
73
|
+
| 결정 수 `dec`(작업 합, 화면에서는 뺌) | 실사용 프로젝트 사본 | 149 | 176 | 굵은 결정 줄 |
|
|
74
|
+
| | 스펙 작업 코퍼스 | 206 | 360 | 굵은 결정 줄, 루트 `final.md` |
|
|
75
|
+
| 작업 상태 분포 | 실사용 프로젝트 사본 | 완료 20·진행 2·폐기 1·기록 1 | 완료 21·진행 2·폐기 1 | `완료 실행 이력` 절을 표시어로 완료로 읽음 |
|
|
76
|
+
| | 스펙 작업 코퍼스 | 기록 36 | 완료 28·진행 7·폐기 1 | spec-kit 장부 절 `In Progress`·`Completed`와 `폐기` 절을 읽음 |
|
|
77
|
+
| 화면→API 쌍(`calls` 엣지) | 실사용 프로젝트 사본, hookApi 설정 그대로 | 215 | 257 | 1.1.1 쌍 215개 모두 유지, 리터럴로 42쌍 추가(2단계 hook이 부르는 세션 확인, 같은 경로의 메서드 노드, 대응표에 없던 견적 화면 호출) |
|
|
78
|
+
| | 같은 사본, hookApi를 지운 설정 | 0 | 257 | 대응표 없이 리터럴로 관측 |
|
|
79
|
+
| 고아 API | 실사용 프로젝트 사본 | 2 | 1 | 관측한 화면 호출로 셈 |
|
|
80
|
+
| 배포 뒤처짐(`signals.deployBehindAll`) | 실사용 프로젝트 사본, 매니페스트 전용 커밋 직후 두 시점 | 1 | 0 | 매니페스트만 바꾼 커밋을 뺌(`behindManifestOnly` 1) |
|
|
81
|
+
| 검사 신호·등급 A | 엔진 결과 픽스처, 통과한 실행 뒤 build | stale·A 0 | ok·A 1 | JUnit 최상위 testcase 합산과 결과 JSON |
|
|
82
|
+
| | 같은 픽스처, 통과 뒤 작업 문서만 바꾼 커밋 | stale·A 0 | ok·A 1 | 최신 판정이 커밋 일치에서 "결과 커밋 뒤 문서 경로 밖 변경 없음"으로 바뀜 |
|
|
83
|
+
| | 같은 픽스처, 통과 뒤 코드를 바꾼 커밋 | stale·A 0 | stale·A 0 | 그대로 |
|
|
84
|
+
| 검사 개수 읽기 상태 | 실사용 프로젝트 사본 | 없음 | `counts.reading.tests` partial | 제목이 템플릿 문자열인 검사 호출 1파일 |
|
|
85
|
+
| check 경고 수(오류 수·종료 코드는 그대로 0) | 실사용 프로젝트 사본 | 2 | 87 | `router.hookapi-redundant` 54(묶음 줄 한 줄), `tasks.ambiguous-ref` 29, 완료 작업 열린 질문 1, 번호 참조 대상 없음 1, 고아 줄 2(API 1·검사 1) |
|
|
86
|
+
| | 스펙 작업 코퍼스 | build 실패(`semantic` 키 없음) | 17(오류 0) | `tasks.questions-open-done` 9, `tasks.unread-definition` 6, `tasks.questions-unknown` 1, `tasks.stage-unknown` 1 |
|
|
87
|
+
| build 요약 경고(`signals.warnings`) | 실사용 프로젝트 사본 | 0 | 1 | 여정 refs의 잔여 질문 번호가 정의로 풀리지 않음(아래 알려진 한계) |
|
|
88
|
+
|
|
89
|
+
등급 A의 뜻이 바뀐다. 1.1.1은 JUnit 결과의 커밋이 main HEAD와 같을 때만 A였다. 1.2.0은 결과 커밋이 HEAD의 조상이고 그 뒤 설정의 문서 경로 밖을 바꾼 커밋과 실행 때 변경이 없으면 A다. 그래서 작업 문서·판정 파일·여정만 바꾼 커밋 뒤에도 A가 남고, 코드·CI·설정 파일을 바꾼 커밋 뒤에는 다시 검사를 돌려야 A다. 오래된 결과를 지금 HEAD로 `--import`하면 최신으로 보이므로 러너 바로 뒤에 가져온다.
|
|
90
|
+
|
|
91
|
+
### 업그레이드하면 check가 실패할 수 있는 항목
|
|
92
|
+
|
|
93
|
+
새 경고는 종료 코드를 바꾸지 않는다. 다음 경우에만 1.1.1에서 통과하던 `check`가 1이 될 수 있다.
|
|
94
|
+
|
|
95
|
+
- 판정 파일을 둔 프로젝트: `map/judgments/*.json`의 JSON·필드 형식 위반, 없는 작업 폴더·`planFile`·근거 파일, 20자 미만이거나 여러 줄에 걸린 근거 조각, 번호가 없는 근거 줄은 `judgment.invalid`(error)다. 판정 파일이 없는 프로젝트는 해당 없다.
|
|
96
|
+
- 프로젝트 어댑터가 `g.issue`에 넷째 인자를 넘기던 경우: 1.1.1은 무시했지만 1.2.0은 형식을 검사해, 모르는 키·틀린 코드 모양·코드 표와 다른 수준이면 `throw`하고 그 어댑터가 failed(오류)가 된다.
|
|
97
|
+
- 여정 `refs`의 한정 참조(`<폴더>#<번호>`): 그 폴더가 번호를 정의하지 않았으면 "참조 미해결" 오류다. 1.1.1에는 이 모양이 없어 새로 쓴 참조에만 해당한다.
|
|
98
|
+
- `check --strict`를 CI에 배선하면 `tasks.*`·`judgment.*` 경고가 오류가 된다(옵트인).
|
|
99
|
+
- `livemap init`을 다시 돌려 커밋 전 훅을 설치하면 `check` 자체는 그대로지만, 스테이징한 작업 문서에 걸린 `tasks.*`·`judgment.*` 문제가 있는 커밋이 멈춘다.
|
|
100
|
+
|
|
101
|
+
### 알려진 한계
|
|
102
|
+
|
|
103
|
+
- 검사 어댑터(`tests`)가 검사 파일의 로컬 import를 따라갈 때 작은따옴표 import만 읽는다(router는 두 따옴표 모두 읽음).
|
|
104
|
+
- 화면 import 닫힘 밖 모듈의 경로 리터럴(예: 공용 요청 클라이언트의 세션 확인)은 어느 화면에도 붙지 않고 수도 남기지 않는다.
|
|
105
|
+
- 닫힘이 파일 단위라 공유 컴포넌트가 가진 호출은 그 컴포넌트를 쓰는 모든 화면에 붙는다. 변수로 조립한 경로는 잡히지 않고 그 API는 고아 경고로 드러난다.
|
|
106
|
+
- 라우트 페이지가 아닌 틀(레이아웃) 컴포넌트의 호출은 화면 호출로 세지 않아, 그 API가 고아로 남을 수 있다.
|
|
107
|
+
- `execution/`에 헤더만 있는 실행 기록 파일이 있어도 작업 단계가 "실행"이다(1.1.1 단계 규칙 그대로). 계획 단계에서 실행 기록 틀을 만드는 워크플로는 실행 전에도 "실행"으로 보인다.
|
|
108
|
+
- 잔여 질문 번호(`OQ-28`)는 정의로 보지 않아 여정 `refs`의 대상으로 풀리지 않고 "번호 참조 대상 없음" 경고가 된다.
|
|
109
|
+
- 템플릿 문자열 검사 제목 판정은 한 줄 단위라, `test(` 다음 줄에서 제목이 시작하는 호출은 `partial`로 잡지 못한다.
|
|
110
|
+
- 설정 루트가 git 저장소의 하위 폴더이면 배포 `behind`는 그 폴더를 건드린 커밋만 세고 `behindManifestOnly`에 폴더 밖 커밋이 섞인다.
|
|
111
|
+
|
|
5
112
|
## [1.1.1] - 2026-09-17
|
|
6
113
|
|
|
7
114
|
작업 어댑터가 계획 항목·결정·열린 질문을 파일 하나에서만 센다. 생성물의 키와 형식은 그대로고 값만 바뀐다.
|
package/README.md
CHANGED
|
@@ -9,21 +9,23 @@ Project status board engine. It scans a repository (routes, API handlers, migrat
|
|
|
9
9
|
```bash
|
|
10
10
|
npm i -D -E @pghoya2956/livemap
|
|
11
11
|
npm i -D -E @playwright/test@1.63.0 # 화면 예산 검사를 쓸 때만
|
|
12
|
-
npx --no livemap init # map/ 초안·.gitignore·npm
|
|
12
|
+
npx --no livemap init # map/ 초안·.gitignore·npm 스크립트·커밋 전 훅
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
`init`이 만드는 것: `map/config.json`, `map/semantic/journeys.json`, `map/README.md`, `map/captures/README.md`, `.gitignore`의 `map/.out/`, npm 스크립트 `map`·`map:check`·`map:serve`·`map:export`·`test:report`(Playwright가 있으면 `map:budget`). 다시 실행하면 아무것도 바꾸지 않는다.
|
|
15
|
+
`init`이 만드는 것: `map/config.json`, `map/semantic/journeys.json`, `map/README.md`, `map/captures/README.md`, `.gitignore`의 `map/.out/`, npm 스크립트 `map`·`map:check`·`map:serve`·`map:export`·`test:report`(Playwright가 있으면 `map:budget`), 커밋 전 훅 `.githooks/pre-commit`과 `git config core.hooksPath .githooks`(1.2.0, 아래 「커밋 전 훅」). 다시 실행하면 아무것도 바꾸지 않는다.
|
|
16
16
|
|
|
17
17
|
## 명령
|
|
18
18
|
|
|
19
19
|
| 명령 | 하는 일 |
|
|
20
20
|
|---|---|
|
|
21
21
|
| `livemap build [--out map/.out]` | 스캔 → `graph.json`·`data.json`·`overview.json` |
|
|
22
|
-
| `livemap check` | 정합 검사. 오류가 있으면 exit 1 |
|
|
22
|
+
| `livemap check [--json] [--strict]` | 정합 검사. 오류가 있으면 exit 1. `--json`은 stdout에 이슈 JSON만, `--strict`는 `tasks.*`·`judgment.*` 경고도 오류로 센다([docs/issue-codes.md](docs/issue-codes.md)) |
|
|
23
|
+
| `livemap check --staged` | 커밋 전 훅용. 스테이징한 작업 문서·판정 파일에 걸린 문제만 오류로 센다 |
|
|
23
24
|
| `livemap serve [--port 4180]` | `http://127.0.0.1:4180/map/`, 요청마다 재빌드(5초 캐시) |
|
|
24
25
|
| `livemap serve --static <dir>` | export 폴더를 재빌드 없이 같은 배치로 |
|
|
25
26
|
| `livemap export <dir>` | 화면·서체·캡처·생성물을 `/map/` 배치 그대로 한 폴더에 |
|
|
26
|
-
| `livemap test-report` | 단위 검사를 JUnit으로(`config.tests`) |
|
|
27
|
+
| `livemap test-report` | 단위 검사를 JUnit과 결과 JSON으로(`config.tests`) |
|
|
28
|
+
| `livemap test-report --import <파일> [--sha <커밋>]` | livemap 리포터·Playwright JSON·JUnit 출력을 결과 JSON에 넣는다([docs/test-results.md](docs/test-results.md)) |
|
|
27
29
|
| `livemap --version` | 버전 |
|
|
28
30
|
| 화면 예산 | `npx --no playwright test --config node_modules/@pghoya2956/livemap/budget/playwright.config.mjs` |
|
|
29
31
|
|
|
@@ -44,7 +46,7 @@ npx --no livemap init # map/ 초안·.gitignore·npm 스크립
|
|
|
44
46
|
"engine": 1,
|
|
45
47
|
"project": { "name": "ReefDesk", "host": "https://reefdesk.example.invalid" },
|
|
46
48
|
"adapters": ["router", "bff", "migrations", "tests", "wiki", "tasks", "roadmap", "git", "deploy", "testreport"],
|
|
47
|
-
"router": { "app": "web/src/App.tsx", "pagesDir": "web/src/pages", "localDirs": ["web/src/pages"], "mockPattern": "/mock'", "livePattern": "lib/queries"
|
|
49
|
+
"router": { "app": "web/src/App.tsx", "pagesDir": "web/src/pages", "localDirs": ["web/src/pages"], "mockPattern": "/mock'", "livePattern": "lib/queries" },
|
|
48
50
|
"bff": { "server": "app/server.mjs" },
|
|
49
51
|
"migrations": { "dir": "db/migrations" },
|
|
50
52
|
"tests": { "dir": "tests", "gatePattern": "ALLOW_DESTRUCTIVE", "report": "map/.out/junit.xml" },
|
|
@@ -56,13 +58,28 @@ npx --no livemap init # map/ 초안·.gitignore·npm 스크립
|
|
|
56
58
|
}
|
|
57
59
|
```
|
|
58
60
|
|
|
61
|
+
화면→API 호출은 1.2.0부터 화면 코드의 `/api/` 문자열 리터럴로 관측하므로 hook 이름 대응표 `router.hookApi`는 폐기 예정이다.
|
|
62
|
+
1.x 동안은 설정에 있으면 계속 읽고, `livemap check`가 항목마다 지워도 되는지(`router.hookapi-redundant`) 리터럴로 안 잡히는지(`router.hookapi-only`) 알린다.
|
|
63
|
+
2.0.0에서 키를 지운다([docs/migrate.md](docs/migrate.md)).
|
|
64
|
+
|
|
65
|
+
## 커밋 전 훅
|
|
66
|
+
|
|
67
|
+
`livemap init`은 git 저장소 루트에서 `.githooks/pre-commit`(`npx --no livemap check --staged`)을 만들고 `git config core.hooksPath .githooks`를 둔다. 작업 폴더 문서나 `map/judgments/` 판정 파일을 스테이징한 커밋에서, 그 파일에 걸린 `tasks.*`·`judgment.*` 문제가 있으면 종료 코드 1로 커밋을 멈춘다. 출력에는 문제 코드, 근거 줄, 판정 초안이 나온다. 에이전트 세션이 원문을 규칙대로 고치거나 판정 파일을 써서 스테이징하고 다시 커밋한다. `--no-verify`로 넘기지 않는다.
|
|
68
|
+
|
|
69
|
+
- 대상 파일이 없는 커밋(코드만 바꾼 커밋)은 빌드하지 않고 통과한다.
|
|
70
|
+
- `core.hooksPath`는 git 설정이라 클론마다 `livemap init`을 한 번 돌린다.
|
|
71
|
+
- 이미 다른 `core.hooksPath`나 훅 관리자(husky·lefthook·pre-commit), `.git/hooks/pre-commit`이 있으면 덮지 않고 그 설정에 넣을 한 줄을 출력한다.
|
|
72
|
+
- 규칙과 판정 파일은 [docs/semantic-authoring.md](docs/semantic-authoring.md) 「작업 문서」.
|
|
73
|
+
|
|
59
74
|
## 문서
|
|
60
75
|
|
|
61
76
|
| 문서 | 내용 |
|
|
62
77
|
|---|---|
|
|
63
78
|
| [docs/adapter-contract.md](docs/adapter-contract.md) | 어댑터 시그니처·노드·엣지·프로젝트 어댑터 |
|
|
64
|
-
| [docs/semantic-authoring.md](docs/semantic-authoring.md) | 여정 파일 작성 |
|
|
65
|
-
| [docs/semantic-schema.md](docs/semantic-schema.md) | 시맨틱 레이어
|
|
79
|
+
| [docs/semantic-authoring.md](docs/semantic-authoring.md) | 여정 파일·작업 문서·판정 파일 작성 |
|
|
80
|
+
| [docs/semantic-schema.md](docs/semantic-schema.md) | 시맨틱 레이어 모델, 읽기 상태, 생성물 필드 |
|
|
81
|
+
| [docs/issue-codes.md](docs/issue-codes.md) | check 이슈 코드·JSON 출력·커밋 전 훅 |
|
|
82
|
+
| [docs/test-results.md](docs/test-results.md) | 검사 결과 JSON·최신 판정·리포터·CI 배선 |
|
|
66
83
|
| [docs/hosting-and-csp.md](docs/hosting-and-csp.md) | export·정적 서빙·CSP·CI |
|
|
67
84
|
| [docs/view-budget.md](docs/view-budget.md) | 화면 예산 규칙 |
|
|
68
85
|
| [docs/migrate.md](docs/migrate.md) | major 이행 |
|
package/docs/adapter-contract.md
CHANGED
|
@@ -14,17 +14,23 @@ export default function name(g, fs, cfg) {
|
|
|
14
14
|
}
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
- `g` — 그래프. `g.add(kind, id, label, props, src)`, `g.link(fromKind, fromId, edgeKind, toKind, toId)`, `g.get`, `g.of(kind)`, `g.in`, `g.out`, `g.issue(level, label, message)`(1.1.0부터).
|
|
17
|
+
- `g` — 그래프. `g.add(kind, id, label, props, src)`, `g.link(fromKind, fromId, edgeKind, toKind, toId)`, `g.get`, `g.of(kind)`, `g.in`, `g.out`, `g.issue(level, label, message, detail?)`(1.1.0부터, 넷째 인자는 1.2.0부터).
|
|
18
18
|
- `fs` — 저장소 접근. `read(rel)`, `has(rel)`, `isDir(rel)`, `walk(dir, pred)`, `ls(dir)`, `git(...args)`(실패 시 빈 문자열), `hasGit()`, `resolveRef(name)`(main → origin/main → HEAD), `lastCommit(rel)`, `lineOf(text, needle)`.
|
|
19
19
|
- `cfg` — `map/config.json` 전체. 자기 키(`cfg.<name>`)만 읽고, 다른 어댑터의 키는 `?.`로 방어한다.
|
|
20
20
|
|
|
21
21
|
## 오류·경고 보고(g.issue)
|
|
22
22
|
|
|
23
|
-
어댑터가 읽은 사실에서 프로젝트 규칙 위반을 찾았으면 `g.issue(level, label, message)`로 낸다. 반환값 partial·throw는 "어댑터가 제대로 읽었나"를, `g.issue`는 "읽은 내용에 문제가 있나"를 알린다. 1.1.0부터 쓸 수 있다.
|
|
23
|
+
어댑터가 읽은 사실에서 프로젝트 규칙 위반을 찾았으면 `g.issue(level, label, message, detail?)`로 낸다. 반환값 partial·throw는 "어댑터가 제대로 읽었나"를, `g.issue`는 "읽은 내용에 문제가 있나"를 알린다. 세 인자 호출은 1.1.0부터, 넷째 인자는 1.2.0부터 쓸 수 있다.
|
|
24
24
|
|
|
25
25
|
```js
|
|
26
26
|
g.issue('warn', '로드맵', '결정 대기 30일 넘음: 결제 흐름');
|
|
27
27
|
g.issue('error', '여정 파일', '필수 키 없음: owner');
|
|
28
|
+
g.issue('warn', '작업 문서', '완료 작업에 닫히지 않은 잔여 질문 1', {
|
|
29
|
+
code: 'tasks.questions-open-done',
|
|
30
|
+
subject: { kind: 'task', id: '20260101-sample' },
|
|
31
|
+
anchors: [{ file: 'tasks/20260101-sample/spec/final.md', line: 12, excerpt: '| OQ-02 | 남은 질문 |' }],
|
|
32
|
+
resolutions: ['judge'],
|
|
33
|
+
});
|
|
28
34
|
```
|
|
29
35
|
|
|
30
36
|
- `level`은 `'error'` 또는 `'warn'`이다. 그 밖의 값이거나 `label`·`message`가 문자열이 아니면 `throw`하고, 그 어댑터는 failed가 된다. 다른 어댑터는 계속 돈다.
|
|
@@ -33,6 +39,37 @@ g.issue('error', '여정 파일', '필수 키 없음: owner');
|
|
|
33
39
|
- `livemap check`는 기존 검사 뒤에 error를 `✗ {label}: {message}`로, warn을 `△ {label}: {message}`로 출력한다. error는 종료 코드 1에 센다.
|
|
34
40
|
- 개요에는 나오지 않고 더보기 > 이 상황판(`#/more/about`)에 목록으로 나온다.
|
|
35
41
|
|
|
42
|
+
넷째 인자(1.2.0):
|
|
43
|
+
|
|
44
|
+
- 키는 `code`·`subject`·`anchors`·`resolutions`·`judgmentDraft`만 받는다. 모르는 키가 있거나 형식이 틀리면 `throw`하고 그 어댑터는 failed가 된다.
|
|
45
|
+
- `code`는 `<영역>.<이름>`(소문자·숫자·하이픈)이다. 엔진 코드 표(`issue-codes.md`)에 있는 코드는 표의 수준과 `level`이 같아야 한다. 프로젝트 코드는 표 밖 이름을 쓴다.
|
|
46
|
+
- `subject`는 `{ kind, id }` 또는 생략(`null`). `anchors`는 `{ file, line?, excerpt? }` 배열이고 `line`은 1 이상 정수이거나 생략(`null`, 파일 단위 근거)이다. `excerpt`는 엔진이 120 코드 포인트에서 자른다(`data.json`은 서빙되므로 긴 원문을 싣지 않는다).
|
|
47
|
+
- `resolutions`는 `source`·`judge`·`config`·`code`·`engine` 중에서 고른다. 생략하면 표의 처리 값, 표 밖 코드는 `[]`다.
|
|
48
|
+
- `judgmentDraft`는 판정 파일 초안 객체다. `check --json`과 `check --staged` 출력에 그대로 실린다.
|
|
49
|
+
- 넷째 인자를 준 문제만 `issues[]` 항목에 `code`·`subject`·`anchors`·`resolutions`(있으면 `judgmentDraft`)가 더해진다. 세 인자 호출의 항목 모양은 1.1.0 그대로이고, check에서는 코드 `adapter.issue`로 나온다.
|
|
50
|
+
|
|
51
|
+
## 읽기 상태(props.reading)
|
|
52
|
+
|
|
53
|
+
1.2.0부터 노드 값마다 어떻게 읽었는지를 적는다. 화면은 이 상태로 값 뒤에 "?"와 이유를 붙인다.
|
|
54
|
+
|
|
55
|
+
- `props.reading`은 `{ 필드: 상태 }`, `props.readingNotes`는 `{ 필드: 이유 문장 }`이다. 이유 문장에는 파일·줄이 들어갈 수 있어 개요(`overview.json`)에는 싣지 않는다.
|
|
56
|
+
- 상태 값은 일곱이다: `observed`(구조화된 출력이나 livemap 소유 형식), `rule`(규칙으로 읽었고 같은 범위의 후보 줄이 모두 읽힘), `judged`(판정 파일이 값을 채움), `partial`(안 읽힌 후보 줄이나 뜻 확인이 필요한 행이 있음), `stale`(판정 근거나 검사 결과가 낡음), `unknown`(소스가 없거나 형식 밖), `none`(대상 없음).
|
|
57
|
+
- 적지 않은 필드는 `rule`로 본다. 합계는 구성 요소 중 하나라도 `partial`·`stale`·`unknown`이면 `partial`이다.
|
|
58
|
+
- 프로젝트 어댑터는 `props.reading`에 직접 적어도 된다. 참조 어댑터는 `src/lib/reading.mjs`의 `setReading(node, field, value, note?)`를 쓴다.
|
|
59
|
+
- 참조 어댑터가 상태를 적는 필드: 작업 `plan`·`openQuestions`·`stage`(tasks), 검사 `count`(tests·testreport)·`lastRun`(testreport), 화면 `apis`(연결 단계), 배포 `behind`(deploy). 뜻은 `semantic-schema.md`.
|
|
60
|
+
|
|
61
|
+
## 화면 리터럴과 연결 단계(apiLiterals)
|
|
62
|
+
|
|
63
|
+
1.2.0 router 어댑터는 화면에서 API로 가는 호출을 대응표(`router.hookApi`) 대신 코드의 문자열 리터럴로 관측한다.
|
|
64
|
+
|
|
65
|
+
1. router는 페이지 파일에서 로컬 import를 따라가며(작은따옴표·큰따옴표, `import type` 제외, `export … from` 재수출 포함) 리터럴을 모은다. `router.localDirs` 안 파일은 파일 전체, `router.app` 폴더 안이지만 `localDirs` 밖인 모듈은 가져온 이름의 최상위 선언만(그 선언이 같은 모듈의 다른 최상위 선언을 쓰면 깊이 2까지) 넣는다. 이 확장 닫힘은 리터럴 추출에만 쓰고, 화면 `files`·`source`·`mockVia`·`fixedVia`와 git 변경 연결은 1.1.1 파일 닫힘 그대로다.
|
|
66
|
+
2. 따옴표·백틱 바로 뒤가 `/api/`인 문자열을 뽑아 `?`·`#` 뒤와 끝 `/`를 떼고, 조각 전체가 `${…}`면 `:param`, 조각 중간의 `${…}`는 앞 글자까지 남기고 열린 끝으로 둔다.
|
|
67
|
+
3. router는 화면 노드 `apiLiterals[]`에 `{ path, open?, file, line, matched }`를 적기만 한다. 어댑터 순서상 router가 bff보다 먼저 돌아 대응할 API 노드가 아직 없기 때문이다.
|
|
68
|
+
4. 모든 어댑터가 끝난 뒤 엔진 연결 단계(`src/link.mjs`)가 리터럴을 API 노드에 대응해 `calls` 엣지와 `matched`(맞은 API 노드 id 배열)를 채운다. 경로 조각 수가 같고 조각마다 같거나 한쪽이 `:이름`이면 맞고, 같은 경로의 메서드 노드는 모두 잇는다. 맞는 노드가 없으면 `router.unknown-api`다. 연결 단계는 `adapters[]`에 들지 않고, 실패하면 오류 이슈(`연결 단계: …`)로 남는다.
|
|
69
|
+
5. 설정에 `router.hookApi`가 있으면 router가 1.1.1처럼 노드와 엣지도 만들고(합집합), 화면 노드 `hookApiKeys`에 쓴 키를 적는다. 연결 단계가 키마다 `router.hookapi-redundant`(리터럴로도 나옴, 지워도 됨) 또는 `router.hookapi-only`(hookApi로만 나옴)를 한 건 낸다. `router.hookApi`는 2.0.0에서 지울 예정이다.
|
|
70
|
+
|
|
71
|
+
프로젝트 어댑터가 다른 방식으로 화면 호출을 찾으면 `calls` 엣지를 직접 이어도 된다. `apiLiterals`를 적으면 연결 단계가 같은 규칙으로 대응한다.
|
|
72
|
+
|
|
36
73
|
## 어디에 두나
|
|
37
74
|
|
|
38
75
|
엔진은 `config.adapters`의 이름마다 프로젝트 `map/adapters/<name>.mjs`를 먼저 찾고, 없으면 패키지에 딸린 참조 어댑터(`src/adapters/<name>.mjs`)를 쓴다. 프로젝트 파일이 참조 어댑터와 이름이 같으면 `build`·`check`가 "프로젝트 어댑터가 참조 어댑터를 가림: <name>" 한 줄을 알린다. 참조 어댑터의 결함은 엔진 저장소에서 고치고, 프로젝트만의 스택은 다른 이름의 프로젝트 어댑터로 둔다.
|
|
@@ -68,7 +105,7 @@ g.issue('error', '여정 파일', '필수 키 없음: owner');
|
|
|
68
105
|
|
|
69
106
|
## 순서
|
|
70
107
|
|
|
71
|
-
`config.adapters` 순서로 돈다. `tests`·`git`은 `screen`·`api`가 있어야 covers·changes를 잇고, `testreport`는 `git`이 만든 `deploy:head`로
|
|
108
|
+
`config.adapters` 순서로 돈다. `tests`·`git`은 `screen`·`api`가 있어야 covers·changes를 잇고, `testreport`는 `git`이 만든 `deploy:head`로 검사 결과가 최신인지 판정한다(`test-results.md`). 새 어댑터가 다른 어댑터의 노드에 기대면 그 뒤에 둔다. 모든 어댑터가 끝나면 엔진 연결 단계가 화면 리터럴을 API 노드에 잇는다(위 「화면 리터럴과 연결 단계」). 어댑터 순서를 바꾸지 않아도 되므로 기존 설정의 `adapters` 순서는 그대로 둔다.
|
|
72
109
|
|
|
73
110
|
## 골격
|
|
74
111
|
|
|
@@ -109,7 +146,7 @@ test('openapi: paths → api 노드', () => {
|
|
|
109
146
|
| Express·Fastify·Hono | OpenAPI 문서(있으면) | 라우터 등록 호출 `app.get('/x'` 정규식 |
|
|
110
147
|
| Rails·Django·Spring | `rails routes`/`manage.py show_urls`/Actuator 덤프를 CI에서 파일로 저장 → 파일 어댑터 | 소스 파싱은 tree-sitter |
|
|
111
148
|
| Prisma·Django ORM·Alembic | migration 폴더 관례 | 스키마 파일(`schema.prisma`) |
|
|
112
|
-
| Jest·pytest·Go test | JUnit XML(거의 모든 러너가 낸다) → testreport | 소스에서 라우트 문자열 grep |
|
|
149
|
+
| Jest·pytest·Go test | JUnit XML(거의 모든 러너가 낸다)을 `livemap test-report --import`로 결과 JSON에 넣음 → testreport | 소스에서 라우트 문자열 grep |
|
|
113
150
|
| 작업 문서가 tasks/가 아님 | tasks 어댑터의 `dir`·절 이름만 바꿈 | Linear·GitHub Issues는 CI에서 JSON 덤프 → 파일 어댑터 |
|
|
114
151
|
|
|
115
152
|
원칙은 "스택이 이미 내놓는 산출물을 읽는다"이다. 산출물은 형식이라 언어를 넘어 재사용되고, 소스 정규식은 그 프로젝트에서만 산다.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# 이슈 코드
|
|
2
|
+
|
|
3
|
+
`livemap check`가 내는 문제마다 안정 코드, 대상, 근거 줄, 허용 처리가 붙는다. 이 문서가 코드의 뜻과 허용 처리의 정본이다. 코드 이름은 1.x 동안 바꾸거나 지우지 않고 더하기만 한다. 언제 원문을 고치고 언제 판정하는지는 이 코드를 받는 에이전트 절차가 정한다. 엔진은 원문과 판정 파일을 고치지 않는다.
|
|
4
|
+
|
|
5
|
+
## 출력
|
|
6
|
+
|
|
7
|
+
`livemap check`의 텍스트 출력은 1.1.1 줄 문구를 그대로 둔다. 새 코드(아래 첫 표) 중 같은 코드가 6건 이상이면 `△ tasks.ambiguous-ref 32건(단계 17): livemap check --json`처럼 한 줄로 묶는다. 괄호 안 수는 서로 다른 대상 수다. 기존 check 줄과 코드 표에 없는 코드는 묶지 않는다.
|
|
8
|
+
|
|
9
|
+
`livemap check --json`은 stdout에 JSON 하나만 낸다. 빌드 중 어댑터가 찍은 줄과 "프로젝트 어댑터가 참조 어댑터를 가림" 알림은 stderr로 간다. 종료 코드는 텍스트 출력과 같다(오류가 있으면 1).
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"schema": 1,
|
|
14
|
+
"engine": "1.2.0",
|
|
15
|
+
"errors": 0,
|
|
16
|
+
"warnings": 1,
|
|
17
|
+
"problems": [
|
|
18
|
+
{
|
|
19
|
+
"level": "warn",
|
|
20
|
+
"code": "tasks.questions-open-done",
|
|
21
|
+
"msg": "작업 문서: 완료 작업에 닫히지 않은 잔여 질문 1",
|
|
22
|
+
"subject": { "kind": "task", "id": "20260101-sample" },
|
|
23
|
+
"anchors": [{ "file": "tasks/20260101-sample/spec/final.md", "line": 12, "excerpt": "| OQ-02 | 남은 질문 |" }],
|
|
24
|
+
"resolutions": ["judge"],
|
|
25
|
+
"judgmentDraft": { "task": "20260101-sample", "questions": { "items": [{ "id": "OQ-02", "state": null, "at": null }] } }
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- `problems`는 수준(error 먼저), 코드, 첫 근거 줄의 파일, 줄 순으로 정렬한다. 나머지가 같으면 check 순서를 지킨다.
|
|
32
|
+
- `msg`는 텍스트 출력의 기호 뒤 문구와 같다.
|
|
33
|
+
- `subject`는 `{ kind, id }`이고 대상이 하나로 정해지지 않는 문제(고아 목록 등)는 `null`이다. `kind`는 그래프 노드 종류 이름(`task`, `step`, `journey`, `milestone`은 로드맵 항목, `release`는 마일스톤)이나 `adapter`·`config`다.
|
|
34
|
+
- `anchors`는 `{ file, line, excerpt? }` 배열이다. `line`이 `null`이면 파일 단위 근거다. `excerpt`는 120 코드 포인트까지 자른다.
|
|
35
|
+
- `resolutions` 값: `source`(원문을 규칙대로 고침), `judge`(판정 파일), `config`(설정), `code`(프로젝트 코드), `engine`(엔진 결함 보고).
|
|
36
|
+
- `judgmentDraft`는 판정으로 처리할 수 있는 문제에만 있다(아래 「판정 초안」).
|
|
37
|
+
|
|
38
|
+
`livemap check --strict`는 `tasks.*`·`judgment.*` 경고를 오류로 센다. 텍스트 줄 기호가 `✗`로 바뀌고 JSON의 `level`이 `error`가 된다. 과거 작업 채우기가 끝났는지 한 번 확인할 때 쓰고, 배포를 막는 CI 잡에는 배선하지 않는다.
|
|
39
|
+
|
|
40
|
+
## 판정 초안(judgmentDraft)
|
|
41
|
+
|
|
42
|
+
처리에 `judge`가 있는 작업 문서 문제는 `judgmentDraft`를 싣는다. 판정 파일(`semantic-authoring.md` 「판정 파일」)의 모양에서 원문을 읽어야 정할 값(`at`, `as`, `state`, `planFile`)을 `null`로 둔 초안이다.
|
|
43
|
+
|
|
44
|
+
| 코드 | 초안 |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `tasks.unread-definition`(표 첫 칸 결정) | `{ task, lines: [{ at: null, as: null, id }] }` |
|
|
47
|
+
| `tasks.unread-definition`(잔여 질문 행) | `{ task, questions: { items: [{ id, state: null, at: null }] } }`. `id`는 칸 원문의 번호 모양 그대로(`OQ-04·OQ-05` 행은 둘, `R-OQ-01`·`OQ-H2b`는 하나) |
|
|
48
|
+
| `tasks.unread-checklist`(계획 파일 불분명) | `{ task, planFile: null }` |
|
|
49
|
+
| `tasks.questions-unknown`(잔여 질문 절 없음) | `{ task, questions: { none: { at: null } } }` |
|
|
50
|
+
| `tasks.questions-open-done`(닫히지 않은 질문) | `{ task, questions: { items: [{ id, state: null, at: null }] } }` |
|
|
51
|
+
| `judgment.stale`(근거 조각 사라짐) | 낡은 판정 항목(`at: null`)과 `candidates: [{ file, line, excerpt }]`(같은 파일에서 그 번호를 가진 줄) |
|
|
52
|
+
|
|
53
|
+
`judgment.invalid`에는 초안이 없다. 파일 모양 문제는 파일 전체를, 근거 조각 문제는 그 항목만 적용하지 않으며 위반마다 한 건이다. 대상은 `{ kind: 'judgment', id: <작업 폴더> }`이고 첫 근거는 판정 파일(줄 `null`), 나머지는 근거 파일이다. `judgment.stale`이 난 판정이 맡던 규칙 문제(같은 줄의 후보, 같은 번호의 `tasks.questions-open-done`)는 다시 내지 않는다.
|
|
54
|
+
|
|
55
|
+
## 커밋 전 훅(check --staged)
|
|
56
|
+
|
|
57
|
+
`livemap check --staged`는 스테이징된 파일 중 작업 설정 `dir` 바로 아래 `<8자리 날짜>-` 작업 폴더 안 파일, 장부 파일(작업 설정 `index`), `map/judgments/*.json`만 대상으로 본다. 목록은 `git diff --cached --name-only --relative --no-renames`에서 얻는다.
|
|
58
|
+
|
|
59
|
+
- 오류로 세는 문제는 코드가 `tasks.`·`judgment.`로 시작하고 대상이 작업·장부·판정 파일이면서, 대상이 스테이징된 작업 폴더·판정 파일이거나 근거 줄이 스테이징된 대상 파일에 있는 것이다. 나머지 문제는 보이지 않는다.
|
|
60
|
+
- `tasks.ambiguous-ref`는 대상이 여정 단계이고 고칠 곳이 여정 파일이라 세지 않는다. 코드만 바꾼 커밋과 과거 작업의 남은 문제는 막지 않는다.
|
|
61
|
+
- 대상 파일이 없거나 git 저장소가 아니면 빌드하지 않고 종료 코드 0이다.
|
|
62
|
+
- 출력은 문제마다 `✗ <코드> <문구>`, `처리:`, `근거:`(파일:줄과 원문 조각), `판정 초안(judgmentDraft):` 한 줄 JSON이고, 끝 줄이 `map check --staged: 오류 n (…)`와 처리 안내다. 통과는 `map check --staged: 통과 (대상 파일 n)`, 대상 없음은 `map check --staged: 대상 없음`, git 저장소가 아니면 `map check --staged: git 저장소 아님, 건너뜀`이다. `--json`을 함께 주면 `check --json`과 같은 모양으로 낸다.
|
|
63
|
+
- 오류가 있으면 종료 코드 1이라 커밋이 멈춘다. 멈춘 출력을 받은 에이전트 세션이 원문을 식별자 줄 규칙대로 고치거나 판정 파일을 써서 스테이징하고 다시 커밋한다. `--no-verify`로 넘기지 않는다.
|
|
64
|
+
|
|
65
|
+
`livemap init`이 `.githooks/pre-commit`(`npx --no livemap check --staged`)을 만들고 `git config core.hooksPath .githooks`를 둔다. 이미 다른 `core.hooksPath`, 훅 관리자(`.husky`, lefthook 설정, `.pre-commit-config.yaml`), `.git/hooks/pre-commit`, 내용이 다른 `.githooks/pre-commit`이 있으면 덮지 않고 그 설정에 넣을 한 줄을 출력한다. `core.hooksPath`는 git 설정이라 클론마다 한 번 `livemap init`을 돌린다.
|
|
66
|
+
|
|
67
|
+
## 어댑터가 코드를 붙이는 법
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
g.issue('warn', '작업 문서', '완료 작업에 닫히지 않은 잔여 질문 1', {
|
|
71
|
+
code: 'tasks.questions-open-done',
|
|
72
|
+
subject: { kind: 'task', id: '20260101-sample' },
|
|
73
|
+
anchors: [{ file: 'tasks/20260101-sample/spec/final.md', line: 12, excerpt: '| OQ-02 | 남은 질문 |' }],
|
|
74
|
+
resolutions: ['judge'],
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- 세 인자 호출(1.1.0 계약)은 그대로 받고 check에서 코드 `adapter.issue`가 된다.
|
|
79
|
+
- 넷째 인자는 객체이고 키는 `code`·`subject`·`anchors`·`resolutions`·`judgmentDraft`만 쓴다. 모르는 키가 있으면 `throw`한다. `code`는 `<영역>.<이름>`(소문자·숫자·하이픈) 모양이어야 한다. 아래 표에 있는 코드는 표의 수준과 `level`이 같아야 한다. 형식이 틀리면 `throw`하고 그 어댑터는 failed가 된다.
|
|
80
|
+
- `subject`·`anchors`·`resolutions`를 빼면 `null`·`[]`·표의 처리 값이 들어간다. 표에 없는 프로젝트 코드는 처리 기본값이 `[]`다.
|
|
81
|
+
- 넷째 인자를 준 문제만 `graph.json`·`data.json` `issues[]`에 `code`·`subject`·`anchors`·`resolutions`(있으면 `judgmentDraft`)가 더해진다.
|
|
82
|
+
|
|
83
|
+
## 1.2.0 새 코드
|
|
84
|
+
|
|
85
|
+
| 코드 | 수준 | 대상 | 처리 | 뜻 |
|
|
86
|
+
|---|---|---|---|---|
|
|
87
|
+
| `tasks.unread-definition` | warn | 작업 | source·judge | 스펙 final 표 첫 칸의 DEC 번호, 잔여 질문 절에서 첫 칸이 번호 하나가 아닌 행 |
|
|
88
|
+
| `tasks.unread-checklist` | warn | 작업 | source·judge | 계획 파일이 없는 작업에 체크박스를 가진 루트 md가 둘 이상 |
|
|
89
|
+
| `tasks.stage-unknown` | warn | 작업 | source | 진행·대기 작업에 파이프라인 문서가 없음 |
|
|
90
|
+
| `tasks.questions-unknown` | warn | 작업 | source·judge | 스펙 final에 잔여 질문 절이 없음 |
|
|
91
|
+
| `tasks.questions-open-done` | warn | 작업 | judge | 완료 작업의 잔여 질문 행이 계획 항목으로 닫히지 않음 |
|
|
92
|
+
| `tasks.index-section-unknown` | warn | 장부 | source | 표시어가 없는 절에 작업 링크 행이 있음 |
|
|
93
|
+
| `tasks.ambiguous-ref` | warn | 단계 | source | 여정 refs 번호를 정의한 작업이 둘 이상 |
|
|
94
|
+
| `judgment.invalid` | error | 판정 파일 | judge | JSON·스키마 위반, 없는 파일·폴더, 조각을 가진 줄이 여럿, 번호 없는 근거 줄 |
|
|
95
|
+
| `judgment.stale` | warn | 판정 파일 | judge | 근거 조각을 가진 줄이 원문에 없음 |
|
|
96
|
+
| `router.unknown-api` | warn | 화면 | code·config | 리터럴 경로에 맞는 API 노드가 없음 |
|
|
97
|
+
| `router.hookapi-redundant` | warn | 설정 | config | hookApi 항목이 리터럴로도 연결됨(지워도 됨), 설정 키 하나에 한 건 |
|
|
98
|
+
| `router.hookapi-only` | warn | 설정 | code·config | hookApi로만 연결되는 항목, 설정 키 하나에 한 건 |
|
|
99
|
+
| `journey.api-not-observed` | warn | 단계 | source·code | 여정 `apis`에 있으나 어느 화면에서도 관측되지 않음 |
|
|
100
|
+
|
|
101
|
+
## 1.1.1 check 줄의 코드
|
|
102
|
+
|
|
103
|
+
문구는 1.1.1과 같다. 괄호 안 문구는 줄의 앞부분이다.
|
|
104
|
+
|
|
105
|
+
| 코드 | 수준 | 대상 | 처리 | 뜻 |
|
|
106
|
+
|---|---|---|---|---|
|
|
107
|
+
| `adapter.failed` | error | 어댑터 | code·engine | 어댑터가 throw함("어댑터 실패 …") |
|
|
108
|
+
| `adapter.issue` | 어댑터가 정함 | 어댑터 | — | 어댑터가 코드 없이 `g.issue` 세 인자로 낸 문제 |
|
|
109
|
+
| `floor.below` | error | 설정 | code·config·engine | 노드 수가 `floors` 바닥값 미만("바닥값 미달 …") |
|
|
110
|
+
| `journey.duplicate-id` | error | 여정 | source | 여정 id 중복 |
|
|
111
|
+
| `journey.no-steps` | error | 여정 | source | 여정에 장면 없음 |
|
|
112
|
+
| `journey.actor-unknown` | warn | 여정 | source | 여정·장면 배우가 배우 사전에 없음 |
|
|
113
|
+
| `step.duplicate-id` | error | 여정 | source | 한 여정 안에서 장면 id 중복 |
|
|
114
|
+
| `step.intent-empty` | warn | 단계 | source | 장면 intent 비어 있음 |
|
|
115
|
+
| `step.route-missing` | error | 단계 | source·code | 장면이 가리키는 라우트 없음 |
|
|
116
|
+
| `step.ref-unresolved` | error | 단계 | source | 장면 refs 번호를 정의한 작업 없음(참조 미해결) |
|
|
117
|
+
| `step.screen-not-live` | error | 단계 | source·code | 장면은 동작인데 화면이 실데이터가 아님 |
|
|
118
|
+
| `step.screen-live-early` | warn | 단계 | source | 장면은 planned·next인데 화면은 동작 |
|
|
119
|
+
| `step.no-evidence` | error | 단계 | source·code | 동작 주장에 관측 근거 없음 |
|
|
120
|
+
| `step.review-stale` | warn | 단계 | source | 장면 확인일 뒤 화면 변경(확인 필요) |
|
|
121
|
+
| `step.warning` | 문구에 따름 | 단계 | source | 위 규칙에 맞지 않는 장면 경고 문구(대체 코드) |
|
|
122
|
+
| `step.unknown-status` | error | 단계 | source | 장면 상태 어휘가 아님 |
|
|
123
|
+
| `step.planned-has-screen` | warn | 단계 | source | planned 장면에 화면이 있음 |
|
|
124
|
+
| `step.capture-missing` | warn | 단계 | source | 장면 캡처 파일 없음 |
|
|
125
|
+
| `roadmap.duplicate-id` | error | 로드맵 항목 | source | 로드맵 id 중복 |
|
|
126
|
+
| `roadmap.scene-missing` | error | 로드맵 항목 | source | 항목이 가리키는 장면 없음 |
|
|
127
|
+
| `roadmap.task-missing` | error | 로드맵 항목 | source | 항목이 가리키는 작업 폴더 없음 |
|
|
128
|
+
| `roadmap.dep-missing` | error | 로드맵 항목 | source | 선행 항목 없음 |
|
|
129
|
+
| `roadmap.unknown-status` | error | 로드맵 항목 | source | 항목 상태 어휘가 아님 |
|
|
130
|
+
| `roadmap.milestone-missing` | error | 로드맵 항목 | source | 항목이 가리키는 마일스톤 없음 |
|
|
131
|
+
| `roadmap.problem` | error | 로드맵 항목 | source | 위 규칙에 맞지 않는 로드맵 오류 문구(대체 코드) |
|
|
132
|
+
| `roadmap.running-no-task` | warn | 로드맵 항목 | source | 진행인데 작업 폴더가 없음 |
|
|
133
|
+
| `roadmap.running-no-milestone` | warn | 로드맵 항목 | source | 진행인데 마일스톤 없음 |
|
|
134
|
+
| `roadmap.running-open-deps` | warn | 로드맵 항목 | source | 진행인데 선행 미완 |
|
|
135
|
+
| `milestone.no-id` | error | 마일스톤 | source | 마일스톤 id 없음 |
|
|
136
|
+
| `milestone.duplicate-id` | error | 마일스톤 | source | 마일스톤 id 중복 |
|
|
137
|
+
| `milestone.unknown-status` | error | 마일스톤 | source | 마일스톤 상태 어휘가 아님 |
|
|
138
|
+
| `milestone.bad-date` | error | 마일스톤 | source | 완료일·목표일 날짜 형식 |
|
|
139
|
+
| `milestone.problem` | error | 마일스톤 | source | 위 규칙에 맞지 않는 마일스톤 오류 문구(대체 코드) |
|
|
140
|
+
| `milestone.done-open-items` | warn | 마일스톤 | source | 완료인데 미완료 항목 |
|
|
141
|
+
| `milestone.all-items-done` | warn | 마일스톤 | source | 항목이 모두 완료인데 상태가 완료 아님 |
|
|
142
|
+
| `milestone.running-items` | warn | 마일스톤 | source | 다음·대기·이후인데 진행 항목이 있음 |
|
|
143
|
+
| `milestone.no-items` | warn | 마일스톤 | source | 묶인 항목 없음 |
|
|
144
|
+
| `milestone.completed-on-mismatch` | warn | 마일스톤 | source | 완료일과 상태가 맞지 않음 |
|
|
145
|
+
| `milestone.warning` | warn | 마일스톤 | source | 위 규칙에 맞지 않는 마일스톤 경고 문구(대체 코드) |
|
|
146
|
+
| `milestone.multiple-running` | warn | 마일스톤 | source | 진행 마일스톤이 둘 이상 |
|
|
147
|
+
| `orphan.screens` | warn | 화면 | source | 여정에 없는 화면 |
|
|
148
|
+
| `orphan.apis` | warn | API | code·source | 어느 화면도 부르지 않는 API |
|
|
149
|
+
| `orphan.tests` | warn | 검사 | code | 라우트·API에 붙지 않는 검사 |
|
|
150
|
+
| `deploy.behind-unknown` | warn | 배포 | config | 배포 sha가 main 이력에 없어 뒤처짐을 계산하지 못함 |
|
package/docs/migrate.md
CHANGED
|
@@ -20,3 +20,18 @@
|
|
|
20
20
|
- 끝으로 `npm run map:budget`이 통과하고 스크린샷 `map/.out/overview-1440.png`이 전환 전과 같은 정보를 보이는지 본다.
|
|
21
21
|
|
|
22
22
|
로컬에서 고친 엔진을 끼워 볼 때는 `npm install --no-save --install-links <엔진 저장소 경로>`를 쓴다. `--install-links` 없이 폴더를 설치하면 심링크가 되어 예산 설정이 `@playwright/test`를 엔진 저장소 쪽에서 찾다 실패한다.
|
|
23
|
+
|
|
24
|
+
## 1.x → 2.0.0(예고)
|
|
25
|
+
|
|
26
|
+
2.0.0은 아직 나오지 않았다. 1.2.0까지 폐기를 예고했거나 1.x 호환 때문에 남긴 항목을 모아 지울 예정이다. 목록은 바뀔 수 있고, 판이 나오면 이 절을 이행 절차로 바꾼다. 1.x에서 미리 옮겨 두면 2.0.0 올림이 설정·참조 수정 없이 끝난다.
|
|
27
|
+
|
|
28
|
+
| 항목 | 2.0.0 예정 | 1.x에서 미리 할 일 |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| 설정 `router.hookApi` | 키를 지운다. 화면→API는 리터럴 관측만 쓴다 | `livemap check`의 `router.hookapi-redundant` 항목을 설정에서 지우고, `router.hookapi-only` 항목은 화면 코드가 경로 리터럴을 쓰게 고친다 |
|
|
31
|
+
| `data.json` `tasks[].dec`·`oq`, `overview.json` `counts.oq` | 지운다 | 화면·스크립트가 `openQuestions`·`counts.openQuestions`를 읽게 한다 |
|
|
32
|
+
| 1.0.1 잔여 필드(`line`, `running`, `waiting`, `tasks`, 최상위 `openQuestions`, `areas`, `recent`, `roadmap[]`) | 지운다 | `overview.json` 1.1.0 필드를 읽는다 |
|
|
33
|
+
| 결정·계획 항목 노드 id | 전역 번호(`DEC-57`)에서 `<작업 폴더>#<번호>`로 | 여정 `refs`를 `<작업 폴더>#<번호>` 한정 참조로 적는다(`tasks.ambiguous-ref` 0) |
|
|
34
|
+
| 노드 종류 이름 | `milestone` → `roadmapItem`, `release` → `milestone` | 프로젝트 어댑터·스크립트가 노드 종류 이름에 기대는 곳을 찾아 둔다 |
|
|
35
|
+
| 여정 파일 | md 여정 어댑터를 더할 수 있다 | 없음 |
|
|
36
|
+
| 읽기 상태 강제 | `partial`·`stale`·`unknown`을 기본으로 오류로 셀지 정한다 | `livemap check --strict`로 과거 작업 채우기가 끝났는지 본다 |
|
|
37
|
+
| `config.json` `engine` | `2` | 2.0.0으로 올리는 커밋에서 바꾼다. 엔진은 major가 다르면 멈추고 이 문서를 가리킨다 |
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
| 여정 파일(`semantic`) | 배우·목표·장면과 장면 상태 |
|
|
10
10
|
| 로드맵(`roadmap.file`) | 로드맵 항목과 마일스톤(아래 「로드맵과 마일스톤」) |
|
|
11
11
|
| 작업 장부(`tasks.index`) | 지금 실행 중·대기 중인 작업 |
|
|
12
|
-
| 작업 폴더 문서(`tasks.dir`) | 스펙·계획의 결정(DEC)·계획 항목(PN)·열린 질문 |
|
|
12
|
+
| 작업 폴더 문서(`tasks.dir`) | 스펙·계획의 결정(DEC)·계획 항목(PN)·열린 질문(아래 「작업 문서」) |
|
|
13
|
+
| 판정 파일(`map/judgments/`) | 규칙이 못 읽은 과거 작업 줄의 뜻(아래 「판정 파일」, 1.2.0부터) |
|
|
13
14
|
|
|
14
15
|
## 화면 어휘
|
|
15
16
|
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
- `screens` — 코드에 실제로 있는 라우트만. 없는 라우트는 `check`가 오류로 막는다. 여러 라우트를 한 장면에 둘 수 있다.
|
|
66
67
|
- `apis` — 화면에서 유도되지 않는 API를 명시할 때만.
|
|
67
68
|
- `capture` — `<config.captures.site>/<id>.jpg`(기본 `map/captures/<id>.jpg`). 없으면 장면 카드가 빈 칸으로 그려지고 `check`가 경고한다.
|
|
68
|
-
- `refs` — `DEC-nn`·`PN-nn`
|
|
69
|
+
- `refs` — `DEC-nn`·`PN-nn` 등 작업 문서 번호(정의한 작업으로 자동 연결), `<작업 폴더>#<번호>` 한정 참조(1.2.0부터), 위키 결정 slug(`decisions/<slug>.md`). 해결되지 않는 DEC/PN·한정 참조는 오류다. 규칙은 아래 「번호 참조와 한정 참조」.
|
|
69
70
|
- `reviewedAt` — 사람이 이 장면을 마지막으로 확인한 날. 그 뒤 화면 파일이 바뀌면 "확인 필요" 경고가 뜬다. 상태를 바꿀 때 같이 갱신한다.
|
|
70
71
|
- `actor` — 장면 단위 배우가 여정 배우와 다를 때만(예: 예약 여정 안의 "리조트 접수함").
|
|
71
72
|
- `actors` — 배우 사전. 여정·장면의 `actor`는 이 사전의 키를 적고, 화면은 값(이름)을 보인다. 사전이 있는데 여정 `actor`나 여정과 다른 장면 `actor`가 키에 없으면 `check`가 경고한다(1.1.0부터, `△ <여정 제목>: 배우 사전에 없는 값 <값>` 또는 `△ <여정 제목> › <장면 이름>: 배우 사전에 없는 값 <값>`). 사전이 없으면 검사하지 않는다. 경고라 종료 코드는 바뀌지 않는다. 사전에 키를 더하거나 `actor`를 사전 키로 고친다.
|
|
@@ -90,6 +91,109 @@
|
|
|
90
91
|
- 여정에 없는 화면은 "미분류"로 센다. 화면을 어딘가 억지로 넣기보다, 정말 어느 여정에도 안 속하면 그 화면이 필요한지 묻는다.
|
|
91
92
|
- `next`는 스펙도 없는 것이다. 스펙이 생기면 `planned`, 화면이 생기면 `mock`, 실데이터가 붙으면 `live`. 각 전이는 병합과 같은 커밋에서 적는다.
|
|
92
93
|
|
|
94
|
+
## 작업 문서
|
|
95
|
+
|
|
96
|
+
tasks 어댑터(`tasks.dir`, `tasks.index`)가 작업 폴더 문서에서 결정·계획 항목·열린 질문·단계·상태를 읽는 규칙이다(1.2.0). 엔진은 줄 모양으로만 읽고 원문을 고치지 않는다. 규칙이 못 읽은 줄은 조용히 0으로 세지 않고 "?"와 `tasks.*` 이슈(`issue-codes.md`)로 드러낸다. 백틱·물결 코드 펜스 안 줄은 읽지 않으므로 규칙 모양의 예시 줄은 펜스 안에 둔다.
|
|
97
|
+
|
|
98
|
+
### 파일 역할과 대체 규칙
|
|
99
|
+
|
|
100
|
+
| 역할 | 파일 |
|
|
101
|
+
|---|---|
|
|
102
|
+
| 스펙 | `spec/initial.md`, `spec/review-log.md`, `spec/final.md`. `spec/final.md`가 없고 작업 폴더 루트에 `final.md`가 있으면 그 파일을 스펙 final로 읽고 `specFile`·`readingNotes.spec`에 남긴다 |
|
|
103
|
+
| 계획 | `task_plan.md`, 없으면 `plan.md`. 둘 다 없으면 체크박스를 가진 루트 md가 하나일 때 그 파일(대체 규칙, 예: 단일 `spec.md`). 둘 이상이면 `tasks.unread-checklist`이고, 판정 파일 `planFile`이 고른다 |
|
|
104
|
+
| 단계 문서 | `phase/*.md`, `execution/*.md`, `verification.md`, `phase-verification.md`(단계 판정은 1.1.1 규칙 그대로) |
|
|
105
|
+
| 판정 | `map/judgments/<작업 폴더>.json`(아래 「판정 파일」) |
|
|
106
|
+
| 장부 | `tasks.index` |
|
|
107
|
+
|
|
108
|
+
`spec/`·`phase/`·`execution/` 안 체크박스는 계획 항목 후보가 아니다. 계획 파일이 있는 작업의 다른 루트 md 체크박스도 후보가 아니다.
|
|
109
|
+
|
|
110
|
+
### 식별자 줄 규칙
|
|
111
|
+
|
|
112
|
+
| 값 | 규칙이 읽는 줄 | 규칙이 못 읽어 이슈가 되는 줄 |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| 결정(스펙 final에서만 셈) | 목록 항목의 머리가 번호 하나: `- DEC-7 …`, `- **DEC-7** …`. 취소선 `~~DEC-7~~`은 폐기한 정의라 세지 않는다 | 스펙 final 표 첫 칸의 DEC 번호(`\| DEC-1 \|`, `\| DEC-1·DEC-2 \|`) → `tasks.unread-definition`. 제목 줄 DEC와 계획 파일의 DEC 줄(`## Decisions Made` 옮겨 적기)은 세지도 후보로 보지도 않는다 |
|
|
115
|
+
| 계획 항목(계획 파일에서만 셈) | 체크박스 목록 항목 전부. 머리 번호가 `[A-Z]{1,4}[0-9]?-[0-9]+` 모양(`PN-01`, `P0-01`, `DP-01`, `SR-01`)이면 그 번호의 정의로 둔다 | 계획 파일 없이 체크박스를 가진 루트 md가 둘 이상 → `tasks.unread-checklist` |
|
|
116
|
+
| 잔여 질문 | 제목에 "잔여"와 "질문"(또는 remaining과 question)이 든 절의 첫 표에서 첫 칸이 `OQ-<숫자>` 하나인 행. 첫 칸이 대시·"없음"인 행과, 표 대신 `없음:`으로 시작하는 한 줄은 0으로 읽는다. 그 절 밖의 `## 열린 질문` 표는 검토에서 답한 질문 기록이라 세지 않는다 | 첫 칸이 번호 하나가 아닌 행(`OQ-04·OQ-05`, `OQ-B1`, `~~OQ-01~~`, `R-OQ-01`) → `tasks.unread-definition`. 스펙 final에 절이 없으면 `tasks.questions-unknown` |
|
|
117
|
+
| 질문 닫힘 | 체크한 계획 항목의 설명(머리 번호·slug·실행 주체 표시 뒤 첫 콜론 다음)이 같은 번호로 시작함(`- [x] P0-04 oq01-x-vs-y: OQ-01 합의: …`, 숫자 앞 0 무시). 설명 중간의 언급은 닫힘으로 보지 않는다 | 완료 작업에 닫히지 않은 행 → `tasks.questions-open-done` |
|
|
118
|
+
| 단계 | 1.1.1 규칙(파이프라인 문서 유무) | 파이프라인 문서가 없으면 `reading.stage`가 `unknown`, 진행·대기 작업이면 `tasks.stage-unknown` |
|
|
119
|
+
|
|
120
|
+
새 작업을 쓸 때는 이렇게 적으면 판정이 필요 없다.
|
|
121
|
+
|
|
122
|
+
```markdown
|
|
123
|
+
## 결정 사항
|
|
124
|
+
|
|
125
|
+
- DEC-1 [결정 제목]: 근거
|
|
126
|
+
- ~~DEC-2~~ [폐기한 결정]: 근거
|
|
127
|
+
|
|
128
|
+
## 잔여 열린 질문
|
|
129
|
+
|
|
130
|
+
| ID | 질문 | 처리 권장 단계 |
|
|
131
|
+
|----|------|--------------|
|
|
132
|
+
| OQ-04 | 남은 질문 | 계획 |
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
질문이 없으면 표 대신 `없음: <근거>` 한 줄을 둔다. 계획 파일에서 잔여 질문 하나를 항목 하나로 옮기고 설명을 그 번호로 시작하면(`- [ ] P0-04 oq04-slug: OQ-04 합의: …`) 체크할 때 질문이 닫힌다.
|
|
136
|
+
|
|
137
|
+
1.1.1 필드와의 관계: `dec`는 새 문법(굵게 포함, 취소선 제외)으로 세고 화면에서는 뺐다. `oq`는 1.1.1 규칙(표 행 수) 그대로 두고 화면은 `openQuestions`를 쓴다. `pnDone`·`pnOpen`은 계획 파일(대체 규칙 포함)의 체크박스 전부다. `dec`·`oq`는 2.0.0 삭제 후보다.
|
|
138
|
+
|
|
139
|
+
### 장부 상태
|
|
140
|
+
|
|
141
|
+
- 장부 표에서 어느 칸이든 `(<폴더>/` 또는 `(<폴더>)` 링크를 가진 행만 센다. 문단 속 언급은 세지 않는다.
|
|
142
|
+
- 행을 감싼 절 제목의 표시어(대소문자 무시)로 상태를 정한다. 폐기 `폐기`·`cancel`·`abandon`, 완료 `완료`·`complete`·`done`·`shipped`, 대기 `대기`·`중단`·`보류`·`paused`·`blocked`, 진행 `진행`·`실행 장부`·`in progress`·`현재`·`active`. 한 제목에 여럿이면 이 순서로 앞선 것을 쓴다. 가장 깊은 제목부터 위로 올라가 표시어가 처음 나오는 제목을 쓴다.
|
|
143
|
+
- 같은 작업이 여러 절에 있으면 폐기, 진행, 완료, 대기 순으로 앞선 상태다. 계획 문서 머리의 `**폐기**`와 완료 행의 `**폐기**` 표시는 1.1.1처럼 폐기다.
|
|
144
|
+
- 표시어가 없는 절의 작업 링크 행은 상태를 정하지 않고 `tasks.index-section-unknown`이다.
|
|
145
|
+
- 장부 노드(개요 "실행 중"·"대기")는 1.1.1 절 이름 규칙(`현재 실행 장부`, `실행 대기·중단`) 그대로다.
|
|
146
|
+
|
|
147
|
+
### 번호 참조와 한정 참조
|
|
148
|
+
|
|
149
|
+
- 여정 `refs`의 번호(`DEC-57`, `PN-15`, `P0-03`, `SR-01` 모양)는 그 번호를 정의한 작업으로 잇는다. 결정·계획 항목 노드 id는 1.x 동안 전역 번호이고, 같은 번호를 정의한 작업은 결정 노드 `definers`에 모두 실린다.
|
|
150
|
+
- 정의한 작업이 하나면 그 작업으로 잇는다. 둘 이상이면 폴더 이름 순 첫 작업으로 잇되 읽기 상태가 `partial`이고 `tasks.ambiguous-ref`가 난다.
|
|
151
|
+
- 모호하면 `20260914-booking#DEC-57`처럼 작업 폴더를 붙인 한정 참조로 적는다. 그 폴더가 그 번호를 정의하지 않았으면 기존 "참조 미해결" 오류다.
|
|
152
|
+
- `DEC`·`PN`·`P<숫자>` 밖의 접두어가 풀리지 않으면 오류 대신 "번호 참조 대상 없음" 경고다. 잔여 질문 번호(`OQ-28`)는 정의로 보지 않아 이 경고가 된다.
|
|
153
|
+
- 노드 id를 `<폴더>#<번호>`로 바꾸는 일은 2.0.0 후보다.
|
|
154
|
+
|
|
155
|
+
### 판정 파일
|
|
156
|
+
|
|
157
|
+
규칙이 줄 모양으로 뜻을 정할 수 없는 곳(표로 적은 결정, 답이 난 질문, 계획 파일이 불분명한 작업)을 에이전트나 사람이 판정해 `map/judgments/<작업 폴더>.json`에 적는다. 원문 보존이 필요한 완료·폐기 작업에 쓰고, 진행 중인 작업은 원문을 위 규칙대로 고친다. 엔진은 판정 파일을 읽고 원문과 대조만 한다. 사람은 커밋 diff로 판정을 검토한다.
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"schema": 1,
|
|
162
|
+
"task": "20260101-booking",
|
|
163
|
+
"by": "agent",
|
|
164
|
+
"lines": [
|
|
165
|
+
{ "at": { "file": "tasks/20260101-booking/spec/final.md", "text": "| DEC-1 | 결제 링크는 서버에서 만들고" }, "as": "definition", "id": "DEC-1" }
|
|
166
|
+
],
|
|
167
|
+
"questions": {
|
|
168
|
+
"items": [
|
|
169
|
+
{ "id": "OQ-28", "state": "resolved", "at": { "file": "tasks/20260102-signup/spec.md", "text": "초대(OQ-28) 종결됨.** 계정 없는 이메일로 초대" } }
|
|
170
|
+
]
|
|
171
|
+
},
|
|
172
|
+
"note": "가입 작업이 검사로 닫음"
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
| 필드 | 값 | 적용 |
|
|
177
|
+
|---|---|---|
|
|
178
|
+
| `schema` | `1` | 해당 없음 |
|
|
179
|
+
| `task` | 파일 이름과 같은 작업 폴더 이름 | 해당 없음 |
|
|
180
|
+
| `by` | `agent`·`human` | 화면 "판정" 표시에 함께 보인다 |
|
|
181
|
+
| `planFile` | 작업 폴더 기준 md 경로 | 그 파일의 체크박스를 계획 항목으로 센다. 체크 여부는 원문을 따른다 |
|
|
182
|
+
| `lines[]` | `{ at, as, id }`, `as`는 `definition`·`ignore` | `definition`은 정의를 더하고(`id` 필수, 근거 줄에 그 번호가 있어야 함) `ignore`는 그 줄을 후보에서 뺀다. 참조·옮겨 적기·예시는 `ignore`로 적고 까닭은 `note`에 쓴다 |
|
|
183
|
+
| `questions.none` | `{ at }` | 열린 질문 0. `items`와 함께 쓰지 않는다 |
|
|
184
|
+
| `questions.items[]` | `{ id, state, at }`, `id`는 원문 표기 그대로(`OQ-B1` 허용), `state`는 `open`·`resolved` | 그 번호의 규칙 판정을 덮는다. 잔여 질문 절이 없는 작업은 `items`가 열린 질문 목록이 된다 |
|
|
185
|
+
| `note` | 문장 | 작업 상세에 보인다 |
|
|
186
|
+
|
|
187
|
+
- 근거 조각 `at`: `file`은 프로젝트 기준 경로, `text`는 그 파일의 한 줄 안에 그대로 들어 있는 20자(코드 포인트) 이상 조각이다. 줄 번호는 쓰지 않으므로 위에 줄이 더해져도 판정이 살아 있다.
|
|
188
|
+
- 조각을 가진 줄이 하나면 적용하고 값의 읽기 상태가 `judged`다. 0개면 `judgment.stale`(원문이 바뀜)이고 값은 규칙 값으로 돌아가며 상태는 `stale`이다. 둘 이상이면 `judgment.invalid`(조각을 늘린다)다.
|
|
189
|
+
- 근거는 판정하는 작업이나 그 일을 닫은 작업의 폴더 문서에서 고른다. 장부·로드맵·위키는 계속 고쳐지므로 근거로 삼으면 곧 낡는다. 엔진은 위치를 강제하지 않는다.
|
|
190
|
+
- 파일 모양이 틀리면(JSON, 필드 형식, 없는 폴더·`planFile`, `none`과 `items` 동시) 파일 전체를 적용하지 않고, 근거 조각이 틀린 항목은 그 항목만 적용하지 않는다. 둘 다 `judgment.invalid`(error, 종료 코드 1)다. 모르는 키는 무시한다.
|
|
191
|
+
- 적용 순서는 규칙 읽기, `planFile`, `lines`, `questions`, 읽기 상태다. 판정은 체크박스의 체크 여부, 장부 상태, 단계를 바꾸지 못한다.
|
|
192
|
+
- `check --json`의 `tasks.*` 문제는 `judgmentDraft`(판정 초안)를 싣는다. `at`·`as`·`state`를 채우면 판정 파일이 된다. `judgment.stale`의 초안은 같은 파일에서 그 번호를 가진 줄을 `candidates`로 싣는다.
|
|
193
|
+
- 판정은 근거 줄이 있다는 것만 증명하고 뜻이 옳은지는 증명하지 않는다. 그래서 판정 값은 작업 상세와 더보기에 "판정"으로 보인다.
|
|
194
|
+
|
|
195
|
+
작업 문서를 고친 커밋에서 판정이 낡지 않게 `livemap init`이 커밋 전 훅을 설치한다(README 「커밋 전 훅」). 스테이징한 작업 폴더·판정 파일에 걸린 `tasks.*`·`judgment.*` 문제가 있으면 커밋이 멈추고 출력에 판정 초안이 나온다.
|
|
196
|
+
|
|
93
197
|
## 등급과 상태의 관계
|
|
94
198
|
|
|
95
199
|
상태는 사람이 적는 주장이고 등급은 코드가 뒷받침하는 정도다. `live` 장면에만 붙는다.
|
|
@@ -99,7 +203,7 @@
|
|
|
99
203
|
| D | 여정 파일의 주장뿐. 화면이 실데이터가 아님 → 경고 |
|
|
100
204
|
| C | 모든 화면이 실데이터(스캔 관측) |
|
|
101
205
|
| B | 그 화면·API·함수를 다루는 검사 파일이 있음 |
|
|
102
|
-
| A | 그
|
|
206
|
+
| A | 그 검사 파일 중 하나 이상이 최신 결과에서 1건 이상 돌고 실패 0(`npm run test:report`). 최신은 결과 커밋 뒤 문서 경로 밖 변경이 없다는 뜻이다(1.2.0, `test-results.md`) |
|
|
103
207
|
|
|
104
208
|
`live`라고 적었는데 D가 나오면 주장이 틀린 것이다. 상태를 `mock`으로 내리거나 화면을 고친다. 상황판은 둘 중 무엇이 맞는지 판단하지 않고 어긋남만 보여준다.
|
|
105
209
|
|