polydeukes 0.6.1 → 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 (106) hide show
  1. package/README.ko.md +14 -28
  2. package/README.md +14 -28
  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 +73 -90
  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 +8 -8
  44. package/dist/docs/README.md +8 -8
  45. package/dist/docs/catalog.json +44 -28
  46. package/dist/docs/concepts/judgment.ko.md +2 -2
  47. package/dist/docs/concepts/judgment.md +2 -2
  48. package/dist/docs/how-to/configure-project.ko.md +16 -16
  49. package/dist/docs/how-to/configure-project.md +15 -12
  50. package/dist/docs/how-to/connect-surfaces.ko.md +39 -34
  51. package/dist/docs/how-to/connect-surfaces.md +36 -33
  52. package/dist/docs/how-to/write-disciplines.ko.md +61 -6
  53. package/dist/docs/how-to/write-disciplines.md +62 -6
  54. package/dist/docs/index.json +607 -373
  55. package/dist/docs/reference/cli/covenant-check.ko.md +112 -65
  56. package/dist/docs/reference/cli/covenant-check.md +114 -59
  57. package/dist/docs/reference/cli/docs.ko.md +2 -2
  58. package/dist/docs/reference/cli/docs.md +2 -2
  59. package/dist/docs/reference/cli/explain.ko.md +14 -9
  60. package/dist/docs/reference/cli/explain.md +15 -10
  61. package/dist/docs/reference/cli/init.ko.md +52 -52
  62. package/dist/docs/reference/cli/init.md +43 -55
  63. package/dist/docs/reference/configuration/index.ko.md +171 -66
  64. package/dist/docs/reference/configuration/index.md +178 -71
  65. package/dist/docs/reference/packages/adapter-claude-code.ko.md +38 -11
  66. package/dist/docs/reference/packages/adapter-claude-code.md +38 -14
  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 +49 -3
  70. package/dist/docs/reference/packages/core.md +54 -5
  71. package/dist/docs/reference/packages/polydeukes.ko.md +138 -77
  72. package/dist/docs/reference/packages/polydeukes.md +146 -81
  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 +17 -15
  76. package/dist/docs/troubleshooting.md +18 -14
  77. package/dist/docs/tutorials/first-judgment.ko.md +2 -2
  78. package/dist/docs/tutorials/first-judgment.md +2 -2
  79. package/dist/explain.d.ts +1 -1
  80. package/dist/explain.js +37 -22
  81. package/dist/load-config.d.ts +1 -1
  82. package/dist/load-config.js +1 -1
  83. package/dist/pre-state-reader.d.ts +1 -1
  84. package/dist/pre-state-reader.js +1 -1
  85. package/dist/scaffold-project.d.ts +2 -2
  86. package/dist/scaffold-project.js +9 -3
  87. package/dist/schema/polydeukes.schema.json +12 -0
  88. package/dist/worktree-reader.d.ts +19 -0
  89. package/dist/worktree-reader.js +30 -0
  90. package/package.json +3 -17
  91. package/dist/claude-code-hook.d.ts +0 -79
  92. package/dist/claude-code-hook.js +0 -372
  93. package/dist/claude-code.d.ts +0 -6
  94. package/dist/claude-code.js +0 -6
  95. package/dist/covenant-module.d.ts +0 -25
  96. package/dist/covenant-module.js +0 -42
  97. package/dist/docs/reference/packages/adapter-git.ko.md +0 -101
  98. package/dist/docs/reference/packages/adapter-git.md +0 -108
  99. package/dist/docs/reference/packages/covenant.ko.md +0 -115
  100. package/dist/docs/reference/packages/covenant.md +0 -123
  101. package/dist/index.d.ts +0 -22
  102. package/dist/index.js +0 -21
  103. package/dist/init-claude-code.d.ts +0 -52
  104. package/dist/init-claude-code.js +0 -586
  105. package/dist/init-grok.d.ts +0 -51
  106. package/dist/init-grok.js +0 -242
