polydeukes 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.ko.md +30 -70
  2. package/README.md +31 -84
  3. package/dist/baseline.d.ts +37 -1
  4. package/dist/baseline.js +68 -1
  5. package/dist/bin.d.ts +3 -4
  6. package/dist/bin.js +79 -93
  7. package/dist/covenant/bash-line.d.ts +130 -0
  8. package/dist/covenant/bash-line.js +566 -0
  9. package/dist/covenant/declaration-engine.d.ts +108 -0
  10. package/dist/covenant/declaration-engine.js +415 -0
  11. package/dist/covenant/discipline.d.ts +103 -0
  12. package/dist/covenant/discipline.js +667 -0
  13. package/dist/covenant/dispatch.d.ts +153 -0
  14. package/dist/covenant/dispatch.js +237 -0
  15. package/dist/covenant/extract-steps.d.ts +122 -0
  16. package/dist/covenant/extract-steps.js +0 -0
  17. package/dist/covenant/mention.d.ts +117 -0
  18. package/dist/covenant/mention.js +209 -0
  19. package/dist/covenant/module.d.ts +25 -0
  20. package/dist/covenant/module.js +23 -0
  21. package/dist/covenant/mutation-rules.d.ts +40 -0
  22. package/dist/covenant/mutation-rules.js +146 -0
  23. package/dist/covenant/relations.d.ts +46 -0
  24. package/dist/covenant/relations.js +68 -0
  25. package/dist/covenant/run-covenant.d.ts +95 -0
  26. package/dist/covenant/run-covenant.js +164 -0
  27. package/dist/covenant/self-mod.d.ts +55 -0
  28. package/dist/covenant/self-mod.js +84 -0
  29. package/dist/covenant/shell-evidence.d.ts +38 -0
  30. package/dist/covenant/shell-evidence.js +247 -0
  31. package/dist/covenant/shell-mod.d.ts +84 -0
  32. package/dist/covenant/shell-mod.js +213 -0
  33. package/dist/covenant/supply.d.ts +54 -0
  34. package/dist/covenant/supply.js +50 -0
  35. package/dist/covenant/transcript-mod.d.ts +60 -0
  36. package/dist/covenant/transcript-mod.js +254 -0
  37. package/dist/covenant/ttl-witness.d.ts +47 -0
  38. package/dist/covenant/ttl-witness.js +80 -0
  39. package/dist/covenant-check.d.ts +72 -39
  40. package/dist/covenant-check.js +260 -121
  41. package/dist/diff-ir.d.ts +28 -0
  42. package/dist/diff-ir.js +310 -0
  43. package/dist/docs/README.ko.md +60 -0
  44. package/dist/docs/README.md +64 -0
  45. package/dist/docs/catalog.json +480 -0
  46. package/dist/docs/concepts/judgment.ko.md +113 -0
  47. package/dist/docs/concepts/judgment.md +113 -0
  48. package/dist/docs/how-to/configure-project.ko.md +99 -0
  49. package/dist/docs/how-to/configure-project.md +98 -0
  50. package/dist/docs/how-to/connect-surfaces.ko.md +120 -0
  51. package/dist/docs/how-to/connect-surfaces.md +121 -0
  52. package/dist/docs/how-to/write-disciplines.ko.md +179 -0
  53. package/dist/docs/how-to/write-disciplines.md +181 -0
  54. package/dist/docs/index.json +2280 -0
  55. package/dist/docs/reference/cli/covenant-check.ko.md +148 -0
  56. package/dist/docs/reference/cli/covenant-check.md +153 -0
  57. package/dist/docs/reference/cli/docs.ko.md +97 -0
  58. package/dist/docs/reference/cli/docs.md +95 -0
  59. package/dist/docs/reference/cli/explain.ko.md +84 -0
  60. package/dist/docs/reference/cli/explain.md +89 -0
  61. package/dist/docs/reference/cli/init.ko.md +119 -0
  62. package/dist/docs/reference/cli/init.md +119 -0
  63. package/dist/docs/reference/configuration/index.ko.md +553 -0
  64. package/dist/docs/reference/{configuration.md → configuration/index.md} +215 -90
  65. package/dist/docs/reference/packages/adapter-claude-code.ko.md +110 -0
  66. package/dist/docs/reference/{adapter-claude-code.md → packages/adapter-claude-code.md} +47 -19
  67. package/dist/docs/reference/packages/adapter-grok.ko.md +77 -0
  68. package/dist/docs/reference/packages/adapter-grok.md +79 -0
  69. package/dist/docs/reference/packages/core.ko.md +174 -0
  70. package/dist/docs/reference/{core.md → packages/core.md} +74 -12
  71. package/dist/docs/reference/packages/polydeukes.ko.md +195 -0
  72. package/dist/docs/reference/packages/polydeukes.md +204 -0
  73. package/dist/docs/reference/packages/sdk-ts.ko.md +161 -0
  74. package/dist/docs/reference/packages/sdk-ts.md +164 -0
  75. package/dist/docs/troubleshooting.ko.md +144 -0
  76. package/dist/docs/troubleshooting.md +103 -151
  77. package/dist/docs/tutorials/first-judgment.ko.md +82 -0
  78. package/dist/docs/tutorials/first-judgment.md +81 -0
  79. package/dist/docs-catalog.d.ts +25 -0
  80. package/dist/docs-catalog.js +450 -0
  81. package/dist/docs-library.d.ts +23 -0
  82. package/dist/docs-library.js +347 -0
  83. package/dist/docs-markdown.d.ts +32 -0
  84. package/dist/docs-markdown.js +150 -0
  85. package/dist/docs-query.d.ts +11 -40
  86. package/dist/docs-query.js +28 -122
  87. package/dist/docs-types.d.ts +105 -0
  88. package/dist/docs-types.js +2 -0
  89. package/dist/explain.d.ts +1 -1
  90. package/dist/explain.js +37 -22
  91. package/dist/load-config.d.ts +1 -1
  92. package/dist/load-config.js +1 -1
  93. package/dist/pre-state-reader.d.ts +1 -1
  94. package/dist/pre-state-reader.js +1 -1
  95. package/dist/scaffold-project.d.ts +2 -2
  96. package/dist/scaffold-project.js +9 -3
  97. package/dist/schema/polydeukes.schema.json +12 -0
  98. package/dist/worktree-reader.d.ts +19 -0
  99. package/dist/worktree-reader.js +30 -0
  100. package/package.json +3 -17
  101. package/dist/claude-code-hook.d.ts +0 -79
  102. package/dist/claude-code-hook.js +0 -372
  103. package/dist/claude-code.d.ts +0 -6
  104. package/dist/claude-code.js +0 -6
  105. package/dist/covenant-module.d.ts +0 -25
  106. package/dist/covenant-module.js +0 -42
  107. package/dist/docs/configuration.md +0 -103
  108. package/dist/docs/installation.md +0 -241
  109. package/dist/docs/reference/adapter-git.md +0 -100
  110. package/dist/docs/reference/covenant.md +0 -116
  111. package/dist/docs/reference/polydeukes.md +0 -315
  112. package/dist/index.d.ts +0 -22
  113. package/dist/index.js +0 -21
  114. package/dist/init-claude-code.d.ts +0 -52
  115. package/dist/init-claude-code.js +0 -469
  116. package/dist/init-grok.d.ts +0 -51
  117. package/dist/init-grok.js +0 -242
