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.
- package/README.ko.md +14 -28
- package/README.md +14 -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 +103 -0
- package/dist/covenant/discipline.js +667 -0
- package/dist/covenant/dispatch.d.ts +153 -0
- package/dist/covenant/dispatch.js +237 -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 +72 -39
- package/dist/covenant-check.js +260 -121
- package/dist/diff-ir.d.ts +28 -0
- package/dist/diff-ir.js +310 -0
- package/dist/docs/README.ko.md +8 -8
- package/dist/docs/README.md +8 -8
- package/dist/docs/catalog.json +44 -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 +39 -34
- package/dist/docs/how-to/connect-surfaces.md +36 -33
- package/dist/docs/how-to/write-disciplines.ko.md +61 -6
- package/dist/docs/how-to/write-disciplines.md +62 -6
- package/dist/docs/index.json +607 -373
- 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 +14 -9
- package/dist/docs/reference/cli/explain.md +15 -10
- package/dist/docs/reference/cli/init.ko.md +52 -52
- package/dist/docs/reference/cli/init.md +43 -55
- package/dist/docs/reference/configuration/index.ko.md +171 -66
- package/dist/docs/reference/configuration/index.md +178 -71
- package/dist/docs/reference/packages/adapter-claude-code.ko.md +38 -11
- package/dist/docs/reference/packages/adapter-claude-code.md +38 -14
- package/dist/docs/reference/packages/adapter-grok.ko.md +77 -0
- package/dist/docs/reference/packages/adapter-grok.md +79 -0
- package/dist/docs/reference/packages/core.ko.md +49 -3
- package/dist/docs/reference/packages/core.md +54 -5
- package/dist/docs/reference/packages/polydeukes.ko.md +138 -77
- package/dist/docs/reference/packages/polydeukes.md +146 -81
- package/dist/docs/reference/packages/sdk-ts.ko.md +161 -0
- package/dist/docs/reference/packages/sdk-ts.md +164 -0
- package/dist/docs/troubleshooting.ko.md +17 -15
- package/dist/docs/troubleshooting.md +18 -14
- package/dist/docs/tutorials/first-judgment.ko.md +2 -2
- package/dist/docs/tutorials/first-judgment.md +2 -2
- package/dist/explain.d.ts +1 -1
- package/dist/explain.js +37 -22
- package/dist/load-config.d.ts +1 -1
- package/dist/load-config.js +1 -1
- 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 +9 -3
- 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 +3 -17
- 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`
|
|
@@ -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
|
|
@@ -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
|
|
342
|
-
|
|
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
|
|
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
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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
|
|
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,
|
|
440
|
-
|
|
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
|
|
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
|
-
>
|
|
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/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
|
|
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.
|
|
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
|
-
|
|
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/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,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`에 도달해 행을 남깁니다.
|