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
@@ -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`
@@ -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
@@ -312,7 +412,14 @@ declaration reads that source, cuts it into lines, keeps the lines a pattern mat
312
412
  requires the result to be `empty`. It scopes on `command` so that only shell calls are
313
413
  admitted: an Edit carries no command line, and a declaration reading a source its world
314
414
  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.
415
+ of a line; a pattern that would span a line boundary does not match. Heredoc bodies and
416
+ herestring words are not part of the source: bash passes them to the command as stdin
417
+ data rather than executing them, so a banned token quoted inside one is not a match, and
418
+ whether that data becomes a file write is judged by the shell-evidence path instead. Text
419
+ bash expands first stays in the source — a body under an unquoted delimiter that carries
420
+ `$` or a backtick, and a herestring word holding a substitution — as does a line the
421
+ tokenizer could not finish reading. What the receiving command does with its stdin is not
422
+ judged: a script fed to an interpreter is data to bash and a program to the interpreter.
316
423
 
317
424
  ```yaml
318
425
  - id: 'hooks-stay-armed'
@@ -338,8 +445,9 @@ declaration the user turns and tool calls as one snapshot, `toolUses` picks the
338
445
  `matches` finds the required one; `nonEmpty` is the verdict. A call the covenant blocked,
339
446
  one a human refused, and one that simply failed are not precedent. The pattern is matched
340
447
  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.
448
+ limit. `supply: { session: 'pass' }` is what disposes of an absent session on a host that proves
449
+ none; an entry reading the transcript is written in `sessionDisciplines`, so a change set never
450
+ compiles it at all.
343
451
 
