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.
- package/README.ko.md +16 -28
- package/README.md +16 -28
- package/dist/baseline.d.ts +37 -1
- package/dist/baseline.js +68 -1
- package/dist/bin.d.ts +3 -4
- package/dist/bin.js +73 -90
- package/dist/covenant/bash-line.d.ts +130 -0
- package/dist/covenant/bash-line.js +566 -0
- package/dist/covenant/declaration-engine.d.ts +108 -0
- package/dist/covenant/declaration-engine.js +415 -0
- package/dist/covenant/discipline.d.ts +109 -0
- package/dist/covenant/discipline.js +667 -0
- package/dist/covenant/dispatch.d.ts +156 -0
- package/dist/covenant/dispatch.js +250 -0
- package/dist/covenant/extract-steps.d.ts +122 -0
- package/dist/covenant/extract-steps.js +0 -0
- package/dist/covenant/mention.d.ts +117 -0
- package/dist/covenant/mention.js +209 -0
- package/dist/covenant/module.d.ts +25 -0
- package/dist/covenant/module.js +23 -0
- package/dist/covenant/mutation-rules.d.ts +40 -0
- package/dist/covenant/mutation-rules.js +146 -0
- package/dist/covenant/relations.d.ts +46 -0
- package/dist/covenant/relations.js +68 -0
- package/dist/covenant/run-covenant.d.ts +95 -0
- package/dist/covenant/run-covenant.js +164 -0
- package/dist/covenant/self-mod.d.ts +55 -0
- package/dist/covenant/self-mod.js +84 -0
- package/dist/covenant/shell-evidence.d.ts +38 -0
- package/dist/covenant/shell-evidence.js +247 -0
- package/dist/covenant/shell-mod.d.ts +84 -0
- package/dist/covenant/shell-mod.js +213 -0
- package/dist/covenant/supply.d.ts +54 -0
- package/dist/covenant/supply.js +50 -0
- package/dist/covenant/transcript-mod.d.ts +60 -0
- package/dist/covenant/transcript-mod.js +254 -0
- package/dist/covenant/ttl-witness.d.ts +47 -0
- package/dist/covenant/ttl-witness.js +80 -0
- package/dist/covenant-check.d.ts +74 -41
- package/dist/covenant-check.js +372 -129
- package/dist/diff-ir.d.ts +28 -0
- package/dist/diff-ir.js +310 -0
- package/dist/docs/README.ko.md +10 -9
- package/dist/docs/README.md +10 -9
- package/dist/docs/catalog.json +76 -28
- package/dist/docs/concepts/judgment.ko.md +2 -2
- package/dist/docs/concepts/judgment.md +2 -2
- package/dist/docs/how-to/configure-project.ko.md +16 -16
- package/dist/docs/how-to/configure-project.md +15 -12
- package/dist/docs/how-to/connect-surfaces.ko.md +67 -34
- package/dist/docs/how-to/connect-surfaces.md +67 -34
- package/dist/docs/how-to/write-disciplines.ko.md +64 -7
- package/dist/docs/how-to/write-disciplines.md +65 -7
- package/dist/docs/index.json +744 -362
- package/dist/docs/reference/cli/covenant-check.ko.md +112 -65
- package/dist/docs/reference/cli/covenant-check.md +114 -59
- package/dist/docs/reference/cli/docs.ko.md +2 -2
- package/dist/docs/reference/cli/docs.md +2 -2
- package/dist/docs/reference/cli/explain.ko.md +20 -15
- package/dist/docs/reference/cli/explain.md +22 -15
- package/dist/docs/reference/cli/init.ko.md +91 -52
- package/dist/docs/reference/cli/init.md +81 -55
- package/dist/docs/reference/configuration/index.ko.md +182 -71
- package/dist/docs/reference/configuration/index.md +188 -76
- package/dist/docs/reference/packages/adapter-claude-code.ko.md +39 -11
- package/dist/docs/reference/packages/adapter-claude-code.md +40 -14
- package/dist/docs/reference/packages/adapter-codex.ko.md +99 -0
- package/dist/docs/reference/packages/adapter-codex.md +100 -0
- package/dist/docs/reference/packages/adapter-grok.ko.md +81 -0
- package/dist/docs/reference/packages/adapter-grok.md +83 -0
- package/dist/docs/reference/packages/core.ko.md +50 -4
- package/dist/docs/reference/packages/core.md +55 -6
- package/dist/docs/reference/packages/polydeukes.ko.md +139 -77
- package/dist/docs/reference/packages/polydeukes.md +147 -81
- package/dist/docs/reference/packages/sdk-ts.ko.md +164 -0
- package/dist/docs/reference/packages/sdk-ts.md +167 -0
- package/dist/docs/troubleshooting.ko.md +51 -19
- package/dist/docs/troubleshooting.md +53 -17
- package/dist/docs/tutorials/first-judgment.ko.md +6 -4
- package/dist/docs/tutorials/first-judgment.md +6 -4
- package/dist/explain.d.ts +1 -1
- package/dist/explain.js +37 -22
- package/dist/load-config.d.ts +25 -1
- package/dist/load-config.js +21 -3
- package/dist/pre-state-reader.d.ts +1 -1
- package/dist/pre-state-reader.js +1 -1
- package/dist/scaffold-project.d.ts +2 -2
- package/dist/scaffold-project.js +12 -4
- package/dist/schema/polydeukes.schema.json +12 -0
- package/dist/worktree-reader.d.ts +19 -0
- package/dist/worktree-reader.js +30 -0
- package/package.json +5 -18
- package/dist/claude-code-hook.d.ts +0 -79
- package/dist/claude-code-hook.js +0 -372
- package/dist/claude-code.d.ts +0 -6
- package/dist/claude-code.js +0 -6
- package/dist/covenant-module.d.ts +0 -25
- package/dist/covenant-module.js +0 -42
- package/dist/docs/reference/packages/adapter-git.ko.md +0 -101
- package/dist/docs/reference/packages/adapter-git.md +0 -108
- package/dist/docs/reference/packages/covenant.ko.md +0 -115
- package/dist/docs/reference/packages/covenant.md +0 -123
- package/dist/index.d.ts +0 -22
- package/dist/index.js +0 -21
- package/dist/init-claude-code.d.ts +0 -52
- package/dist/init-claude-code.js +0 -586
- package/dist/init-grok.d.ts +0 -51
- 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
|
-
|
|
60
|
-
|
|
61
|
-
protectedPaths:
|
|
62
|
-
- 'packages/core/src'
|
|
59
|
+
example:
|
|
60
|
+
someKey: 'a value the example adapter defines'
|
|
63
61
|
```
|
|
64
62
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
**
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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: '
|
|
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
|
-
|
|
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 `
|
|
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.
|
|
191
|
-
|
|
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
|
|
223
|
-
|
|
224
|
-
the
|
|
225
|
-
|
|
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
|
|
342
|
-
|
|
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
|
|
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
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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
|
|
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
|
|
439
|
-
`
|
|
440
|
-
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
|
|
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
|
-
>
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
>
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
|
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
|
|
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.
|