polydeukes 0.6.1 → 0.8.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.
Files changed (108) hide show
  1. package/README.ko.md +16 -28
  2. package/README.md +16 -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 +109 -0
  12. package/dist/covenant/discipline.js +667 -0
  13. package/dist/covenant/dispatch.d.ts +156 -0
  14. package/dist/covenant/dispatch.js +250 -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 +74 -41
  40. package/dist/covenant-check.js +372 -129
  41. package/dist/diff-ir.d.ts +28 -0
  42. package/dist/diff-ir.js +310 -0
  43. package/dist/docs/README.ko.md +10 -9
  44. package/dist/docs/README.md +10 -9
  45. package/dist/docs/catalog.json +76 -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 +67 -34
  51. package/dist/docs/how-to/connect-surfaces.md +67 -34
  52. package/dist/docs/how-to/write-disciplines.ko.md +64 -7
  53. package/dist/docs/how-to/write-disciplines.md +65 -7
  54. package/dist/docs/index.json +744 -362
  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 +20 -15
  60. package/dist/docs/reference/cli/explain.md +22 -15
  61. package/dist/docs/reference/cli/init.ko.md +91 -52
  62. package/dist/docs/reference/cli/init.md +81 -55
  63. package/dist/docs/reference/configuration/index.ko.md +182 -71
  64. package/dist/docs/reference/configuration/index.md +188 -76
  65. package/dist/docs/reference/packages/adapter-claude-code.ko.md +39 -11
  66. package/dist/docs/reference/packages/adapter-claude-code.md +40 -14
  67. package/dist/docs/reference/packages/adapter-codex.ko.md +99 -0
  68. package/dist/docs/reference/packages/adapter-codex.md +100 -0
  69. package/dist/docs/reference/packages/adapter-grok.ko.md +81 -0
  70. package/dist/docs/reference/packages/adapter-grok.md +83 -0
  71. package/dist/docs/reference/packages/core.ko.md +50 -4
  72. package/dist/docs/reference/packages/core.md +55 -6
  73. package/dist/docs/reference/packages/polydeukes.ko.md +139 -77
  74. package/dist/docs/reference/packages/polydeukes.md +147 -81
  75. package/dist/docs/reference/packages/sdk-ts.ko.md +164 -0
  76. package/dist/docs/reference/packages/sdk-ts.md +167 -0
  77. package/dist/docs/troubleshooting.ko.md +51 -19
  78. package/dist/docs/troubleshooting.md +53 -17
  79. package/dist/docs/tutorials/first-judgment.ko.md +6 -4
  80. package/dist/docs/tutorials/first-judgment.md +6 -4
  81. package/dist/explain.d.ts +1 -1
  82. package/dist/explain.js +37 -22
  83. package/dist/load-config.d.ts +25 -1
  84. package/dist/load-config.js +21 -3
  85. package/dist/pre-state-reader.d.ts +1 -1
  86. package/dist/pre-state-reader.js +1 -1
  87. package/dist/scaffold-project.d.ts +2 -2
  88. package/dist/scaffold-project.js +12 -4
  89. package/dist/schema/polydeukes.schema.json +12 -0
  90. package/dist/worktree-reader.d.ts +19 -0
  91. package/dist/worktree-reader.js +30 -0
  92. package/package.json +5 -18
  93. package/dist/claude-code-hook.d.ts +0 -79
  94. package/dist/claude-code-hook.js +0 -372
  95. package/dist/claude-code.d.ts +0 -6
  96. package/dist/claude-code.js +0 -6
  97. package/dist/covenant-module.d.ts +0 -25
  98. package/dist/covenant-module.js +0 -42
  99. package/dist/docs/reference/packages/adapter-git.ko.md +0 -101
  100. package/dist/docs/reference/packages/adapter-git.md +0 -108
  101. package/dist/docs/reference/packages/covenant.ko.md +0 -115
  102. package/dist/docs/reference/packages/covenant.md +0 -123
  103. package/dist/index.d.ts +0 -22
  104. package/dist/index.js +0 -21
  105. package/dist/init-claude-code.d.ts +0 -52
  106. package/dist/init-claude-code.js +0 -586
  107. package/dist/init-grok.d.ts +0 -51
  108. package/dist/init-grok.js +0 -242
@@ -56,64 +56,35 @@ rejected by that adapter's validator, with the full field path in the error.
56
56
 
57
57
  ```yaml
58
58
  adapters:
59
- git:
60
- enforce: advise
61
- protectedPaths:
62
- - 'packages/core/src'
59
+ example:
60
+ someKey: 'a value the example adapter defines'
63
61
  ```
64
62
 