@@ -0,0 +1,79 @@
1
+ # `@polydeukes/adapter-grok`
2
+
3
+ **English** · [한국어](adapter-grok.ko.md)
4
+
5
+ > **The Grok install unit** — PreToolUse payloads become the covenant input IR, with the
6
+ > file-change evidence the judge reads, and the package installs the session surface into a
7
+ > project.
8
+ >
9
+ > Alpha. Install it next to `polydeukes`, which it names as a `peerDependency`.
10
+
11
+ <a id="ownership"></a>
12
+ ## What this package owns
13
+
14
+ The boundary where Grok's vocabulary is translated away. Agent and tool literals live *here*
15
+ by design, so that they never reach the core.
16
+
17
+ | Unit | What it does |
18
+ |---|---|
19
+ | `pdks-grok` bin | One subcommand, `pdks-grok init`, which registers the session surface in a project |
20
+ | `runHook` | Translates one PreToolUse payload into the input IR and spawns the judge |
21
+ | Payload up-translation | A raw PreToolUse payload becomes a `CovenantInput` |
22
+ | Virtual post-state | Computes what a file *would* contain after a write or substitution applies, without touching disk |
23
+ | File-change evidence | Pairs the disk pre-state with the virtual post-state into union evidence |
24
+
25
+ `runHook({ repoRoot })` is what the generated hook delegator imports. It builds the IR — the
26
+ `tools` roster included, with no `session` or `actor` key — then spawns
27
+ `pdks covenant check --enforce block` in `repoRoot` and returns the child's exit code. The
28
+ judging happens in that child process; this package carries no judgment logic.
29
+
30
+ **This package writes no telemetry rows.** A failure before the spawn is sent to `pdks` on
31
+ stdin as a plain line, and `pdks` records the fail-closed row, so one call still leaves one
32
+ row. It never imports the judge: `polydeukes` and `@polydeukes/core` are both
33
+ `peerDependencies`, so the vocabulary and the judge are shared rather than installed a second
34
+ time here.
35
+
36
+ The roster this adapter puts on the IR is Grok-native: `write` and `search_replace` mutate a
37
+ file; `run_terminal_command` carries a shell line.
38
+
39
+ <a id="consumer-contract"></a>
40
+ ## Where the consumer touches it
41
+
42
+ Two lines install the Grok session surface, run from the project root:
43
+
44
+ ```sh
45
+ npm install --save-dev polydeukes @polydeukes/adapter-grok
46
+ npx pdks-grok init
47
+ ```
48
+
49
+ `pdks-grok init` resolves `polydeukes` from the project, spawns `pdks init` for the
50
+ agent-neutral scaffold, then writes the two Grok artifacts non-destructively. A re-run
51
+ reports each existing artifact as skipped and overwrites nothing. The full artifact list is in
52
+ [`pdks init`](../cli/init.md#init-grok).
53
+
54
+ Installing both this adapter and `@polydeukes/adapter-claude-code` in one project can run the
55
+ judge twice per call.
56
+
57
+ - **The generated hook** imports `runHook` from this package. Upgrading the package upgrades
58
+ what runs; the hook file itself never changes.
59
+
60
+ No configuration namespace of its own.
61
+
62
+ <a id="limits"></a>
63
+ ## Declared limits
64
+
65
+ - **A child process's writes are outside observation.** This surface judges *declared tool
66
+ calls*. A command that spawns a process which then writes files is judged on the command,
67
+ not on what the child did.
68
+ - **Evidence exists only where a post-state can be computed.** `write` and `search_replace`
69
+ contribute one. A substitution that matches nothing, or that matches more than once without
70
+ `replace_all`, yields no evidence — the host tool refuses those calls.
71
+ - **An evidence-free call falls back to the conservative judgment** — the call's arguments
72
+ are compared for a mention rather than a proven target.
73
+ - **There is no transcript channel.** The IR omits `session` and `actor`. Grok's ACP history
74
+ does not supply the human-message evidence the session witness valve needs. For an
75
+ intentional blocked edit, use your own terminal.
76
+ - **An unresolvable `polydeukes` leaves no row.** When the umbrella cannot be resolved from the
77
+ project there is no process to spawn and no log path to write to, so the hook exits `2` with
78
+ one line on stderr and the telemetry log gains nothing. Every other pre-spawn failure does
79
+ reach `pdks` and does leave a row.
@@ -28,7 +28,7 @@
28
28
  **값**이라서, 코어의 에이전트 중립성은 grep으로 확인되는 주장입니다. 다른 모든 패키지가
29
29
  이 패키지에 의존하고, 이 패키지는 그 어느 것에도 의존하지 않습니다.
30
30
 
31
- **판정기와 어댑터는 이 패키지를 `peerDependency`로 선언합니다.** 각각 설치하는 대신 같은 어휘를 공유합니다. `SOURCE_KINDS`와
31
+ **세션 어댑터는 이 패키지를 `peerDependency`로 선언합니다.** 따로 설치하는 대신 우산과 같은 어휘를 공유합니다. `SOURCE_KINDS`와
32
32
  `parseInput`은 검증기와 엔진이
33
33
  같은 값을 참조해야 합니다. 서로 다른 사본을 사용하면 설치는 성공해도 동작이 달라질 수 있습니다.
34
34
  통합 패키지가 일반 의존성으로 core를 제공하므로 사용자는 패키지 하나만 설치하면 됩니다.
@@ -41,7 +41,10 @@
41
41
  `.polydeukes/roi.log`의 차단 기록도 이 판정 어휘를 사용합니다.
42
42
 
43
43
  `world`는 표면이 공급합니다(`files`, `changes`, `channels`). 판정기가 디스크에서 읽지
44
- 않습니다.
44
+ 않습니다. `tools`와 `session`은 호스트가 공급합니다. 명부는 어댑터가 채우는 값이고, 세션은
45
+ IR만으로는 증명하지 못하는 두 사실, 메시지의 신선도와 호출의 결과를 담은 증거입니다.
46
+ `transcriptFromSession`은 그 키에서 증인이나 선행 조건 소비자가 읽는 대화 기록을 만들고,
47
+ `transcriptFromInput`은 그 두 사실 없이 IR에서 같은 대화 기록을 만듭니다.
45
48
 
46
49
  ```ts
47
50
  type CovenantInput = {
@@ -54,6 +57,13 @@ type CovenantInput = {
54
57
  changes?: string[];
55
58
  channels?: { sidecar?: string };
56
59
  };
60
+ tools?: { mutating: string[]; shell: string[]; commandArgs: string[] };
61
+ session?: {
62
+ evidencePath?: string;
63
+ userMessages: { text: string; timestampMs?: number }[];
64
+ toolCalls: { name: string; args?: Record<string, unknown>; succeeded?: boolean }[];
65
+ channels?: { sidecar?: string };
66
+ };
57
67
  };
58
68
 
59
69
  type FileChange =
@@ -98,6 +108,42 @@ function allFileChanges(input: CovenantInput): FileChange[];
98
108
  `allFileChanges`는 귀속이 필요 없는 소비자를 위해 모든 호출의 증거를 호출 순서대로
99
109
  평탄화합니다. 증거 없는 호출은 건너뛰고 무엇으로도 대체하지 않습니다.
100
110
 
111
+ <a id="discipline-lists"></a>
112
+ ## 규율 목록 셋과 통로 유도
113
+
114
+ `defineConfig()`는 규율 목록 셋을 받고, 항목마다 그것이 맞는 목록에 적혔는지 판정합니다.
115
+
116
+ | 키 | 타입 | 판정하는 표면 |
117
+ |---|---|---|
118
+ | `disciplines` | `(DisciplineEntry \| DisciplineDraft)[]` | 두 표면 모두 |
119
+ | `sessionDisciplines` | `DisciplineEntry[]` | 세션 표면(session surface)만 |
120
+ | `changeSetDisciplines` | `DisciplineEntry[]` | 변경 집합 표면(change-set surface)만 |
121
+
122
+ `ResolvedConfig`도 같은 이름으로 셋을 싣되 판정 항목만 담고, 초안(draft)은 `drafts`로 갈라
123
+ 둡니다. 입력이 선언하지 않은 목록은 빈 배열이 되지 않고 없는 채로 남습니다.
124
+
125
+ 검증기와 우산 패키지가 함께 쓰는 유도는 내보내는 함수 하나입니다.
126
+
127
+ ```ts
128
+ type DeclarationChannel = 'transcript' | 'channel' | 'command' | 'actor' | 'changes';
129
+
130
+ function declarationChannels(body: Omit<AlgebraDeclaration, 'discipline'>): DeclarationChannel[];
131
+ ```
132
+
133
+ 순수하고 구문적인 함수라서 선언을 실행하지 않습니다. `transcript`는 `{ transcript: true }`
134
+ 바인딩에서, `channel`은 `{ sidecar: true }` 바인딩에서, `command`는
135
+ `scope.source === 'command'`나 `{ op: 'source', of: 'command' }` 단계에서, `actor`는
136
+ `{ op: 'source', of: 'actor' }` 단계에서, `changes`는
137
+ `{ op: 'source', of: 'changes' }` 단계에서 나옵니다. `witness` 블록 자신의 `extract`도 본체와
138
+ 함께 훑습니다. 반환 목록은 위 표의 고정 순서를 따르므로, 통로 집합을 비교하는 두 호출자가 같은
139
+ 목록을 비교합니다.
140
+
141
+ 다섯 가운데 하나도 이름 짓지 않는 본체는 변경된 파일과 저장소 파일만 읽고 그 둘은 두 표면이
142
+ 모두 공급하므로 `disciplines`에 속합니다. 그 밖은 항목과 통로와 가야 할 목록을 대는
143
+ `ConfigValidationError`이며, 메시지는
144
+ [설정 참조](../configuration/index.ko.md#placement-rule)에 있습니다. 우산 패키지는 같은 질문을
145
+ 따로 답하지 않고 이 함수를 불러 씁니다.
146
+
101
147
  <a id="consumer-contract"></a>
102
148
  ## 사용자와의 접점
103
149
 
@@ -119,7 +165,7 @@ function allFileChanges(input: CovenantInput): FileChange[];
119
165
  - **어댑터 네임스페이스는 이름이 아니라 형태로 검증합니다.** `defineConfig()`는 `adapters`가
120
166
  평범한 객체의 맵인지, 각 네임스페이스 값이 객체인지를 봅니다. 네임스페이스 **이름**이
121
167
  누군가 구현한 것인지는 보지 않고, 네임스페이스 안쪽은 들여다보지 않습니다.
122
- `adapters.git` 안의 미지 어휘는 git 어댑터의 검증기가 자기 층위에서 거부합니다. 여기가
168
+ 네임스페이스 안의 미지 어휘는 해당 어댑터의 검증기가 자기 층위에서 거부합니다. 여기가
123
169
  아닙니다.
124
170
  - **기본 대화 기록은 아무것도 하지 않습니다.** 실제 대화 기록을 주입하지 않은 소비자는
125
171
  "아무 일도 없었다"는 결과를 받습니다. 이 상태에서는 증인 밸브가 작업을 허용하지 않으므로
@@ -28,8 +28,8 @@ hand-rolled and the published JSON Schema is a sibling artifact the source never
28
28
  *values* supplied by configs and adapters, so the core's agent-neutrality is a claim a grep
29
29
  can check. Every other package depends on this one; this one depends on none of them.
30
30
 
31
- **The judge and the two adapters name it as a `peerDependency`, not a dependency of their
32
- own.** They share the vocabulary rather than each installing a copy — `SOURCE_KINDS` and
31
+ **The session adapter names it as a `peerDependency`, not a dependency of its
32
+ own.** It shares the vocabulary with the umbrella rather than installing a copy — `SOURCE_KINDS` and
33
33
  `parseInput` have to be one set of values for the validator and the engine to agree, and two
34
34
  copies would disagree silently instead of failing at install time. The umbrella carries the
35
35
  ordinary dependency that satisfies that peer, which is why a consumer still installs one
@@ -44,7 +44,11 @@ verdict the wrapper translates into an exit code. Blocked rows in `.polydeukes/r
44
44
  use this judgment vocabulary.
45
45
 
46
46
  `world` is supplied by the surface (`files`, `changes`, `channels`). The judge does not
47
- read it from disk.
47
+ read it from disk. `tools` and `session` are supplied by the host: the roster is values the
48
+ adapter fills in, and the session is the evidence a live agent session proves that a bare
49
+ IR cannot — message freshness and call outcome. `transcriptFromSession` wraps that key as
50
+ the transcript a witness or a precedent consumer reads, the way `transcriptFromInput` wraps
51
+ the bare IR without those two facts.
48
52
 
49
53
  ```ts
50
54
  type CovenantInput = {
@@ -57,6 +61,13 @@ type CovenantInput = {
57
61
  changes?: string[];
58
62
  channels?: { sidecar?: string };
59
63
  };
64
+ tools?: { mutating: string[]; shell: string[]; commandArgs: string[] };
65
+ session?: {
66
+ evidencePath?: string;
67
+ userMessages: { text: string; timestampMs?: number }[];
68
+ toolCalls: { name: string; args?: Record<string, unknown>; succeeded?: boolean }[];
69
+ channels?: { sidecar?: string };
70
+ };
60
71
  };
61
72
 
62
73
  type FileChange =
@@ -101,6 +112,44 @@ The exported constants are `EXIT_UPHOLD` (`0`), `EXIT_BREAK_NON_BLOCKING` (`1`),
101
112
  `allFileChanges` flattens every call's evidence in call order for consumers that need no
102
113
  attribution. Calls without evidence are skipped, never substituted for.
103
114
 
115
+ <a id="discipline-lists"></a>
116
+ ## The three discipline lists and the channel derivation
117
+
118
+ `defineConfig()` accepts three discipline lists and decides, for each entry, whether it is
119
+ written in the right one.
120
+
121
+ | Key | Type | Judged on |
122
+ |---|---|---|
123
+ | `disciplines` | `(DisciplineEntry \| DisciplineDraft)[]` | both surfaces |
124
+ | `sessionDisciplines` | `DisciplineEntry[]` | the session surface only |
125
+ | `changeSetDisciplines` | `DisciplineEntry[]` | the change-set surface only |
126
+
127
+ `ResolvedConfig` carries the three under the same names, holding judged entries only, with the
128
+ drafts split out into `drafts`. A list the input did not declare stays absent rather than
129
+ becoming an empty array.
130
+
131
+ The derivation both the validator and the umbrella use is one exported function:
132
+
133
+ ```ts
134
+ type DeclarationChannel = 'transcript' | 'channel' | 'command' | 'actor' | 'changes';
135
+
136
+ function declarationChannels(body: Omit<AlgebraDeclaration, 'discipline'>): DeclarationChannel[];
137
+ ```
138
+
139
+ It is pure and syntactic — the declaration is never run. `transcript` comes from a
140
+ `{ transcript: true }` binding, `channel` from a `{ sidecar: true }` binding, `command` from
141
+ `scope.source === 'command'` or a `{ op: 'source', of: 'command' }` step, `actor` from a
142
+ `{ op: 'source', of: 'actor' }` step, and `changes` from a
143
+ `{ op: 'source', of: 'changes' }` step. A `witness` block's own `extract` is walked with the
144
+ body's. The returned list is in the fixed order above, so two callers comparing channel sets
145
+ compare the same list.
146
+
147
+ A body naming none of the five reads the changed file and repository files alone, which both
148
+ surfaces supply, so it belongs in `disciplines`. Anything else is a `ConfigValidationError`
149
+ naming the entry, its channels, and the list it belongs in; the messages are in [the
150
+ configuration reference](../configuration/index.md#placement-rule). The umbrella imports this
151
+ function rather than answering the same question a second time.
152
+
104
153
  <a id="consumer-contract"></a>
105
154
  ## Where the consumer touches it
106
155
 
@@ -123,8 +172,8 @@ Everything else here is reached through `polydeukes`.
123
172
  - **Adapter namespaces are validated by shape, not by name.** `defineConfig()` checks that
124
173
  `adapters` is a map of plain objects and that each namespace value is an object. It does
125
174
  not check that a namespace *name* is one anybody implements, and it does not look inside
126
- the namespace at all. Unknown vocabulary inside `adapters.git` is rejected by the git
127
- adapter's own validator, at its own layer — not here.
175
+ the namespace at all. Unknown vocabulary inside a namespace is rejected by that adapter's
176
+ own validator, at its own layer — not here.
128
177
  - **The default transcript is a noop.** A consumer that injects no real transcript
129
178
  converges on "nothing happened", which is the safe direction for a valve: it never opens.
130
179
  Real transcripts live behind adapters.
@@ -2,61 +2,62 @@
2
2
 
3
3
  [English](./polydeukes.md) · **한국어**
4
4
 
5
- 일반 사용자는 통합 패키지인 `polydeukes` 하나만 설치하면 됩니다. CLI 실행 파일, 설정 로더, 커밋 표면 실행기, 세션 표면 실행기 서브패스, 동봉된 스키마 자산을
6
- 모두 이 패키지가 맡습니다.
5
+ 일반 사용자는 통합 패키지인 `polydeukes` 하나만 설치하면 됩니다. `pdks` 실행 파일, 판정기,
6
+ 두 표면의 조립 루트, 세션 표면 실행기 서브패스, 동봉된 스키마 자산을 모두 이 패키지가
7
+ 맡습니다.
7
8
 
8
9
  <a id="polydeukes-entry-points"></a>
9
10
  ## 진입점
10
11
 
11
- | 지정자 | 내보내는 |
12
+ | 지정자 | 무엇인가 |
12
13
  |---|---|
13
- | `polydeukes` | `loadConfig`, `runCovenantCheck`, `ResolvedConfig` |
14
- | `polydeukes/claude-code` | `runClaudeCodeHook`과 입력·결과 타입 |
14
+ | `pdks` / `polydeukes` | 실행 파일입니다. `bin`에 이름 둘로 등록된 같은 CLI입니다 |
15
15
  | `polydeukes/schema.json` | 동봉된 설정 JSON Schema |
16
16
 
17
- 같은 CLI를 `pdks`와 별칭 `polydeukes`로 실행할 수 있습니다.
17
+ `.` 진입점은 없습니다. `import 'polydeukes'`는 `ERR_PACKAGE_PATH_NOT_EXPORTED`로 실패합니다.
18
+ 사용자가 닿는 것은 실행 파일과 스키마 파일입니다. 세션 표면도 셸과 같은 방법으로 이
19
+ 패키지에 닿습니다. 즉 `pdks covenant check`를 스폰하므로, 에이전트 어댑터는 이 패키지를
20
+ import하지 않고 peer 의존으로 선언합니다.
18
21
 
19
22
  <a id="polydeukes-bin"></a>
20
23
  ## CLI 명령
21
24
 
22
25
  | 명령 | 목적 |
23
26
  |---|---|
24
- | `pdks covenant check` | 스테이징한 변경, 작업 트리, 리비전 범위를 판정 |
25
- | `pdks init claude-code` | Claude Code 세션 표면 설치 |
26
- | `pdks init grok` | Grok 세션 표면 설치 |
27
+ | `pdks covenant check` | 표준 입력의 입력 IR(기본, 세션 표면) 또는 통합 diff(`--diff`, 변경 집합 표면)를 판정 |
28
+ | `pdks init` | 프로젝트 초기 파일 생성. 설정 파일과 텔레메트리 제외 항목 |
27
29
  | `pdks explain` | 조립된 등록표를 판정 없이 표시 |
28
30
  | `pdks docs [topic]` | 동봉된 주제를 읽음 |
29
31
  | `pdks docs search <query>` | 동봉된 문서를 검색 |
30
32
  | `pdks docs show <document-id>` | 동봉된 문서 또는 절을 표시 |
31
33
 
32
- `pdks docs`는 오프라인입니다. 네트워크가 아니라 설치된 패키지를 읽습니다. 플래그, JSON,
33
- 종료 코드는 [`pdks docs`](../cli/docs.ko.md)에 있습니다.
34
+ 세션 표면 설치기는 어댑터에 있습니다. `pdks-claude-code init`과 `pdks-grok init`입니다.
34
35
 
35
- <a id="polydeukes-export-map"></a>
36
- ## 공개 심볼
36
+ <a id="surface-from-input-mode"></a>
37
+ ### 입력 방식이 곧 표면이다
37
38
 
38
- <a id="root-export"></a>
39
- ### `.` 루트 진입점
39
+ `covenant check`는 입력이 어떻게 도착했는지로 표면을 정하며, 입력 자신의 키로 정하지
40
+ 않습니다. 표준 입력의 IR은 호스트가 실행 전에 관측한 호출 하나이므로 그 실행은 **세션
41
+ 표면(session surface)**이고, `--diff`는 어떤 생산자가 끝낸 변경 집합이므로 그 실행은
42
+ **변경 집합 표면(change-set surface)**입니다. 표면마다 공용 목록에 자기 목록을 더해
43
+ 컴파일합니다.
40
44
 
41
- | 심볼 | 종류 | 메모 |
45
+ | 입력 | 표면 | 컴파일하는 목록 |
42
46
  |---|---|---|
43
- | `loadConfig` | 함수 | `rootDir` 아래의 `polydeukes.config.*` 파일 하나를 찾아 읽습니다. 없거나, 둘 이상이거나, 파싱이나 검증에서 실패하면 예외를 던집니다. |
44
- | `runCovenantCheck` | 함수 | 커밋 표면을 실행하고 `{ exitCode: 0 \| 2 }`를 반환합니다. |
45
- | `ResolvedConfig` | 타입 | `@polydeukes/core`에서 다시 내보냅니다. |
46
- | `LoadConfigSpec`, `LoadedConfig` | 타입 | 설정 로더의 입력과 결과입니다. |
47
- | `CovenantCheckSpec`, `CovenantCheckOutcome`, `CheckDomain` | 타입 | 커밋 실행기의 입력, 결과, 관측 범위입니다. |
47
+ | 표준 입력의 입력 IR | `session` | `disciplines` 다음 `sessionDisciplines` |
48
+ | `--diff`와 함께 오는 표준 입력의 통합 diff | `changeSet` | `disciplines` 다음 `changeSetDisciplines` |
48
49
 
49
- <a id="session-export"></a>
50
- ### `./claude-code`
50
+ IR의 `session` 키는 다른 것을 말합니다. 호스트가 그 호출에 대해 증명한 사실, 곧 증인 밸브가
51
+ 읽는 사람의 턴과 대화 기록(transcript) 축, 그리고 셸 쓰기 증거에 필요한 변경 전 상태
52
+ 통로입니다. 세션을 싣지 않는 호스트(Grok 어댑터)도 여전히 세션 표면에서 판정되며, 그 호스트에
53
+ 없는 것은 표면이 아니라 그 셋이 읽는 증거입니다. `pdks explain`은 표면마다 목록 이름 둘과 그
54
+ 개수를 함께 출력합니다.
51
55
 
52
- | 심볼 | 종류 | 메모 |
53
- |---|---|---|
54
- | `runClaudeCodeHook` | 함수 | 세션 표면을 실행하고 `{ exitCode: 0 \| 2 }`를 반환합니다. |
55
- | `ClaudeCodeHookSpec` | 타입 | 세션 실행기 입력입니다. |
56
- | `ClaudeCodeHookOutcome` | 타입 | 세션 실행기 결과입니다. |
56
+ `pdks docs`는 오프라인입니다. 네트워크가 아니라 설치된 패키지를 읽습니다. 플래그, JSON,
57
+ 종료 코드는 [`pdks docs`](../cli/docs.ko.md)에 있습니다.
57
58
 
58
- 생성된 훅은 배럴 대신 이 서브패스를 가져옵니다. ESM의 정적 가져오기는 모듈을 즉시 평가하므로,
59
- 배럴을 사용하면 세션 도구 호출에 필요 없는 커밋 표면 실행기와 Git 어댑터까지 매번 불러옵니다.
59
+ <a id="polydeukes-export-map"></a>
60
+ ## 공개 심볼
60
61
 
61
62
  <a id="schema-export"></a>
62
63
  ### `./schema.json`
@@ -65,64 +66,124 @@
65
66
  |---|---|
66
67
  | `polydeukes.schema.json` | 통합 패키지에 동봉한 설정 스키마 사본입니다. |
67
68
 
68
- <a id="polydeukes-signatures"></a>
69
- ## 시그니처와 예제
69
+ <a id="covenant-module"></a>
70
+ ## 판정기(`covenant` 모듈)
70
71
 
71
- ```ts
72
- function loadConfig(spec: LoadConfigSpec): LoadedConfig;
72
+ 판정기는 통합 패키지의 `src/covenant/` 모듈입니다. 선언된 약속(covenant)을 평가해 판정
73
+ 결과를 내는 기능을 모두 여기서 구현하며, 두 조립 루트와 `pdks explain`이 이 모듈을 바로
74
+ 가져옵니다. 따로 설치할 것도, 불러올 것도, 충족할 peer 의존도 없습니다. 사용자는 설정의
75
+ `disciplines:` 블록과 `.polydeukes/roi.log`의 기록을 통해 동작을 확인합니다.
73
76
 
74
- type LoadConfigSpec = { rootDir: string };
77
+ <a id="ownership"></a>
78
+ ### 모듈이 담당하는 기능
75
79
 
76
- type LoadedConfig = {
77
- config: ResolvedConfig;
78
- configPath: string;
79
- };
80
-
81
- function runCovenantCheck(spec: CovenantCheckSpec): Promise<CovenantCheckOutcome>;
82
-
83
- type CovenantCheckSpec = {
84
- repoRoot: string;
85
- telemetryPath?: string;
86
- covenantDist?: string;
87
- ttyPrompt?: (prompt: string) => string | null;
88
- domain?: CheckDomain;
89
- };
90
-
91
- type CheckDomain =
92
- | { kind: 'staged' }
93
- | { kind: 'worktree' }
94
- | { kind: 'range'; base: string; head: string; ancestry?: 'merge-base' };
95
-
96
- function runClaudeCodeHook(spec: ClaudeCodeHookSpec): Promise<ClaudeCodeHookOutcome>;
97
-
98
- type ClaudeCodeHookSpec = {
99
- repoRoot: string;
100
- rawPayload?: string;
101
- telemetryPath?: string;
102
- covenantDist?: string;
103
- };
104
- ```
105
-
106
- `ancestry: 'merge-base'`는 `<base>...<head>`와 같이 공통 조상을 기준으로 비교한다는 뜻입니다.
107
- `rawPayload`가 없으면 훅은 표준 입력인 파일 디스크립터 0을 읽습니다. 훅 타입은 루트 배럴이 아니라 `polydeukes/claude-code`에 있습니다.
80
+ | 단위 | 하는 일 |
81
+ |---|---|
82
+ | `runCovenant` 래퍼 | 판정 본체를 실행하고, 비차단 `1`을 차단하는 `2`로 번역하고, 모든 호출을 기록합니다. 측정되지 않고 실행되는 약속은 없습니다 |
83
+ | 경로 라우팅 디스패처 | 보호 경로에 약속을 등록하고 일치하는 항목을 **모두** 실행합니다. 하나가 실패했다고 뒤의 판정을 생략하지 않습니다 |
84
+ | 메타 약속 | 판정 사슬 자신을 지키는 등록 셋 |
85
+ | TTL 증인 | 유효 시간이 있는 인간의 증인 밸브입니다. 차단 판정 뒤에만 확인합니다 |
86
+ | 델타 층 | 파일의 전후 쌍에 대한 신규 위반 전용 판정 |
87
+ | 규율(discipline) 라이브러리 | 설정의 `disciplines:` 항목을 별도 사용자 코드 없이 판정합니다 |
88
+
89
+ <a id="disciplines-and-meta-covenants"></a>
90
+ ### 규율 계열과 메타 약속
91
+
92
+ **`disciplines:` 항목은 선언 하나입니다.** 관측한 증거를 입력으로 받아 `judge = relate ∘ extract`를 계산합니다. 선언의 소스가 무엇을 묶는지가
93
+ 판정에 필요한 증거를 정하고, 그것이 곧 어느 표면에서
94
+ 판정될 수 있는지를 정합니다.
95
+
96
+ | 소스 | 판정 대상 | 필요한 증거 | 목록 |
97
+ |---|---|---|---|
98
+ | 고정 이름 `target.path` · `pre` · `post` · `state` | 변경 자체 | 파일 변경 | `disciplines` |
99
+ | 고정 이름 `changes` | 관측의 변경 집합 전체 | 끝난 변경 집합 | `changeSetDisciplines` |
100
+ | 고정 이름 `command` | 셸 호출의 명령줄 | 셸 호출입니다. Edit에는 없습니다 | `sessionDisciplines` |
101
+ | 고정 이름 `actor` | 관측을 만든 주체 | 호스트가 증명한 주체 | `sessionDisciplines` |
102
+ | `{ transcript: true }` | 세션 이력입니다. 이 호출 **앞에** 자격을 갖춘 호출이 실제로 실행됐는가 | 세션 | `sessionDisciplines` |
103
+ | `{ file: … }` | 다른 파일 | 표면의 파일 리더 | `disciplines` |
104
+ | `{ sidecar: true }` | 스폰 기록 채널 | 표면의 채널 리더 | `sessionDisciplines` |
105
+
106
+ 항목들을 쓰는 가이드는 [설정 레퍼런스의 `disciplines` 절](../configuration/index.ko.md#disciplines)에
107
+ 있습니다. 선언 문법은 코어의 `algebra-declaration.schema.json`입니다.
108
+
109
+ **메타 약속 셋**이 판정 사슬을 지킵니다. 다른 약속과 똑같은 약속이고, 아래 어휘가 그대로
110
+ 적용됩니다.
111
+
112
+ | 등록 | 축 | 판정 대상 |
113
+ |---|---|---|
114
+ | self-mod | 도구 | 편집 도구를 통한 보호 경로 변형입니다. 호출의 증명된 변형 대상만 대조하므로, 편집 **내용** 안의 보호 경로는 언급이고 통과합니다 |
115
+ | shell-mod | 셸 | 같은 것을 명령줄로 합니다. 보호 경로를 언급하는 명령은 첫 낱말이 읽기 전용임을 증명할 때만 통과합니다 |
116
+ | transcript-mod | 대화 기록 | 라이브 세션 대화 기록에 대한 쓰기입니다. 전체 경로 **등가**로 판정하고 보호 조상으로는 결코 보지 않습니다 |
108
117
 
109
- ```ts
110
- import { loadConfig, runCovenantCheck } from 'polydeukes';
111
- import { runClaudeCodeHook } from 'polydeukes/claude-code';
118
+ **낱말 여섯**이 텔레메트리 계약입니다. 판정 결과 다섯과 관측 하나입니다. `.polydeukes/roi.log`의
119
+ 행은 정확히 하나를 담고, CLI와 문서와 테스트가 같은 사건에 같은 낱말을 씁니다.
120
+ 행을 읽는 법은 [문제 해결](../../troubleshooting.ko.md#reading-verdict)에 있습니다.
112
121
 
113
- const { configPath } = loadConfig({ rootDir: process.cwd() });
114
- const check = await runCovenantCheck({ repoRoot: process.cwd() });
115
- const hook = await runClaudeCodeHook({ repoRoot: process.cwd(), rawPayload: '{}' });
116
- ```
122
+ | 판정 결과 | |
123
+ |---|---|
124
+ | `passed` | 호출이 판정됐고 약속을 지켰습니다 |
125
+ | `blocked` | 호출이 판정됐고 약속을 깼습니다 |
126
+ | `witnessed` | **차단된** 작업을 인간의 증언으로 허용했습니다. 위반이 없었다는 뜻이 아니며 허용 사실을 기록합니다 |
127
+ | `advised` | 호출을 멈추지 않고 위반을 기록했습니다. 항목이 `enforce: block`을 적지 않는 한 두 표면 모두에서 규율 항목의 기본 처분입니다 |
128
+ | `skipped` | 적용 범위가 일치했지만 판정할 수 없었습니다. **정상 판정이 아니라** 판정하지 못했다는 기록입니다 |
129
+ | `unattributed` | 보호 항목의 디스크 상태가 변했는데 그것을 설명하는 판정 기록이 없습니다. **판정이 아닙니다.** 이 행 때문에 차단되거나 통과하는 호출은 없으며, 세션 표면이 저장된 기준선과 상태를 대조한 뒤에 기록합니다 |
130
+
131
+ `unattributed`는 나머지 다섯이 답할 수 없는 물음에 답합니다. 다섯은 전부 판정기가 건네받은
132
+ 호출에 관해 쓰는 낱말이라, 선언된 호출 없이 도착한 쓰기는 행을 하나도 남기지 않습니다.
133
+ 인터프리터 안의 쓰기, 테스트 러너 자식 프로세스의 쓰기, 대상 경로를 자기 인자에서 조립하는
134
+ 스크립트가 그런 경우입니다. 상태 대조는 철자가 아니라 결과를 관측하므로 그 쓰기를 사후에
135
+ 기록합니다. 차단하지는 않습니다. 이미 일어난 쓰기이고, 대조는 판정의 양쪽 모두에서
136
+ fail-open입니다.
137
+
138
+ <a id="consumer-contract"></a>
139
+ ### 사용자와의 접점
140
+
141
+ - **설정의 `disciplines:` 블록.** 항목 하나가 등록 하나로 컴파일되고 자기 텔레메트리
142
+ 라벨을 답니다.
143
+ - **`protectedPaths`.** 경로 라우팅 디스패처가 여기에 대조합니다.
144
+ - **`witness` 블록.** TTL 증인 밸브를 설정합니다.
145
+ - **`.polydeukes/roi.log`.** 판정 기록을 추가하는 로그입니다.
146
+
147
+ 직접 불러올 필요는 없습니다. 통합 패키지가 두 표면에 필요한 구성을 조립합니다.
148
+
149
+ <a id="limits"></a>
150
+ ### 선언된 한계
151
+
152
+ - **셸 축은 `skipped` 행을 남기고, 그 행이 계약입니다.** 셸 명령의 대상을 텍스트에서
153
+ 예측하는 일은 결정 불가능합니다. 그래서 이 축이 지키는 불변식은 "아무것도 빠져나가지
154
+ 못한다"가 아니라 **기록 없이 통과하는 호출이 없다**입니다. 새로운 명령 형태가 `skipped`로 기록되면 판정하지 못한 한계를 확인할 수 있습니다. 행이 아예 없는
155
+ 통과, 또는
156
+ 판정하지 않고 `passed`로 기록된 통과가 결함입니다.
157
+ - **세션을 증명하지 못하는 호스트에서 세션 전용 선언은 `skipped`를 남깁니다.** 그 항목은
158
+ 세션 표면에 있으므로 컴파일되지만, 선언이 묶는 대화 기록이 없고, 항목 자신의
159
+ `supply: pass`가 그 부재를 사유 `supply-pass`의 `skipped` 행으로 처리합니다. 정책이 없으면
160
+ 없는 세션은 판정 불가(exit 2)이지 자동 건너뛰기가 아닙니다.
161
+ - **표면은 다른 표면의 목록을 등록하지 않습니다.** `command`나 대화 기록을 읽는 선언은 변경
162
+ 집합에 대해 컴파일되지 않고, `changes`를 읽는 선언은 호출 하나에 대해 컴파일되지 않으므로,
163
+ 관측하지 않는 표면에는 행도 남기지 않습니다. 표면이 어떤 항목을 지니는지는 텔레메트리
164
+ 로그가 아니라 `pdks explain`과 설정 파일의 목록 이름에서 읽습니다.
165
+ - **컴파일러가 해석하지 못하는 선언은 스킵 등록으로 컴파일됩니다.** 라우팅은 그대로 두고
166
+ 본체를 두지 않습니다. 등재 표 밖의 단계, 단계의 키 밖의 인자, 컴파일되지 않는 패턴,
167
+ 쌍/단일 불일치가 그 경우입니다. 그래서 조립은 예외를 던지지 않습니다. 해소되지 않는 항목
168
+ 하나가 형제 항목과 메타 약속과 밸브까지 무너뜨리면, 그것을 일으킨 설정을 고칠 길이
169
+ 사라지기 때문입니다. 예약 기전, 또는 카탈로그가 거부하는 축·관계는 다른 단계입니다.
170
+ 설정을 읽는 단계에서 파일을 거부하고(exit 2) skip 행은 남지 않습니다. 판정 시점에 세계에 없는
171
+ 소스는 세 번째 경우입니다. 선언 자신의 `supply` 정책이 처분하고, 정책이 없으면 본체가
172
+ 판정 불가(exit 2)로 답합니다. 통과로 답하지 않습니다.
173
+ - **완전 봉쇄는 목표가 아닙니다.** 여기에 차단 목록은 없습니다. 우회 철자를 열거하는 일은
174
+ 언제나 한 걸음 뒤지므로 논리를 뒤집었습니다. 보호 경로를 언급하면 안전이 증명되지 않는
175
+ 한 차단합니다. 간접 경로 계산 같은 잔여 벡터는 차단 대상이 아니라 텔레메트리 대상입니다.
176
+ 마찰 밸브 둘, 곧 읽기 전용 허용 목록과 TTL 증인은 둘 다 측정 가능한 흔적을 남깁니다.
177
+ - **밸브는 판정 뒤에 확인합니다.** 실제로 차단한 판정만 증언으로 열 수 있고, 문장 중간의 토큰
178
+ 언급은 밸브를 무장시키지 않으며, AI는 자기 밸브를 결코 스스로 열 수 없습니다.
117
179
 
118
180
  <a id="polydeukes-failure-boundaries"></a>
119
181
  ## 실패 경계
120
182
 
121
- - `loadConfig()`는 설정이 없거나, 이상이거나, 파싱이나 검증에서 실패하면 예외를 던집니다.
122
- - `runCovenantCheck()`와 `runClaudeCodeHook()`는 예외를 던지지 않고 `{ exitCode: 0 \| 2 }`를 반환합니다.
183
+ - `runCovenantCheck()`는 예외를 던지지 않고 `{ exitCode: 0 \| 2 }`를 반환합니다.
123
184
  - 숫자 코드는 `@polydeukes/core`의 `EXIT_UPHOLD`(`0`), `EXIT_BREAK_NON_BLOCKING`(`1`),
124
185
  `EXIT_BREAK_BLOCKING`(`2`)입니다. 우산 실행기는 `0` 또는 `2`만 노출하며 `1`을 반환하지 않습니다.
125
- - `pdks covenant check`가 증인 토큰을 요청하는 것은 스테이징한 변경을 검사할 때뿐입니다. `--worktree`와 `--range`에서는 묻지 않습니다.
186
+ - `pdks covenant check`는 사람에게 묻지 않습니다. 표준 입력을 읽고 종료 코드 0 또는 2를 내며, 그 종료 코드의 뜻은 호출한 쪽이 정합니다.
126
187
  - `pdks docs`와 `pdks explain`은 실패 시 중간 출력 없이 끝납니다.
127
188
 
128
189
  <a id="polydeukes-see-also"></a>