344
452
  ```yaml
345
453
  - id: 'dependency-needs-npm-view'
@@ -411,16 +519,15 @@ Each observation is judged as one **world** with seven source names: `target.pat
411
519
  repo-relative path), `pre` and `post` (the file's text on the side the change carries —
412
520
  a creation has no `pre`, a deletion no `post`), `state` (`{ pre, post }`, present only
413
521
  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
522
+ session surface, the whole staged set on the change-set surface), and `command` (the shell
415
523
  call's command line — present on a shell call only, and a shell call that changes no file
416
524
  is one world of its own, so a declaration scoped on `command` sees it while one scoped on
417
525
  `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
526
+ declaration that reads `changes` is written in `changeSetDisciplines` and compiled only where
527
+ the whole change set is observed, because one call never carries the other half of a pair.
528
+ This repository's live config carries one `docs-stay-bilingual`, an `implies` over the
529
+ `.md`/`.ko.md` pair, advised on the change-set surface when one side is staged without the
530
+ other. A declaration that needs a
424
531
  file outside the target names it in a `sources` block, `sources: { en: { file:
425
532
  'locales/en.json' } }`, and reads it as `{ op: 'source', of: 'en' }`; the path is
426
533
  repo-relative (no leading `/`, no `..` segment) and the name may not be one of the seven. The
@@ -430,16 +537,16 @@ touches is read from the change's `post`, so both surfaces judge the same text.
430
537
  kind, `sources: { spawns: { sidecar: true } }`, names the session's spawn-record channel
431
538
  instead of a path — the subagent records the host keeps beside the transcript, supplied as
432
539
  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
540
+ `true`, and on the change-set surface (which has no session) the channel is always absent. A
434
541
  third kind, `sources: { session: { transcript: true } }`, names the session's own
435
542
  conversation history — the user turns and tool calls the surface reads, handed to the
436
543
  declaration as one snapshot whose entries carry their
437
544
  observation ordinal; the history steps (`toolUses`, `userTexts`, `first`, `ageMs`) read it, and
438
545
  `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
546
+ `tests-before-implementation`, an `ordered` over the ordinals of two subagent spawns, written
547
+ in `sessionDisciplines`. The seventh fixed name, `actor`, is the
441
548
  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' }`
549
+ where the surface proves none (the change-set surface) — read as `{ op: 'source', of: 'actor' }`
443
550
  followed by `select` on `agentType`; it derives the `actor` axis the `producer-owned` and
444
551
  `actor-scope` mechanisms require, and this repository's live config carries one of each
445
552
  (`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/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,10 @@ Claude Code의 입력을 공통 형식으로 번역합니다. 에이전트와
81
101
  경로가 언급됐는지 대조합니다.
82
102
  - **저장소 밖 조상은 범위 밖입니다.** 프로젝트 루트 위의 경로는 여기서 관측하지 않습니다.
83
103
  그 범위는 에이전트 실행 환경의 권한 정책으로 제한해야 합니다.
104
+ - **`polydeukes`를 찾지 못하면 행이 남지 않습니다.** 프로젝트에서 우산 패키지를 찾지 못하면
105
+ 스폰할 프로세스도, 기록할 로그 경로도 없습니다. 훅은 stderr에 한 줄을 남기고 종료 코드
106
+ `2`를 내며 텔레메트리 로그에는 아무것도 추가되지 않습니다. 스폰 전 실패 가운데 이 경우만
107
+ 그렇고, 나머지는 모두 `pdks`에 도달해 행을 남깁니다.
108
+ - **Grok는 `@polydeukes/adapter-grok`로 설치합니다.** 그 패키지가 자기 위임자와 명부를
109
+ 씁니다. 두 세션 어댑터를 한 프로젝트에 함께 설치하면 호출마다 판정기가 두 번 실행될 수
110
+ 있습니다.
@@ -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
+ > Alpha. 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/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,9 @@ 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
+ - **Grok installs through `@polydeukes/adapter-grok`.** That package writes its own delegator
112
+ and roster. Installing both session adapters in one project can run the judge twice per call.
@@ -0,0 +1,77 @@
1
+ # `@polydeukes/adapter-grok`
2
+
3
+ [English](adapter-grok.md) · **한국어**
4
+
5
+ > **Grok의 설치 단위**입니다. PreToolUse 페이로드가 약속(covenant) 입력 IR이 되고,
6
+ > 판정기가 읽는 파일 변경 증거가 함께 실립니다. 세션 표면을 프로젝트에 설치하는 것도 이
7
+ > 패키지가 합니다.
8
+ >
9
+ > 알파입니다. `polydeukes`와 함께 설치하며, `polydeukes`는 이 패키지의 `peerDependency`입니다.
10
+
11
+ <a id="ownership"></a>
12
+ ## 담당하는 기능
13
+
14
+ Grok의 입력을 공통 형식으로 번역합니다. 에이전트와 도구의 구체적인 이름은 이 패키지에서
15
+ 처리하고 코어에는 넣지 않습니다.
16
+
17
+ | 단위 | 하는 일 |
18
+ |---|---|
19
+ | `pdks-grok` 실행 파일 | 하위 명령 하나 `pdks-grok init`으로 프로젝트에 세션 표면을 등록합니다 |
20
+ | `runHook` | PreToolUse 페이로드 하나를 입력 IR로 바꾸고 판정기를 스폰합니다 |
21
+ | 페이로드 상향 번역 | 원본 PreToolUse 페이로드가 `CovenantInput`이 됩니다 |
22
+ | 예상 변경 후 상태 | 쓰기나 치환이 적용되면 파일이 무엇을 담을지 디스크를 건드리지 않고 계산합니다 |
23
+ | 파일 변경 증거 | 디스크의 변경 전 상태와 예상 변경 후 상태를 짝지어 변경 증거를 만듭니다 |
24
+
25
+ 생성된 훅 위임자가 불러오는 것이 `runHook({ repoRoot })`입니다. `tools` 명부를 실은 입력 IR을
26
+ 만든 뒤 — `session`과 `actor` 키는 없습니다 — `repoRoot`에서
27
+ `pdks covenant check --enforce block`을 스폰하고 그 자식 프로세스의 종료 코드를 그대로
28
+ 돌려줍니다. 판정은 그 자식 프로세스가 하며, 이 패키지에는 판정 코드가 없습니다.
29
+
30
+ **이 패키지는 텔레메트리 행을 쓰지 않습니다.** 스폰 전에 실패하면 그 사실을 한 줄로 만들어
31
+ `pdks`의 표준 입력으로 보내고, `pdks`가 fail-closed 행을 기록합니다. 호출 하나에 행 하나는
32
+ 그대로입니다. 판정기를 불러오지도 않습니다. `polydeukes`와 `@polydeukes/core` 모두
33
+ `peerDependencies`이므로 어휘와 판정기를 공유할 뿐 사본을 따로 설치하지 않습니다.
34
+
35
+ 이 어댑터가 IR에 실는 명부는 Grok 원어입니다. `write`와 `search_replace`는 파일을 바꾸고,
36
+ `run_terminal_command`는 셸 한 줄을 실습니다.
37
+
38
+ <a id="consumer-contract"></a>
39
+ ## 소비자가 닿는 곳
40
+
41
+ 프로젝트 루트에서 두 줄이면 Grok 세션 표면이 설치됩니다.
42
+
43
+ ```sh
44
+ npm install --save-dev polydeukes @polydeukes/adapter-grok
45
+ npx pdks-grok init
46
+ ```
47
+
48
+ `pdks-grok init`은 프로젝트에서 `polydeukes`를 찾고, 에이전트와 무관한 초기 파일을 위해
49
+ `pdks init`을 스폰한 뒤, Grok 산출물 둘을 덮어쓰지 않고 씁니다. 다시 실행하면 이미 있는
50
+ 산출물은 `skipped`로 보고하고 내용을 바꾸지 않습니다. 산출물 목록은
51
+ [`pdks init`](../cli/init.ko.md#init-grok)에 있습니다.
52
+
53
+ 이 어댑터와 `@polydeukes/adapter-claude-code`를 한 프로젝트에 함께 설치하면 호출마다
54
+ 판정기가 두 번 실행될 수 있습니다.
55
+
56
+ - **생성된 훅**은 이 패키지의 `runHook`을 불러옵니다. 패키지를 올리면 실행되는 코드가
57
+ 올라가고, 훅 파일 자체는 바뀌지 않습니다.
58
+
59
+ 자체 설정 네임스페이스는 없습니다.
60
+
61
+ <a id="limits"></a>
62
+ ## 선언된 한계
63
+
64
+ - **자식 프로세스의 쓰기는 관측 밖입니다.** 이 표면은 *선언된 도구 호출*을 판정합니다. 파일을
65
+ 쓰는 프로세스를 띄우는 명령은 그 명령 줄로 판정되며, 자식이 한 일로 판정되지 않습니다.
66
+ - **증거는 변경 후 상태를 계산할 수 있을 때만 있습니다.** `write`와 `search_replace`가 하나를
67
+ 만듭니다. 치환이 한 번도 맞지 않거나, `replace_all` 없이 두 번 이상 맞으면 증거가 없습니다.
68
+ 호스트 도구가 그 호출을 거부하기 때문입니다.
69
+ - **증거 없는 호출은 보수적으로 판정합니다.** 증명된 대상이 없으므로 호출 인자에 보호 경로가
70
+ 언급됐는지 대조합니다.
71
+ - **대화 기록(transcript) 통로가 없습니다.** IR은 `session`과 `actor`를 생략합니다. Grok의
72
+ ACP 대화 기록은 세션 증인(witness) 밸브가 필요로 하는 인간 메시지 증거를 공급하지 않습니다.
73
+ 의도한 편집이 차단되면 본인 터미널에서 수행하세요.
74
+ - **`polydeukes`를 찾지 못하면 행이 남지 않습니다.** 프로젝트에서 우산 패키지를 찾지 못하면
75
+ 스폰할 프로세스도, 기록할 로그 경로도 없습니다. 훅은 stderr에 한 줄을 남기고 종료 코드
76
+ `2`를 내며 텔레메트리 로그에는 아무것도 추가되지 않습니다. 스폰 전 실패 가운데 이 경우만
77
+ 그렇고, 나머지는 모두 `pdks`에 도달해 행을 남깁니다.