@@ -0,0 +1,164 @@
1
+ # `@polydeukes/sdk-ts`
2
+
3
+ **English** · [한국어](sdk-ts.ko.md)
4
+
5
+ > **One verb from TypeScript to the judge** — hand a covenant input IR to
6
+ > `pdks covenant check` and read the verdict back as a value.
7
+ >
8
+ > Alpha. Install it next to `polydeukes` and `@polydeukes/core`, which it names as
9
+ > `peerDependencies`.
10
+
11
+ <a id="ownership"></a>
12
+ ## What this package owns
13
+
14
+ The spawn and the translation of an exit status into a value, and nothing else. It locates the
15
+ `polydeukes` install of the project being judged, runs its bin with the input on stdin, and
16
+ returns what the child answered. No judgment logic lives here: the branches are whether the
17
+ umbrella resolved and what status the child left with.
18
+
19
+ | Unit | What it does |
20
+ |---|---|
21
+ | `checkCovenant` | Spawns `pdks covenant check` in the judged project and returns the verdict |
22
+ | Umbrella resolution | Finds `polydeukes` in the install graph of `repoRoot` and reads its `pdks` bin |
23
+ | Verdict translation | Exit `0` is `upheld`, exit `2` is `blocked`, everything else is `unjudged` |
24
+
25
+ This package writes no telemetry row. Every row a run leaves is written by the child process,
26
+ where the judgment happened, so one call still leaves one row.
27
+
28
+ <a id="install"></a>
29
+ ## Install
30
+
31
+ ```sh
32
+ pnpm add @polydeukes/sdk-ts polydeukes @polydeukes/core
33
+ ```
34
+
35
+ There is no bin and no install step. The umbrella supplies the judge the SDK spawns, and the
36
+ core supplies the `CovenantInput` type the caller fills in.
37
+
38
+ <a id="verb"></a>
39
+ ## The verb
40
+
41
+ ```ts
42
+ import { checkCovenant } from '@polydeukes/sdk-ts';
43
+
44
+ const verdict = await checkCovenant({
45
+ repoRoot: '/path/to/the/project',
46
+ input: {
47
+ toolCalls: [
48
+ {
49
+ name: 'writeFile',
50
+ args: { path: 'src/index.ts', content: 'export const answer = 42;\n' },
51
+ fileChange: {
52
+ kind: 'modify',
53
+ path: 'src/index.ts',
54
+ pre: 'export const answer = 41;\n',
55
+ post: 'export const answer = 42;\n',
56
+ },
57
+ },
58
+ ],
59
+ subagentSpawns: [],
60
+ userMessages: [],
61
+ tools: { mutating: ['writeFile', 'rm'], shell: ['exec'], commandArgs: ['command'] },
62
+ },
63
+ });
64
+ ```
65
+
66
+ The IR is the caller's. This package neither reads it nor completes it: it adds no `session`,
67
+ no `actor`, and no roster of its own, and the `tools` values above are the caller's own tool
68
+ names. `subagentSpawns` and `userMessages` are required collections, so a caller with neither
69
+ sends the empty arrays. A `world` key is refused by the judge — the runner reads the world from
70
+ the project on disk, and a client choosing the world would be choosing what is judged.
71
+
72
+ <a id="spec"></a>
73
+ ## The spec
74
+
75
+ ```ts
76
+ type CheckCovenantSpec = {
77
+ repoRoot: string;
78
+ input: CovenantInput;
79
+ enforce?: 'advise' | 'block';
80
+ spawn?: (spec: CheckCovenantSpawnSpec) => Promise<{ status: number | null; stderr: string }>;
81
+ };
82
+
83
+ type CheckCovenantSpawnSpec = { command: string; args: string[]; cwd: string; stdin: string };
84
+ ```
85
+
86
+ | Field | What it is |
87
+ |---|---|
88
+ | `repoRoot` | The project being judged: config discovery, the world axis, the child's cwd, and the install graph the umbrella is found in |
89
+ | `input` | The caller's own IR, sent verbatim as the child's stdin |
90
+ | `enforce` | The observer's posture for the whole run. **Absent is `block`** |
91
+ | `spawn` | An injected spawn seam. Absent, the child runs under this process's node executable |
92
+
93
+ **`enforce` defaults to `block`.** That is the surface's level, not an entry's: protected paths
94
+ and entries carrying `enforce: block` stop the call, and every other break is recorded
95
+ `advised` at exit 0. An entry's own level composes with it lenient-side-wins, as on every other
96
+ surface. `@polydeukes/adapter-claude-code` and `@polydeukes/adapter-grok` spawn the judge at
97
+ the same level.
98
+
99
+ The default spawn inherits no file descriptor. A caller may hold none of its own, and an
100
+ inherited stdout that is closed would kill the child with EPIPE before it answered. stderr is
101
+ collected and returned; stdout is drained and dropped, because the judge writes no verdict
102
+ there.
103
+
104
+ <a id="verdicts"></a>
105
+ ## The three verdicts
106
+
107
+ ```ts
108
+ type CheckCovenantVerdict =
109
+ | { verdict: 'upheld'; advisories: string }
110
+ | { verdict: 'blocked'; reason: string }
111
+ | { verdict: 'unjudged'; reason: string };
112
+ ```
113
+
114
+ | Verdict | Child status | What it means for the caller |
115
+ |---|---|---|
116
+ | `upheld` | `0` | The call was judged and nothing blocked it. `advisories` is the child's stderr verbatim, carrying any advisory lines the run produced. Proceed |
117
+ | `blocked` | `2` | The call was judged and something blocked it. `reason` is the child's stderr verbatim. Do not proceed |
118
+ | `unjudged` | anything else, or no umbrella | No judgment happened. `reason` says which. Reading it as an uphold would let an uninstalled judge pass every call |
119
+
120
+ **There is no valve, and the reason is the substitute.** An unattended real-time caller has no
121
+ TTY and no human turn, so this package takes no witness argument and invents no session.
122
+ `blocked.reason` and `upheld.advisories` come back as data instead, and the consumer decides
123
+ where the text goes — an issue, a log, or back to the model. The posture rules for a config
124
+ author and for a consumer are in [write disciplines](../../how-to/write-disciplines.md#posture).
125
+
126
+ <a id="failure"></a>
127
+ ## A failure example
128
+
129
+ When the project has no `polydeukes` installed there is nothing to spawn, and the verb says so
130
+ rather than answering `upheld`:
131
+
132
+ ```ts
133
+ const verdict = await checkCovenant({ repoRoot: '/tmp/project-without-polydeukes', input });
134
+
135
+ // {
136
+ // verdict: 'unjudged',
137
+ // reason: 'no polydeukes in the install graph of /tmp/project-without-polydeukes:
138
+ // install it to have this input judged',
139
+ // }
140
+ ```
141
+
142
+ No child process runs, and the telemetry log gains nothing: the row is written where the
143
+ judgment happens, and no judgment happened.
144
+
145
+ <a id="limits"></a>
146
+ ## Declared limits
147
+
148
+ - **The caller builds the IR.** The tool roster, the pre-state, and the envelope are the
149
+ host's facts, so a consumer that knows them fills them in. This package supplies none of
150
+ them.
151
+ - **The SDK exposes the session surface only.** The input travels as an IR on stdin, which is
152
+ what makes the run a session-surface judgment. A caller that has a finished change set pipes
153
+ a unified diff to `pdks covenant check --diff` from its shell instead.
154
+ - **No telemetry row is written here.** Every row comes from the child.
155
+ - **An `unjudged` verdict is not a pass.** It records that the judge did not answer, and the
156
+ consumer decides what a project without a judge is allowed to do.
157
+
158
+ <a id="see-also"></a>
159
+ ## See also
160
+
161
+ - [`pdks covenant check`](../cli/covenant-check.md)
162
+ - [`polydeukes`](polydeukes.md)
163
+ - [`@polydeukes/core`](core.md)
164
+ - [Configuration reference](../configuration/index.md)
@@ -0,0 +1,144 @@
1
+ # Polydeukes 문제 해결
2
+
3
+ [English](./troubleshooting.md) · **한국어**
4
+
5
+ 정책을 바꾸기 전에 어느 단계에서 실패했는지 확인하세요. 세션이 복구 명령까지 차단한다면
6
+ 본인의 터미널에서 실행합니다. `pdks docs`는 동봉 문서가 온전하면 프로젝트 설정이나
7
+ 판정 관련 패키지가 없어도 사용할 수 있습니다.
8
+
9
+ <a id="no-config"></a>
10
+ ## 설정 파일이 없을 때
11
+
12
+ 프로젝트 루트 바로 아래에 `polydeukes.config.yaml`, `polydeukes.config.yml`,
13
+ `polydeukes.config.json` 중 하나도 없으면 설정이 필요한 명령은 종료 코드 2를 반환합니다.
14
+ Git에서 원래 파일을 복원하세요. 새 프로젝트라면 `pdks init`, `pdks-claude-code init`,
15
+ `pdks-grok init`로 만들 수 있습니다. 이후 `pdks explain`을 실행합니다. 설정이 없다고
16
+ 기본 정책으로 대신 실행하지는 않습니다.
17
+
18
+ <a id="multiple-config"></a>
19
+ ## 설정 파일이 여러 개일 때
20
+
21
+ 오류 메시지에 중복된 파일명이 나옵니다. 필요한 내용을 합친 뒤 허용되는 파일명 하나만
22
+ 남기세요. 로더는 어느 파일을 사용할지 임의로 선택하지 않습니다. 정리한 뒤
23
+ `pdks explain`을 다시 실행합니다.
24
+
25
+ <a id="invalid-config"></a>
26
+ ## 설정 오류
27
+
28
+ 구문이나 스키마 오류가 있으면 종료 코드 2와 함께 파일명을 알립니다. 스키마 오류는 문제가
29
+ 있는 필드도 알려 줍니다. 잘못된 YAML, 사용자 정의 태그, 알 수 없는 필드, 비어 있는
30
+ `languages` 객체를 고치세요. YAML 태그는 파서가 실행할 수 없더라도 거부합니다.
31
+ 설정은 실행할 코드가 아니라 데이터이기 때문입니다.
32
+
33
+ `protectedPath:`나 `adaptors:` 같은 오타는 거부합니다. 다만 어댑터 이름에는 확장 가능성을
34
+ 남겨 두었습니다. 아무도 구현하지 않은 네임스페이스도 설정으로 읽히지만 읽는 쪽이 없습니다.
35
+ 수정한 뒤 `pdks explain`에서 실제 등록 내용을 확인하세요.
36
+
37
+ <a id="grok-witness"></a>
38
+ ## Grok 증인
39
+
40
+ 훅을 아직 읽지 않은 문제와 증인(witness) 밸브를 사용할 수 없는 문제는 구별해야 합니다.
41
+
42
+ - `pdks-grok init` 실행 뒤 Hooks 탭을 다시 불러오거나 새 세션을 시작하세요. 실제 도구를
43
+ 호출하고 텔레메트리를 확인합니다. 설치기가 성공했다고 해서 열린 세션에 훅이 적용됐다는
44
+ 뜻은 아닙니다.
45
+ - Grok의 ACP 대화 기록에는 현재 세션 증인 밸브가 요구하는 Claude 형식의 인간 메시지가
46
+ 없습니다. 다시 불러와도 이 기능이 생기지는 않습니다. 복구가 필요하면 Grok에 Claude용
47
+ 증인 토큰을 보내려 하지 말고 본인의 터미널에서 작업하세요.
48
+
49
+ 커밋 증인은 해당 스테이징 검사만 허용합니다. 차단된 Grok 도구 호출까지 허용하지 않습니다.
50
+
51
+ <a id="config-fault"></a>
52
+ ## 선언을 판정으로 구성하지 못할 때
53
+
54
+ 설정은 읽었지만 어떤 항목을 판정할 수 있는 형태로 컴파일하지 못한 경우입니다. 적용 범위가
55
+ 일치하면 `skipped`와 사유 `config-fault`를 기록합니다. `pdks explain`에서도 원인을 볼 수
56
+ 있습니다. 추출 단계 이름, 인수, 정규식 문법, 쌍과 단일 추출 결과의 사용을 확인하세요.
57
+ 지목된 선언을 고친 뒤 같은 관측으로 다시 검사합니다. 미판정은 정상 판정이 아닙니다.
58
+
59
+ <a id="judge-cannot-be-loaded"></a>
60
+ ## 판정기를 불러올 수 없을 때
61
+
62
+ 패키지나 판정 모듈이 빠져 있으면 작업을 차단합니다. 본인의 터미널에서 패키지를 다시
63
+ 설치하거나 워크스페이스 전체를 빌드하세요. 생성된 훅은 설치된 패키지에 판정을 맡기며,
64
+ 판정기를 별도로 복사해 가지고 있지 않습니다. 복구 뒤 실제 호출로 다시 확인합니다.
65
+ 텔레메트리를 불러오기 전에 실패했다면 기록이 전혀 남지 않을 수도 있습니다.
66
+
67
+ 세션 훅은 메시지 앞에 `covenant hook failed closed:`를, 커밋 검사는
68
+ `covenant check failed closed:`를 붙입니다. 실제로 보게 되는 두 형태는 다음과 같습니다.
69
+
70
+ ```text
71
+ covenant hook failed closed: Cannot find package 'polydeukes' imported from …
72
+ covenant check failed closed: the covenant judges could not be loaded from … — run 'pnpm build' to rebuild them: Cannot find module './self-mod.js' …
73
+ ```
74
+
75
+ 첫째는 설치된 패키지가 없는 경우이고, 둘째는 소스 체크아웃에서 판정기 빌드 산출물이 없거나
76
+ 일부만 있는 경우입니다.
77
+
78
+ <a id="reading-verdict"></a>
79
+ ## 판정 결과 읽기
80
+
81
+ | 기록 | 의미 |
82
+ |---|---|
83
+ | `passed` | 관측한 입력을 판정했으며 약속(covenant)을 지켰습니다. |
84
+ | `blocked` | 위반 때문에 작업을 멈췄습니다. |
85
+ | `witnessed` | 차단 결과가 나왔지만 증인 밸브를 통해 작업을 허용했습니다. |
86
+ | `advised` | 위반을 기록하되 작업을 멈추지 않았습니다. 두 표면 모두에서 나올 수 있습니다. |
87
+ | `skipped` | 적용 범위가 일치한 등록을 판정할 수 없었습니다. 정상 판정이 아닙니다. |
88
+ | `unattributed` | 기준 상태와 비교해 판정으로 설명되지 않는 보호 대상의 변경을 발견했거나, 유효한 기준 상태를 읽지 못했습니다. 판정 결과가 아니라 관측 기록입니다. |
89
+
90
+ 판정은 기본적으로 `.polydeukes/roi.log`에 추가됩니다. `telemetry.logPath`로 위치를 바꿀 수
91
+ 있습니다. 로그 쓰기에 실패하더라도 판정 결과는 바뀌지 않습니다. 종료 코드 0은 작업을
92
+ 계속해도 된다는 뜻이지 모든 규율을 지켰다는 뜻은 아닙니다.
93
+
94
+ <a id="opening-a-blocked-call"></a>
95
+ ## 차단된 호출을 증언으로 허용하기
96
+
97
+ 지원되는 Claude Code 세션에서 설정된 증인 토큰을 인간 메시지의 첫 줄에 단독으로 입력한 뒤,
98
+ 유효 시간 안에 다시 시도합니다. 보호 대상을 의도적으로 편집하기 전에 입력해도 됩니다.
99
+ 먼저 한 번 차단돼야 하는 것은 아닙니다. 토큰은 비밀이 아니며, 밸브는 사람이 직접 입력했는지
100
+ 확인하고 차단 판정에만 적용됩니다. 증언으로 허용한 호출은 `witnessed` 행을 새로 남깁니다.
101
+ 앞서 기록한 `blocked` 행을 고치는 것이 아닙니다.
102
+
103
+ 모듈 누락처럼 판정기를 조립하지 못하게 하는 오류는 증인으로 해결할 수 없습니다.
104
+
105
+ <a id="blocked-commit"></a>
106
+ ## 커밋이 차단됐을 때
107
+
108
+ 변경 집합 표면은 사람에게 묻지 않습니다. 기본값에서는 모든 판정에 종료 코드 0을 내고 위반을
109
+ `advised`로 기록합니다. 종료 코드 2는 검사를 `--enforce block`으로 실행했을 때(보호 경로나
110
+ `enforce: block` 항목의 위반) 또는 판정 자체가 불가능할 때만 냅니다. 커밋을 멈출지는 훅
111
+ 배선이 정합니다. 종료 코드를 따르는 훅은 커밋을 멈추고, 무시하는 훅은 멈추지 않습니다. Git은
112
+ 커밋 실패를 다른 0이 아닌 코드로 알릴 수도 있습니다.
113
+
114
+ 일반 규율 항목은 자신이 `enforce: block`을 적고 검사가 `--enforce block`으로 실행될 때만
115
+ 작업을 멈춥니다. 설정에는 기본값 `advise`인 항목을 승격하는 것이 없습니다. 수준 변경은
116
+ 정책을 바꾸는 결정이지 필수 복구 절차가 아닙니다. 판정된 위반을 통과시키려면 설정이 아니라
117
+ 훅 명령에서 `--enforce block`을 빼세요. 행은 그대로 남습니다.
118
+
119
+ <a id="skipped-rows-on-the-change-set-surface"></a>
120
+ ## 변경 집합 표면의 미판정 기록
121
+
122
+ 대화 기록을 읽는 선언에 `supply: { session: 'pass' }`가 있으면 세션이 없는 변경 집합 표면에서는
123
+ `supply-pass`를 기록합니다. 이 약속은 세션 표면에서 확인하세요. 미판정은 과거 작업을
124
+ 검증했다는 뜻이 아닙니다. 다른 채널을 공급할 수 없으면 `no-observation`이 나올 수도
125
+ 있습니다. 모든 자료 부재를 같은 실패로 취급하지 말고 등록 내용과 사유를 확인하세요.
126
+
127
+ 스테이징한 변경에는 명령줄이 없으므로 명령 범위 선언은 적용되지 않으며 판정도 기록하지
128
+ 않습니다. 설정, 범위 일치, 자료 공급, 최종 비교는 각각 다른 단계입니다.
129
+
130
+ <a id="local-state"></a>
131
+ ## 다른 컴퓨터로 프로젝트를 옮길 때
132
+
133
+ 텔레메트리와 `.polydeukes/baseline.json`은 로컬 상태이며 Git이 옮겨 주는 판정 이력이
134
+ 아닙니다. 이 파일 없이 저장소를 복제해도 과거 판정이 복원되지는 않습니다. 세션 훅은 기준
135
+ 상태가 없거나 유효하지 않으면 이를 기록하고 다음 비교에 쓸 기준을 만듭니다. 과거 변경을
136
+ 판정했다는 증거가 생기는 것은 아닙니다. 필요한 로그는 따로 보관해 옮기세요.
137
+ `telemetry.logPath`를 바꿨다면 모든 기록이 `.polydeukes/`에 있다고 가정해서는 안 됩니다.
138
+
139
+ <a id="next-steps"></a>
140
+ ## 다음 단계
141
+
142
+ - [표면 연결하기](./how-to/connect-surfaces.ko.md)
143
+ - [프로젝트 설정하기](./how-to/configure-project.ko.md)
144
+ - [규율 작성하기](./how-to/write-disciplines.ko.md)
@@ -2,188 +2,140 @@
2
2
 
3
3
  **English** · [한국어](./troubleshooting.ko.md)
4
4
 
5
- > Alpha. Nine states cover what ships today — five ways a fail-closed system refuses to
6
- > proceed, and four things worth knowing when a judgment surprises you. Each entry is
7
- > symptom → cause → recovery.
8
-
9
- This is the guide layer for recovery: the fail-closed states, reading verdicts, and the
10
- witness valve.
11
-
12
- The one principle behind half of this page: **a gate that cannot judge blocks rather than
13
- guesses.** A missing config, an ambiguous config, an invalid config, an installer that
14
- cannot prove resolution, and a judge that cannot be loaded all fail closed, because a dead
15
- gate that waves things through is the cheapest bypass of all. The recovery is never to
16
- disable the gate — it is to give it back what it needs to judge. And run that recovery
17
- **from your own terminal**: inside a session the repair commands are judged by the very
18
- gate they repair, and while no config is loaded there is no witness valve to open.
19
-
20
- ## Every call is blocked and there is no config
21
-
22
- **Symptom.** On the session surface, every editing tool call and shell command exits 2;
23
- on the commit surface, every `pdks covenant check` run does. The error says no Polydeukes
24
- config was found and names the three candidate filenames.
25
-
26
- **Cause.** The surface is wired but config discovery found nothing at the project root.
27
- Discovery looks for exactly these, in this order: `polydeukes.config.yaml`,
28
- `polydeukes.config.yml`, `polydeukes.config.json`. A missing config never silently loads
29
- defaults — silent defaults would mean silently unprotected.
30
-
31
- **Recovery.** Restore the file from git. On the session path,
32
- `pnpm exec pdks init claude-code` or `pdks init grok` recreates files that
33
- are absent. An existing grok JSON is not rewritten except when its `command`
34
- still names the grok delegator and a Claude delegator is on disk — then that
35
- command is retargeted and the matcher follows the settings entry. On the commit path the
36
- config is hand-written — the [install guide](./installation.md)'s commit-surface section
37
- has a starting point.
5
+ Diagnose the failed stage before changing policy. If a session blocks the repair command itself,
6
+ run the repair from your own terminal. `pdks docs` remains usable without project configuration
7
+ or the judging packages, provided its own installed documentation bundle is intact.
38
8
 
39
- ## More than one config file
40
-
41
- **Symptom.** Every call exits 2 with an error naming two (or three) config files at once.
42
-
43
- **Cause.** Two spellings coexist — say a `polydeukes.config.yaml` created next to a
44
- project's existing `.yml`. Ambiguity never picks a winner.
9
+ <a id="no-config"></a>
10
+ ## No config found
45
11
 
46
- **Recovery.** Keep exactly one file and delete the others. If both have content, merge by
47
- hand first — the loader will not choose for you.
12
+ Commands that need configuration exit 2 when none of `polydeukes.config.yaml`,
13
+ `polydeukes.config.yml`, or `polydeukes.config.json` exists directly at the project root.
14
+ Restore the intended file from Git, or use `pdks init` / `pdks-claude-code init` /
15
+ `pdks-grok init` for a new project. Then run `pdks explain`. No configuration means no silent
16
+ default policy.
48
17
 
49
- ## The config is invalid
18
+ <a id="multiple-config"></a>
19
+ ## More than one config file
50
20
 
51
- **Symptom.** Every call exits 2 with an error naming the offending file — and, for schema
52
- violations, the exact key.
21
+ An ambiguity error names the competing files. Merge their intended contents and retain exactly
22
+ one accepted filename. The loader will not choose one on your behalf. Retry `pdks explain`.
53
23
 
54
- **Cause.** One of: a YAML parse error; a custom YAML tag (rejected even though the parser
55
- cannot execute it — config data stays uncomputable by contract); an unknown key (a typo
56
- like `protectedPath:` is rejected with the full field path — with one open ground: an
57
- adapter namespace's *name* is not validated, so `adapters.gti:` for `adapters.git:` loads
58
- clean and its entries are simply never read; that one spelling you check yourself); or an
59
- empty `languages` block, the schema's one required entry.
24
+ <a id="invalid-config"></a>
25
+ ## Invalid config
60
26
 
61
- **Recovery.** Fix the named key in the named file. The error is specific on purpose — no
62
- rewrite-and-hope needed.
27
+ Parsing or schema failures exit 2 and name the file; schema errors also identify the offending
28
+ field. Repair invalid YAML, custom tags, unknown fields, or an empty `languages` object.
29
+ Custom YAML tags are rejected even if the parser cannot execute them: configuration is data.
63
30
 
64
- ## A Grok session does not pick up a newly installed hook
31
+ Typos such as `protectedPath:` or `adaptors:` are refused. Adapter namespace names are deliberately
32
+ open, however: a namespace nobody implements loads without being read by anything. After repair,
33
+ run `pdks explain` and check the assembled registrations.
65
34
 
66
- **Symptom.** `pdks init grok` reported created files, but this session's tool calls still
67
- leave no telemetry row.
35
+ <a id="grok-witness"></a>
36
+ ## Grok witness
68
37
 
69
- **Cause.** Grok loads hooks at session start. An already-open session keeps that snapshot.
38
+ A hook not yet loaded and an unavailable witness valve are different problems:
70
39
 
71
- **Recovery.** Press `r` in the Hooks tab, or start a new session. The witness valve also
72
- does not open on Grok — a block is recovered from another terminal or the commit-surface TTY.
40
+ - After `pdks-grok init`, reload the Hooks tab or start a new session. Verify an actual call and
41
+ its telemetry; a successful installer run does not prove the open host loaded the hook.
42
+ - Grok's ACP history does not supply the Claude-format human message required by the current
43
+ session witness valve. Reloading does not add that capability. Perform a necessary repair
44
+ from your own terminal rather than trying to send a Claude witness token through Grok.
73
45
 
74
- ## One Grok tool call leaves two telemetry rows
46
+ A commit witness authorizes its staged check only. It cannot release a blocked Grok tool call.
75
47
 
76
- **Symptom.** In a tree wired for both Claude Code and Grok, every `write` or
77
- `run_terminal_command` in a Grok session appends two rows to `.polydeukes/roi.log`,
78
- milliseconds apart.
48
+ <a id="config-fault"></a>
49
+ ## Config-fault
79
50
 
80
- **Cause.** Grok reads `.claude/settings.json` as well as `.grok/hooks/*.json` and collapses
81
- the two registrations only when `command` and `matcher` are both identical. A grok JSON
82
- whose matcher differs from the settings entry spawns the judge a second time.
51
+ The configuration loaded, but an entry could not compile into a judgment. A matching registration
52
+ records `skipped` with `config-fault`; `pdks explain` shows the fault. Check extraction step names,
53
+ arguments, regex syntax, and paired versus single extraction use. Fix the named declaration and
54
+ repeat the same observation. A skipped entry is not a passing one.
83
55
 
84
- **Recovery.** Make the grok JSON's `matcher` the same string as the settings entry that
85
- registers the same command. Re-running either installer does it: every grok entry that
86
- names the Claude hook takes the settings entry's matcher.
56
+ <a id="judge-cannot-be-loaded"></a>
57
+ ## The judge cannot be loaded
87
58
 
88
- ## `pdks init claude-code` refuses to run
59
+ A missing package or judging module fails closed. Reinstall the package or run the complete
60
+ workspace build from your own terminal. The generated hook delegates to the installed package;
61
+ it is not an independent copy of the judge. Verify another real call after repair. A failure
62
+ before telemetry can load may leave no row at all.
89
63
 
90
- **Symptom.** The installer prints an install command and exits 2 without creating anything.
64
+ The session hook prefixes the message with `covenant hook failed closed:` and the commit check
65
+ with `covenant check failed closed:`. The two shapes you will see:
91
66
 
92
- **Cause.** Preflight: before writing any file, the installer proves the `polydeukes`
93
- package resolves from the target project root. A hook generated without that would block
94
- every call through its own fail-closed catch — an uneditable tree. The usual trigger is
95
- running via a one-off `npx` without installing, or running in the wrong directory (it
96
- installs where it is invoked).
67
+ ```text
68
+ covenant hook failed closed: Cannot find package 'polydeukes' imported from …
69
+ covenant check failed closed: the covenant judges could not be loaded from … — run 'pnpm build' to rebuild them: Cannot find module './self-mod.js' …
70
+ ```
97
71
 
98
- **Recovery.** `pnpm add -D polydeukes` in the project you meant, then re-run from that
99
- root. Zero files were written, so there is no partial state to clean up.
72
+ The first is the installed package missing; the second is a source checkout whose judge
73
+ build output is missing or partial.
100
74
 
101
- ## The judge cannot be loaded
75
+ <a id="reading-verdict"></a>
76
+ ## Reading a verdict
102
77
 
103
- **Symptom.** Every call exits 2 with `covenant hook failed closed: Cannot find package
104
- 'polydeukes'` — or an error naming a judge-body file that does not exist.
78
+ | Record | Meaning |
79
+ |---|---|
80
+ | `passed` | The observed input was judged and upheld the covenant. |
81
+ | `blocked` | A violation stopped the operation. |
82
+ | `witnessed` | A blocking result was allowed through its witness valve. |
83
+ | `advised` | A violation was recorded without stopping the operation, on either surface. |
84
+ | `skipped` | No judgment was possible for the matching registration. This is not a pass. |
85
+ | `unattributed` | Baseline comparison found protected changes without an explaining judgment, or could not read a valid baseline. This is an observation, not a verdict. |
105
86
 
106
- **Cause.** The hook is wired but the package it delegates to is gone or incomplete: the
107
- dependency was removed, the tree is a fresh clone that was never installed, or (in a
108
- source clone of this repository) the judge's build output is missing. The installer's
109
- preflight prevents *wiring* a project into this state, but nothing prevents a wired
110
- project from entering it later.
87
+ Judgments append telemetry at `.polydeukes/roi.log` unless `telemetry.logPath` changes the location.
88
+ Logging is fail-open: a write failure does not alter the verdict. Exit 0 means the operation may
89
+ continue, not that every discipline passed.
111
90
 
112
- **Recovery.** From your own terminal, reinstall the dependency (`pnpm install`, or
113
- `pnpm add -D polydeukes` if it was removed). In a source clone, run the build. The hook
114
- file itself needs no repair — it is a delegator, and it recovers the moment the package
115
- resolves again.
91
+ <a id="opening-a-blocked-call"></a>
92
+ ## Opening a blocked call
116
93
 
117
- ## Reading a verdict
94
+ In a supported Claude Code session, type the configured witness token alone on the first line
95
+ of a human message, then retry within its configured TTL. The message may also precede an
96
+ intentional protected edit; no previous failed attempt is required. The token is not a secret.
97
+ The valve checks human provenance and applies only to a blocking judgment. A successful retry
98
+ appends a `witnessed` row; it does not rewrite the earlier blocked row.
118
99
 
119
- **Symptom.** Something was blocked (or passed) and you want to know what the record says.
100
+ A witness cannot repair missing modules or other failures that prevent judgment assembly.
120
101
 
121
- **Cause.** Not a failure — this is the measurement working. Every judgment appends exactly
122
- one record to the telemetry log (`.polydeukes/roi.log` by default, `telemetry.logPath` to
123
- move it).
102
+ <a id="blocked-commit"></a>
103
+ ## A blocked commit
124
104
 
125
- **Recovery.** Read the last lines and the six-word vocabulary:
105
+ The change-set surface does not prompt. By default it exits 0 on every verdict and records the
106
+ break as `advised`; it exits 2 only when the check runs with `--enforce block` (a protected
107
+ path or an entry set to `enforce: block` broke) or when it could not judge at all. Whether the
108
+ commit stops is your hook wiring — a hook that honours the exit code stops it, one that ignores
109
+ it does not. Git may report a different nonzero exit code for the failed commit.
126
110
 
127
- | Word | Means |
128
- |---|---|
129
- | `passed` | Judged, upheld the covenant. |
130
- | `blocked` | Judged, broke it. The call did not run. |
131
- | `witnessed` | A blocked verdict a human opened in person. Never silent. |
132
- | `advised` | Commit surface at `advise` level: a break recorded without stopping the commit. |
133
- | `skipped` | A registration matched but could not judge — **the recorded absence of a judgment, not a pass.** |
134
- | `unattributed` | A protected entry changed on disk and no judgment explains it — **an observation, not a verdict.** Nothing was blocked; the write already happened. |
111
+ A normal entry blocks only when it declares `enforce: block` and the check runs with
112
+ `--enforce block`; nothing in the config promotes a default-`advise` entry. Changing a level is
113
+ a policy decision, not a required repair. To let a judged break through, drop `--enforce block`
114
+ from the hook command rather than editing the config — the row is still written.
135
115
 
136
- An `unattributed` row names the entry, not the file inside it. Rebuilding a protected `dist`
137
- without a judged call producing one is expected — it says a write reached that entry outside
138
- the session's view, which is exactly what the row is for.
116
+ <a id="skipped-rows-on-the-change-set-surface"></a>
117
+ ## `skipped` rows on the change-set surface
139
118
 
140
- ## Opening a blocked call — the witness
119
+ A transcript-reading declaration with `supply: { session: 'pass' }` records `supply-pass` when the
120
+ change-set surface has no session. Use the session surface for that promise; a skip does not verify
121
+ history. Other unavailable channels can produce `no-observation`. Inspect the registration and
122
+ reason instead of treating every missing source as the same failure.
141
123
 
142
- **Symptom.** A call you and your agent agree should proceed was blocked, and you want it
143
- through without editing the policy.
124
+ A command-scoped declaration does not match a staged diff's absent command line and records no
125
+ judgment there. Configuration, scope matching, supply, and the final comparison are separate steps.
144
126
 
145
- **Cause.** The valve exists for exactly this, and it sits *after* the verdict — only a
146
- judgment that actually blocked can be witnessed open.
127
+ <a id="local-state"></a>
128
+ ## Moving a project between machines
147
129
 
148
- **Recovery.** Type the token from your config's `witness:` block so it stands **alone on
149
- the first line** of a conversation message. The window holds for `ttlMinutes`, then
150
- blocking resumes on its own. Three things that do not work, by design: quoting or
151
- mentioning the token mid-sentence (invocation is first-line-standalone only); witnessing a
152
- call that was never blocked (the valve is consulted only after a block); and the agent
153
- typing the token for itself (only human-authored messages count — the defence is
154
- provenance, not secrecy). Every allowance lands as one `witnessed` row.
130
+ Telemetry and `.polydeukes/baseline.json` are local state, not a portable history supplied by Git.
131
+ A clone without them does not reconstruct prior judgments. The session hook records an absent
132
+ or invalid baseline and establishes one for subsequent comparisons; this is not proof that old
133
+ changes were judged. Preserve needed logs separately when migrating, and check any custom
134
+ `telemetry.logPath` rather than assuming `.polydeukes/` holds all records.
155
135
 
156
- ## A blocked commit
136
+ <a id="next-steps"></a>
137
+ ## Next steps
157
138
 
158
- **Symptom.** `git commit` stops at a prompt asking a human to witness a staged protected
159
- change — or, from an agent, the commit simply fails with exit 2.
160
-
161
- **Cause.** The commit surface at the default `block` level judges the staged diff, and its
162
- valve is a TTY prompt. An agent-spawned commit has no TTY, so for it the valve is not even
163
- assembled — a terminal-holding human is the pass condition, not a workaround.
164
-
165
- **Recovery.** Run the commit from your own terminal and answer the prompt with the full
166
- token — one answer covers that whole commit. If you want the commit surface to measure
167
- without stopping, set `adapters.git.enforce: advise`: verdicts are then recorded as
168
- `advised` and the commit proceeds with one advisory line on stderr. At either level a run
169
- that *cannot judge* (missing or invalid config, an unresolvable judge) still exits 2 —
170
- `advise` relaxes the verdict, never the gate's integrity.
171
-
172
- ## `skipped` rows on the commit surface
173
-
174
- **Symptom.** A `precedent` declaration that judges normally in sessions always lands as
175
- `skipped` on commits.
176
-
177
- **Cause.** A declaration that reads the session judges *session history* — was the required
178
- step actually executed before this change. A commit has no session to read, so the
179
- declaration's own `supply: { session: 'pass' }` takes over: when its scope matches a staged
180
- change it records `skipped` with the reason `supply-pass` and the entry's id, and proceeds.
181
-
182
- **Recovery.** None needed — this is a declared condition of the surface, not a defect. The
183
- row is the point: a gate that did nothing says so in the data. `pdks explain` shows the
184
- same fact before any commit: the entry appears under the commit surface with its
185
- `transcript` source, which that surface never supplies. Such a declaration is
186
- really a session-surface tool — on a project that wires only the commit surface, such an
187
- entry only ever buys telemetry, so declare it where an AI partner's session exists to be
188
- judged. The row appears only when the entry's scope actually matched, so an unrelated
189
- commit records nothing.
139
+ - [Connect the surfaces](./how-to/connect-surfaces.md)
140
+ - [Configure the project](./how-to/configure-project.md)
141
+ - [Write disciplines](./how-to/write-disciplines.md)