65
- <a id="adapters-git"></a>
66
- ### `adapters.git` — the git commit adapter
67
-
68
- | Key | Values | Default | Meaning |
69
- |---|---|---|---|
70
- | `enforce` | `block` \| `advise` | `block` | Enforcement level of the commit surface |
71
- | `protectedPaths` | string array | `[]` | Additive protection scope judged by the commit surface only |
72
-
73
- - **`block`** — a staged change that breaks a covenant judged at block level blocks the
74
- commit (exit 2): a protected path, or an entry promoted with `enforce: block`. An ordinary
75
- entry keeps its own default `advise` under this setting (see `enforce` below). The
76
- only way through is the witness valve: a human answering the TTY prompt with the full
77
- token. The prompt names what it asks the human to witness — the broken registration,
78
- the matched entry, and the fact that the one answer covers the whole commit. An absent
79
- namespace, an absent `adapters` map, or an absent `enforce` key all mean `block` — not
80
- writing the key selects the strictest level.
81
- - **`advise`** — the commit surface becomes a backstop without a block: a verdict on a
82
- staged change is recorded as an `advised` telemetry event and the commit proceeds
83
- (exit 0) with one advisory line on stderr. No TTY prompt fires. The judgment criteria stay
84
- the same; violations no longer block. A run that cannot judge (missing or invalid config,
85
- an unresolvable judge body) still fails closed at exit 2, at either level.
86
-
87
- **`protectedPaths` here is an additive scope.** The commit surface judges the union of the
88
- top-level `protectedPaths` and this list — concatenated (common first) and normalized as one,
89
- so spelling and dedupe rules are identical for both. The session surface never reads it: the
90
- list exists for paths whose edit is legitimate work during a session but must pass a judged
91
- checkpoint when it is promoted into repository history — a judgment chain's own sources are
92
- the canonical tenant. As the enforcement level is the observer's setting, so is the
93
- additional scope. There is no subtractive vocabulary: a config line can widen a surface's
94
- scope, never quietly strip one.
95
-
96
- The session surface (the editor-time hook) has no level setting here. What it blocks is the
63
+ **`protectedPaths` is one list for both surfaces.** There is no additive change-set-only scope
64
+ and no surface-level enforcement key in the config. The change-set surface judges the same
65
+ normalized list the session surface does. A violation exits 2 on the session surface; on the
66
+ change-set surface it lands `advised` at exit 0 unless the check runs with `--enforce block`. The
67
+ judge emits only that exit code: whether a commit stops is the user's hook wiring, so a
68
+ telemetry row records the verdict, not the commit's fate — a `blocked` row can sit beside a
69
+ commit that proceeded because the wiring ignored the exit code.
70
+
71
+ The session surface (the editor-time hook) has no level setting either. What it blocks is the
97
72
  judging chain's own protection — `protectedPaths` mutations and mentions on the tool and
98
73
  shell axes, the session transcript, an assembly that cannot judge (missing or invalid
99
74
  config, unbuilt judge, unparseable payload, a routing that could not answer) — plus any
100
75
  entry promoted with `enforce: block`. Every other discipline entry lands `advised` there.
101
76
 
102
- **Declarations that read the session skip on the commit surface when their supply policy is `pass`.**
103
- A commit has no session to look at, so a declaration whose `sources` bind the transcript — a `precedent`,
104
- `phase-order`, `turn-locality` or `stated-ground` entry — cannot be judged there; demanding
105
- evidence a commit cannot carry would block every matching commit with no legitimate way
106
- through. The declaration's own `supply: { session: 'pass' }` disposes of the absence: when
107
- its scope matches a staged change it records a `skipped` telemetry event carrying the reason
108
- token `supply-pass` and lets the commit proceed. The record carries the entry's `id` and the
109
- change it would have judged, so a gate that did nothing says so in the data — and it appears
110
- **only when the entry's scope actually matched**. A declaration scoped on the `command`
111
- source records nothing at all there: a staged diff carries no command line, so no world it
112
- observes is ever admitted.
113
-
114
- This is the same disposition the session surface uses whenever it has no transcript to
115
- read **and** the declaration's `supply` is `pass`. Without that policy a missing source is
116
- unjudgeable (exit 2), not an automatic skip.
77
+ **A declaration reaches only the surface that observes its channels.** A transcript reader is
78
+ written in `sessionDisciplines` and is never compiled for a change set; a `changes` reader is
79
+ written in `changeSetDisciplines` and is never compiled for one call. The three lists and the
80
+ rule that places an entry in one of them are in [the three discipline
81
+ lists](#three-lists).
82
+
83
+ A declaration's `supply` policy still governs a channel the surface registers but the host
84
+ does not prove. A host that carries no session (the Grok adapter) leaves a
85
+ `sources: { session: { transcript: true } }` entry's source absent, and `supply: { session: 'pass' }`
86
+ records a `skipped` telemetry event carrying the reason token `supply-pass` and lets the call
87
+ proceed. Without that policy a missing source is unjudgeable (exit 2), not an automatic skip.
117
88
 
118
89
  <a id="telemetry"></a>
119
90
  ## `telemetry`
@@ -137,7 +108,7 @@ Optional.
137
108
 
138
109
  ```yaml
139
110
  witness:
140
- token: 'covenant witness' # the phrase a human types in the conversation
111
+ token: 'pdks witness' # the phrase a human types in the conversation
141
112
  ttlMinutes: 10 # validity window, in minutes, from that message
142
113
  ```
143
114
 
@@ -164,7 +135,7 @@ carrying the token alone does, with any following lines free for the work itself
164
135
  A message that invokes — the token alone on the first line, the rest free:
165
136
 
166
137
  ```text
167
- covenant witness
138
+ pdks witness
168
139
 
169
140
  now fix the hook file
170
141
  ```
@@ -172,7 +143,7 @@ now fix the hook file
172
143
  A message that merely mentions — the valve stays shut:
173
144
 
174
145
  ```text
175
- so when does `covenant witness` expire?
146
+ so when does `pdks witness` expire?
176
147
  ```
177
148
 
178
149
  The token's value is free — any phrase works, and it is never checked for a prefix or a
@@ -183,12 +154,141 @@ when the token arrives in a message positively identified as human-typed in the
183
154
  transcript, so an AI that knows the token still cannot forge one. Witnessed judgments are
184
155
  recorded as `witnessed`, never silent.
185
156
 
157
+ <a id="three-lists"></a>
158
+ ## The three discipline lists
159
+
160
+ A discipline is written in one of three lists, and which one is a fact about the declaration
161
+ rather than a choice the author makes. A declaration names the evidence channels it reads in
162
+ its own syntax, and a surface observes some channels and not others, so the list follows from
163
+ the channels.
164
+
165
+ | List | Judged on | What its declarations read |
166
+ |---|---|---|
167
+ | `disciplines` | both surfaces | the changed file itself (`target.path` · `pre` · `post` · `state`) and `file` sources — nothing else. Drafts live here too |
168
+ | `sessionDisciplines` | the session surface only | at least one of `command` · `transcript` · the `sidecar` channel · `actor`, and never `changes` |
169
+ | `changeSetDisciplines` | the change-set surface only | `changes`, and no session channel |
170
+
171
+ The session surface is one call a host observed before it ran — an adapter hook, or a program
172
+ calling the SDK with an input IR. It carries the command line, the conversation history, the
173
+ spawn-record channel, and the actor, and it does not carry the finished change set. The
174
+ change-set surface is `pdks covenant check --diff` over a change set some producer finished.
175
+ It carries `changes`, and it carries no command line, no history, and no actor.
176
+
177
+ <a id="channel-to-list"></a>
178
+ ### Channel to list
179
+
180
+ Each channel appears in a declaration in a fixed syntactic position, and the loader reads
181
+ those positions to derive the list.
182
+
183
+ | Channel | How it appears in a declaration | List |
184
+ |---|---|---|
185
+ | `transcript` | `sources: { session: { transcript: true } }` | `sessionDisciplines` |
186
+ | `channel` | `sources: { spawns: { sidecar: true } }` | `sessionDisciplines` |
187
+ | `command` | `scope: { source: 'command' }`, or `{ op: 'source', of: 'command' }` in any pipeline | `sessionDisciplines` |
188
+ | `actor` | `{ op: 'source', of: 'actor' }` in any pipeline | `sessionDisciplines` |
189
+ | `changes` | `{ op: 'source', of: 'changes' }` in any pipeline | `changeSetDisciplines` |
190
+ | none of the five | the declaration reads the changed file and `file` sources alone | `disciplines` |
191
+
192
+ A `witness` block's own `extract` is walked with the body's, so a valve reading the transcript
193
+ puts its entry in `sessionDisciplines` like any other transcript reader.
194
+
195
+ <a id="placement-rule"></a>
196
+ ### The placement rule
197
+
198
+ `disciplines` takes an entry binding none of the five channels, plus every draft.
199
+ `sessionDisciplines` takes an entry binding at least one session channel and not `changes`.
200
+ `changeSetDisciplines` takes an entry binding `changes` and no session channel. Anything else
201
+ is a `ConfigValidationError` at load time, on both surfaces, and the message names the entry,
202
+ the channels it reads, and the list it belongs in:
203
+
204
+ ```text
205
+ disciplines[7] ('merge-is-the-users-call') reads transcript, command: it belongs in sessionDisciplines
206
+ ```
207
+
208
+ Two shapes have no destination to name. A declaration binding both `changes` and a session
209
+ channel is refused outright, because no surface observes both at once:
210
+
211
+ ```text
212
+ sessionDisciplines[2] ('pairs-across-a-session') reads transcript, changes: no surface observes both changes and a session channel
213
+ ```
214
+
215
+ A draft binds nothing — it carries no declaration — so it belongs in `disciplines`, and a
216
+ draft written in either surface list is refused with `a draft belongs in disciplines`.
217
+
218
+ Ids are unique across all three lists and the three meta-covenant labels (`self-mod` ·
219
+ `shell-mod` · `transcript-mod`): the telemetry label space is one space, and `pdks explain`
220
+ and every label-keyed reader index by the label alone.
221
+
222
+ <a id="lists-in-practice"></a>
223
+ ### What each surface compiles
224
+
225
+ `pdks covenant check` reading an input IR on stdin compiles `disciplines` followed by
226
+ `sessionDisciplines`; the same command with `--diff` compiles `disciplines` followed by
227
+ `changeSetDisciplines`. `pdks explain` prints both surfaces with each list's name and count in
228
+ the surface header, so the placement of every entry is readable without running a judgment.
229
+ This repository's live config places 11 judged entries and 1 draft in `disciplines`, 13 in
230
+ `sessionDisciplines`, and 1 (`docs-stay-bilingual`) in `changeSetDisciplines` — 24
231
+ registrations on the session surface and 12 on the change-set surface.
232
+
233
+ ```yaml
234
+ disciplines: # both surfaces
235
+ - id: 'covenant-vocabulary'
236
+ declare:
237
+ mechanism: 'added-only'
238
+ scope: { source: 'target.path', include: ['^packages/'] }
239
+ supply: { pre: 'empty', post: 'empty' }
240
+ extract:
241
+ before: [{ op: 'source', of: 'pre' }, { op: 'lines' }]
242
+ after: [{ op: 'source', of: 'post' }, { op: 'lines' }]
243
+ added: [{ op: 'onlyIn', of: 'after', notIn: 'before' }]
244
+ relate:
245
+ - { id: 'nothing-added', relation: { op: 'empty', of: 'added' }, message: 'adds {value}' }
246
+
247
+ sessionDisciplines: # the session surface only — reads command
248
+ - id: 'pnpm-only'
249
+ declare:
250
+ mechanism: 'forbidden-command'
251
+ scope: { source: 'command' }
252
+ extract:
253
+ hits:
254
+ - { op: 'source', of: 'command' }
255
+ - { op: 'lines' }
256
+ - { op: 'matches', re: '\bnpm install\b' }
257
+ relate:
258
+ - { id: 'no-npm', relation: { op: 'empty', of: 'hits' }, message: '{value}' }
259
+
260
+ changeSetDisciplines: # the change-set surface only — reads changes
261
+ - id: 'docs-stay-bilingual'
262
+ declare:
263
+ mechanism: 'companion'
264
+ scope: { source: 'target.path', include: ['\.md$'] }
265
+ extract:
266
+ en:
267
+ - { op: 'source', of: 'target.path' }
268
+ - { op: 'keyByPattern', re: '^(.+?)(?<!\.ko)\.md$' }
269
+ koChanged:
270
+ - { op: 'source', of: 'changes' }
271
+ - { op: 'items' }
272
+ - { op: 'keyByPattern', re: '^(.+)\.ko\.md$' }
273
+ relate:
274
+ - id: 'ko-follows'
275
+ relation: { op: 'implies', of: 'en', requires: 'koChanged' }
276
+ message: '{value} changed without {key}.ko.md'
277
+ ```
278
+
279
+ Moving an entry between lists is the whole edit: the entry's body is unchanged by the move,
280
+ and the loader is what says whether the new place is the right one.
281
+
186
282
  <a id="disciplines"></a>
187
283
  ## `disciplines`
188
284
 
189
285
  Optional. Each entry is one discipline: a practice the team imposes on itself, declared as
190
- data. A judged entry carries a `declare` block — the one judged form, a declaration whose
191
- `scope` lives inside the block — an `id` (the telemetry label), and optionally a `why` (the
286
+ data. Everything in this section applies to an entry in any of the three lists; which list an
287
+ entry is written in is decided by [the placement rule](#placement-rule), and
288
+ `sessionDisciplines` and `changeSetDisciplines` take the same entry shape as `disciplines`
289
+ minus the draft form. A judged entry carries a `declare` block — the one judged form, a
290
+ declaration whose `scope` lives inside the block — an `id` (the telemetry label), and
291
+ optionally a `why` (the
192
292
  reason, which travels with the block message the agent reads) and an `enforce` level. The
193
293
  closed key set is `id` · `why` · `enforce` · `declare`; any other key is refused.
194
294
 
@@ -219,10 +319,10 @@ is one line.
219
319
  **`enforce` — the entry's own level.** Optional on any judged entry: `block` or `advise`.
220
320
  **Absent means `advise`.** Under `advise` a break is recorded as an `advised` telemetry
221
321
  event and the call proceeds (exit 0), with the break message still written to stderr;
222
- `block` is the promotion — it pins the entry at block. The entry's level composes with the
223
- surface's (`adapters.git.enforce` on the commit surface; the session surface has none) and
224
- the lenient side wins — an `advise` on either axis makes the entry advise, and an explicit
225
- `block` never raises a surface the observer set to advise. An unjudgeable body (never
322
+ `block` is the promotion — it pins the entry at block. The config carries no surface-level
323
+ enforcement key: absent or `advise` means `advised` on both surfaces, `block` means exit 2 on
324
+ the session surface and on a change-set check run with `--enforce block` (the change-set
325
+ surface's default posture is advise for every verdict). An unjudgeable body (never
226
326
  built, or one that cannot be loaded) still blocks whatever the level. A draft carries no
227
327
  `enforce`; any
228
328
  other value is rejected at load time. `pdks explain` prints the level an entry declares
@@ -252,7 +352,10 @@ banned word, a stray `.only`, a citation that resolves nowhere — is written as
252
352
  verdict. Existing occurrences are forgiven, so adopting the discipline never blocks a legacy
253
353
  codebase. `supply: empty` is what lets a file creation (no `pre`) count as all-added and a
254
354
  deletion (no `post`) as adding nothing; the `scope` block replaces `in`/`except` with
255
- regular expressions over the path.
355
+ regular expressions over the path. It takes two lists, `include` and `exclude`: a path is
356
+ in scope when it matches at least one `include` pattern (an absent `include` admits every
357
+ path) and matches no `exclude` pattern. `excludeIgnoreCase: true` makes the `exclude`
358
+ patterns case-insensitive; `include` is always case-sensitive.
256
359
 
257
360
  ```yaml
258
361
  disciplines:
@@ -312,7 +415,14 @@ declaration reads that source, cuts it into lines, keeps the lines a pattern mat
312
415
  requires the result to be `empty`. It scopes on `command` so that only shell calls are
313
416
  admitted: an Edit carries no command line, and a declaration reading a source its world
314
417
  lacks is unjudgeable. A multi-line command is judged line by line, so `^` means the start
315
- of a line; a pattern that would span a line boundary does not match.
418
+ of a line; a pattern that would span a line boundary does not match. Heredoc bodies and
419
+ herestring words are not part of the source: bash passes them to the command as stdin
420
+ data rather than executing them, so a banned token quoted inside one is not a match, and
421
+ whether that data becomes a file write is judged by the shell-evidence path instead. Text
422
+ bash expands first stays in the source — a body under an unquoted delimiter that carries
423
+ `$` or a backtick, and a herestring word holding a substitution — as does a line the
424
+ tokenizer could not finish reading. What the receiving command does with its stdin is not
425
+ judged: a script fed to an interpreter is data to bash and a program to the interpreter.
316
426
 
317
427
  ```yaml
318
428
  - id: 'hooks-stay-armed'
@@ -338,8 +448,9 @@ declaration the user turns and tool calls as one snapshot, `toolUses` picks the
338
448
  `matches` finds the required one; `nonEmpty` is the verdict. A call the covenant blocked,
339
449
  one a human refused, and one that simply failed are not precedent. The pattern is matched
340
450
  anywhere in a command line — a line that merely mentions the command counts, a declared
341
- limit. `supply: { session: 'pass' }` is what makes the commit surface record `skipped`
342
- instead of blocking every matching commit.
451
+ limit. `supply: { session: 'pass' }` is what disposes of an absent session on a host that proves
452
+ none; an entry reading the transcript is written in `sessionDisciplines`, so a change set never
453
+ compiles it at all.
343
454
 
344
455
  ```yaml
345
456
  - id: 'dependency-needs-npm-view'
@@ -411,16 +522,15 @@ Each observation is judged as one **world** with seven source names: `target.pat
411
522
  repo-relative path), `pre` and `post` (the file's text on the side the change carries —
412
523
  a creation has no `pre`, a deletion no `post`), `state` (`{ pre, post }`, present only
413
524
  on a modification), and `changes` (every path the observation changes — the one call on the
414
- session surface, the whole staged set on the commit surface), and `command` (the shell
525
+ session surface, the whole staged set on the change-set surface), and `command` (the shell
415
526
  call's command line — present on a shell call only, and a shell call that changes no file
416
527
  is one world of its own, so a declaration scoped on `command` sees it while one scoped on
417
528
  `target.path` does not), and `actor` (the observation's actor — described below). A
418
- declaration that reads `changes` is judged only where the whole change set is observed: the
419
- session surface records it `skipped`, the same disposition the commit surface gives a
420
- declaration that reads the session, because one call can never carry the other half of a
421
- pair. This repository's live config
422
- carries one — `docs-stay-bilingual`, an `implies` over the `.md`/`.ko.md` pair, advised
423
- on the commit surface when one side is staged without the other. A declaration that needs a
529
+ declaration that reads `changes` is written in `changeSetDisciplines` and compiled only where
530
+ the whole change set is observed, because one call never carries the other half of a pair.
531
+ This repository's live config carries one — `docs-stay-bilingual`, an `implies` over the
532
+ `.md`/`.ko.md` pair, advised on the change-set surface when one side is staged without the
533
+ other. A declaration that needs a
424
534
  file outside the target names it in a `sources` block, `sources: { en: { file:
425
535
  'locales/en.json' } }`, and reads it as `{ op: 'source', of: 'en' }`; the path is
426
536
  repo-relative (no leading `/`, no `..` segment) and the name may not be one of the seven. The
@@ -430,16 +540,18 @@ touches is read from the change's `post`, so both surfaces judge the same text.
430
540
  kind, `sources: { spawns: { sidecar: true } }`, names the session's spawn-record channel
431
541
  instead of a path — the subagent records the host keeps beside the transcript, supplied as
432
542
  one JSON array; where the channel lives is the surface's fact, so the value is the marker
433
- `true`, and on the commit surface (which has no session) the channel is always absent. A
543
+ `true`, and on the change-set surface (which has no session) the channel is always absent. A
434
544
  third kind, `sources: { session: { transcript: true } }`, names the session's own
435
545
  conversation history — the user turns and tool calls the surface reads, handed to the
436
546
  declaration as one snapshot whose entries carry their
437
547
  observation ordinal; the history steps (`toolUses`, `userTexts`, `first`, `ageMs`) read it, and
438
- `agentType` reads the parsed sidecar. This repository's live config carries one —
439
- `tests-before-implementation`, an `ordered` over the ordinals of two subagent spawns, which
440
- the commit surface (no session) records `skipped`. The seventh fixed name, `actor`, is the
548
+ `agentType` reads the parsed sidecar and requires `is`, the agent type to keep
549
+ (`{ op: 'agentType', is: 'tdd-test-writer' }`); without it the step does not compile and
550
+ the entry lands as a skip registration. This repository's live config carries one —
551
+ `tests-before-implementation`, an `ordered` over the ordinals of two subagent spawns, written
552
+ in `sessionDisciplines`. The seventh fixed name, `actor`, is the
441
553
  observation's actor — `{ agentType }` inside a subagent, `{}` in the main session, absent
442
- where the surface proves none (the commit surface) — read as `{ op: 'source', of: 'actor' }`
554
+ where the surface proves none (the change-set surface) — read as `{ op: 'source', of: 'actor' }`
443
555
  followed by `select` on `agentType`; it derives the `actor` axis the `producer-owned` and
444
556
  `actor-scope` mechanisms require, and this repository's live config carries one of each
445
557
  (`tests-are-the-writers`, `commits-come-from-the-main-session`). A `supply` key must name
@@ -2,11 +2,11 @@
2
2
 
3
3
  [English](adapter-claude-code.md) · **한국어**
4
4
 
5
- > **세션 표면의 번역기**입니다. PreToolUse 페이로드가 약속(covenant) 입력 IR이 되고,
6
- > 판정기가 읽는 파일 변경 증거와 대화 기록(transcript) 통로가 함께 실립니다.
5
+ > **Claude Code의 설치 단위**입니다. PreToolUse 페이로드가 약속(covenant) 입력 IR이 되고,
6
+ > 판정기가 읽는 파일 변경 증거와 대화 기록(transcript) 통로가 함께 실립니다. 세션 표면을
7
+ > 프로젝트에 설치하는 것도 이 패키지가 합니다.
7
8
  >
8
- > 알파입니다. 통합 패키지의 의존성으로 설치되므로 일반 사용자가 따로 설치하거나 불러올 필요는 없습니다. 세션 표면은
9
- > [`polydeukes/claude-code`](polydeukes.ko.md#session-export)를 통해 닿습니다.
9
+ > 베타입니다. `polydeukes`와 함께 설치하며, `polydeukes`는 이 패키지의 `peerDependency`입니다.
10
10
 
11
11
  <a id="ownership"></a>
12
12
  ## 담당하는 기능
@@ -16,15 +16,22 @@ Claude Code의 입력을 공통 형식으로 번역합니다. 에이전트와
16
16
 
17
17
  | 단위 | 하는 일 |
18
18
  |---|---|
19
+ | `pdks-claude-code` 실행 파일 | 하위 명령 하나 `pdks-claude-code init`으로 프로젝트에 세션 표면을 등록합니다 |
20
+ | `runHook` | PreToolUse 페이로드 하나를 입력 IR로 바꾸고 판정기를 스폰합니다 |
19
21
  | 페이로드 상향 번역 | 원본 PreToolUse 페이로드가 `CovenantInput`이 됩니다 |
20
22
  | 예상 변경 후 상태 | 편집이 적용되면 파일이 무엇을 담을지 디스크를 건드리지 않고 계산합니다 |
21
23
  | 파일 변경 증거 | 디스크의 변경 전 상태와 예상 변경 후 상태를 짝지어 변경 증거를 만듭니다 |
22
24
  | 대화 기록(transcript) 제공자 | 세션 JSONL 파일을 `CanonicalTranscript`로 바꿉니다 |
23
- | 텔레메트리 배선 | 전체 처리 경로에서 호출이 기록되도록 연결합니다 |
24
25
 
25
- 이 패키지는 약속(covenant) 패키지를 결코 불러오지 않습니다. 판정 전달 함수는 통합 패키지가
26
- **주입**하고, 그래서 의존은 코어를 통해 한 방향으로만 흐릅니다. `@polydeukes/core`는
27
- `peerDependency`로 부릅니다. 판정기와 같은 어휘를 공유하며 별도 사본을 설치하지 않습니다.
26
+ 생성된 훅 위임자가 불러오는 것이 `runHook({ repoRoot })`입니다. `tools`와 `session` 증거를
27
+ 실은 입력 IR을 만든 뒤 `repoRoot`에서 `pdks covenant check --enforce block`을 스폰하고 그
28
+ 자식 프로세스의 종료 코드를 그대로 돌려줍니다. 판정은 그 자식 프로세스가 하며, 이 패키지에는
29
+ 판정 코드가 없습니다.
30
+
31
+ **이 패키지는 텔레메트리 행을 쓰지 않습니다.** 스폰 전에 실패하면 그 사실을 한 줄로 만들어
32
+ `pdks`의 표준 입력으로 보내고, `pdks`가 fail-closed 행을 기록합니다. 호출 하나에 행 하나는
33
+ 그대로입니다. 판정기를 불러오지도 않습니다. `polydeukes`와 `@polydeukes/core` 모두
34
+ `peerDependencies`이므로 어휘와 판정기를 공유할 뿐 사본을 따로 설치하지 않습니다.
28
35
 
29
36
  <a id="translation"></a>
30
37
  ## 페이로드 번역과 세 축
@@ -62,9 +69,22 @@ Claude Code의 입력을 공통 형식으로 번역합니다. 에이전트와
62
69
  <a id="consumer-contract"></a>
63
70
  ## 사용자와의 접점
64
71
 
65
- - **생성된 훅.** 통합 패키지의 `claude-code` 서브패스를 통해 이 어댑터를 불러옵니다. 패키지를 갱신하면 실행할 구현도 바뀌지만 훅 파일은 그대로입니다.
72
+ 프로젝트 루트에서 두 줄이면 Claude Code 세션 표면이 섭니다.
73
+
74
+ ```sh
75
+ npm install --save-dev polydeukes @polydeukes/core @polydeukes/adapter-claude-code
76
+ npx pdks-claude-code init
77
+ ```
78
+
79
+ `pdks-claude-code init`은 프로젝트에서 `polydeukes`를 찾고, 에이전트와 무관한 초기 파일을
80
+ 만드는 `pdks init`을 스폰한 뒤, Claude Code 산출물 넷을 기존 파일을 덮지 않고 씁니다.
81
+ 다시 실행하면 이미 있는 산출물마다 `skipped`로 보고하고 아무것도 덮어쓰지 않습니다.
82
+ 산출물 전체 목록은 [`pdks init`](../cli/init.ko.md#init-claude-code)에 있습니다.
83
+
84
+ - **생성된 훅.** 이 패키지의 `runHook`을 불러옵니다. 패키지를 갱신하면 실행할 구현도 바뀌지만
85
+ 훅 파일은 그대로입니다.
66
86
 
67
- 일반 사용자가 직접 불러올 필요는 없으며 별도 설정 네임스페이스도 없습니다.
87
+ 별도 설정 네임스페이스는 없습니다.
68
88
 
69
89
  <a id="limits"></a>
70
90
  ## 선언된 한계
@@ -72,7 +92,7 @@ Claude Code의 입력을 공통 형식으로 번역합니다. 에이전트와
72
92
  - **자식 프로세스의 쓰기는 관측 밖입니다.** 이 표면이 판정하는 것은 **선언된 도구
73
93
  호출**입니다. 프로세스를 띄우고 그 프로세스가 파일을 쓰는 명령은, 테스트 러너든 빌드든,
74
94
  명령에 대해 판정되지 자식이 한 일에 대해 판정되지 않습니다. 추적되는 파일에 대해서는
75
- 커밋 표면에서 결과를 별도로 관측할 수 있습니다.
95
+ 변경 집합 표면에서 결과를 별도로 관측할 수 있습니다.
76
96
  - **증거는 후상태를 계산할 수 있는 곳에만 있습니다.** 변형 도구 넷이 전부 증거를 내고
77
97
  노트북도 그렇습니다. `NotebookEdit`는 셀 단위 `modify` 증거를 냅니다. 증거가 없는 것은
78
98
  이 어댑터가 해소하지 못하는 페이로드입니다. 읽을 수 없거나 파싱되지 않는 노트북, 이름을
@@ -81,3 +101,11 @@ Claude Code의 입력을 공통 형식으로 번역합니다. 에이전트와
81
101
  경로가 언급됐는지 대조합니다.
82
102
  - **저장소 밖 조상은 범위 밖입니다.** 프로젝트 루트 위의 경로는 여기서 관측하지 않습니다.
83
103
  그 범위는 에이전트 실행 환경의 권한 정책으로 제한해야 합니다.
104
+ - **`polydeukes`를 찾지 못하면 행이 남지 않습니다.** 프로젝트에서 우산 패키지를 찾지 못하면
105
+ 스폰할 프로세스도, 기록할 로그 경로도 없습니다. 훅은 stderr에 한 줄을 남기고 종료 코드
106
+ `2`를 내며 텔레메트리 로그에는 아무것도 추가되지 않습니다. 스폰 전 실패 가운데 이 경우만
107
+ 그렇고, 나머지는 모두 `pdks`에 도달해 행을 남깁니다.
108
+ - **다른 호스트는 각자의 어댑터로 설치합니다.** Grok는 `@polydeukes/adapter-grok`로,
109
+ Codex는 `@polydeukes/adapter-codex`로 설치합니다. 각 패키지가 자기 위임자와 명부를
110
+ 씁니다. 한 프로젝트에 세션 어댑터를 둘 이상 설치하면 호출마다 판정기가 여러 번 실행될 수
111
+ 있습니다.
@@ -2,12 +2,11 @@
2
2
 
3
3
  **English** · [한국어](adapter-claude-code.ko.md)
4
4
 
5
- > **The session surface's translator** — PreToolUse payloads become the covenant input IR,
6
- > with the file-change evidence and the transcript channel the judge reads.
5
+ > **The Claude Code install unit** — PreToolUse payloads become the covenant input IR,
6
+ > with the file-change evidence and the transcript channel the judge reads, and the
7
+ > package installs the session surface into a project.
7
8
  >
8
- > Alpha. A transitive dependency of the umbrella: you do not install it and you do not
9
- > import it. The session surface reaches it through
10
- > [`polydeukes/claude-code`](polydeukes.md#session-export).
9
+ > Beta. Install it next to `polydeukes`, which it names as a `peerDependency`.
11
10
 
12
11
  <a id="ownership"></a>
13
12
  ## What this package owns
@@ -18,16 +17,23 @@ agent-neutrality a claim a test can check rather than a slogan.
18
17
 
19
18
  | Unit | What it does |
20
19
  |---|---|
20
+ | `pdks-claude-code` bin | One subcommand, `pdks-claude-code init`, which registers the session surface in a project |
21
+ | `runHook` | Translates one PreToolUse payload into the input IR and spawns the judge |
21
22
  | Payload up-translation | A raw PreToolUse payload becomes a `CovenantInput` |
22
23
  | Virtual post-state | Computes what a file *would* contain after an edit applies, without touching disk |
23
24
  | File-change evidence | Pairs the disk pre-state with the virtual post-state into union evidence |
24
25
  | Transcript provider | Turns a session JSONL file into a `CanonicalTranscript` |
25
- | Telemetry wiring | Drives the full funnel so exactly one row lands per call |
26
26
 
27
- This package never imports the covenant package. The dispatch seam is *injected* by the
28
- umbrella, which keeps dependencies one-way, through the core alone. It names
29
- `@polydeukes/core` as a `peerDependency`: the vocabulary is shared with the judge, not
30
- installed a second time here.
27
+ `runHook({ repoRoot })` is what the generated hook delegator imports. It builds the IR — the
28
+ `tools` and `session` evidence included — then spawns `pdks covenant check --enforce block` in
29
+ `repoRoot` and returns the child's exit code. The judging happens in that child process; this
30
+ package carries no judgment logic.
31
+
32
+ **This package writes no telemetry rows.** A failure before the spawn is sent to `pdks` on
33
+ stdin as a plain line, and `pdks` records the fail-closed row, so one call still leaves one
34
+ row. It never imports the judge: `polydeukes` and `@polydeukes/core` are both
35
+ `peerDependencies`, so the vocabulary and the judge are shared rather than installed a second
36
+ time here.
31
37
 
32
38
  <a id="translation"></a>
33
39
  ## Payload translation and the three axes
@@ -66,17 +72,29 @@ there is no separate precedent evaluator in this package. The grammar is in
66
72
  <a id="consumer-contract"></a>
67
73
  ## Where the consumer touches it
68
74
 
69
- - **The generated hook**, which loads this adapter through the umbrella's `claude-code`
70
- subpath. Upgrading the package upgrades what runs; the hook file itself never changes.
75
+ Two lines install the Claude Code session surface, run from the project root:
76
+
77
+ ```sh
78
+ npm install --save-dev polydeukes @polydeukes/core @polydeukes/adapter-claude-code
79
+ npx pdks-claude-code init
80
+ ```
81
+
82
+ `pdks-claude-code init` resolves `polydeukes` from the project, spawns `pdks init` for the
83
+ agent-neutral scaffold, then writes the four Claude Code artifacts non-destructively. A re-run
84
+ reports each existing artifact as skipped and overwrites nothing. The full artifact list is in
85
+ [`pdks init`](../cli/init.md#init-claude-code).
86
+
87
+ - **The generated hook** imports `runHook` from this package. Upgrading the package upgrades
88
+ what runs; the hook file itself never changes.
71
89
 
72
- No import, and no configuration namespace of its own.
90
+ No configuration namespace of its own.
73
91
 
74
92
  <a id="limits"></a>
75
93
  ## Declared limits
76
94
 
77
95
  - **A child process's writes are outside observation.** This surface judges *declared tool
78
96
  calls*. A command that spawns a process which then writes files — a test runner, a build
79
- — is judged on the command, not on what the child did. The commit surface is the second
97
+ — is judged on the command, not on what the child did. The change-set surface is the second
80
98
  observation that covers the same ground for tracked files.
81
99
  - **Evidence exists only where a post-state can be computed.** All four mutating tools
82
100
  contribute one, notebooks included — a `NotebookEdit` yields cell-level `modify` evidence.
@@ -86,3 +104,11 @@ No import, and no configuration namespace of its own.
86
104
  are compared for a mention rather than a proven target.
87
105
  - **Out-of-repository ancestors stay out of scope.** A path above the project root is not
88
106
  observed here; the agent's own deny policy owns that ground.
107
+ - **An unresolvable `polydeukes` leaves no row.** When the umbrella cannot be resolved from the
108
+ project there is no process to spawn and no log path to write to, so the hook exits `2` with
109
+ one line on stderr and the telemetry log gains nothing. Every other pre-spawn failure does
110
+ reach `pdks` and does leave a row.
111
+ - **Other hosts install through their own adapters.** Grok through
112
+ `@polydeukes/adapter-grok`, Codex through `@polydeukes/adapter-codex`. Each package writes
113
+ its own delegator and roster. Installing more than one session adapter in a project can run
114
+ the judge more than once per call.