polydeukes 0.5.0 → 0.6.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 +54 -74
- package/README.md +55 -86
- package/dist/baseline.d.ts +82 -0
- package/dist/baseline.js +166 -0
- package/dist/bin.d.ts +1 -1
- package/dist/bin.js +26 -6
- package/dist/claude-code-hook.d.ts +7 -5
- package/dist/claude-code-hook.js +69 -17
- package/dist/claude-code.d.ts +6 -0
- package/dist/claude-code.js +6 -0
- package/dist/covenant-check.d.ts +10 -17
- package/dist/covenant-check.js +44 -12
- package/dist/covenant-module.d.ts +2 -2
- package/dist/covenant-module.js +9 -1
- package/dist/docs/README.ko.md +60 -0
- package/dist/docs/README.md +64 -0
- package/dist/docs/catalog.json +464 -0
- package/dist/docs/concepts/judgment.ko.md +113 -0
- package/dist/docs/concepts/judgment.md +113 -0
- package/dist/docs/how-to/configure-project.ko.md +99 -0
- package/dist/docs/how-to/configure-project.md +95 -0
- package/dist/docs/how-to/connect-surfaces.ko.md +115 -0
- package/dist/docs/how-to/connect-surfaces.md +118 -0
- package/dist/docs/how-to/write-disciplines.ko.md +124 -0
- package/dist/docs/how-to/write-disciplines.md +125 -0
- package/dist/docs/index.json +2046 -0
- package/dist/docs/reference/cli/covenant-check.ko.md +101 -0
- package/dist/docs/reference/cli/covenant-check.md +98 -0
- package/dist/docs/reference/cli/docs.ko.md +97 -0
- package/dist/docs/reference/cli/docs.md +95 -0
- package/dist/docs/reference/cli/explain.ko.md +79 -0
- package/dist/docs/reference/cli/explain.md +84 -0
- package/dist/docs/reference/cli/init.ko.md +119 -0
- package/dist/docs/reference/cli/init.md +131 -0
- package/dist/docs/reference/configuration/index.ko.md +448 -0
- package/dist/docs/reference/configuration/index.md +474 -0
- package/dist/docs/reference/packages/adapter-claude-code.ko.md +83 -0
- package/dist/docs/reference/{adapter-claude-code.md → packages/adapter-claude-code.md} +16 -10
- package/dist/docs/reference/packages/adapter-git.ko.md +101 -0
- package/dist/docs/reference/{adapter-git.md → packages/adapter-git.md} +25 -15
- package/dist/docs/reference/packages/core.ko.md +128 -0
- package/dist/docs/reference/{core.md → packages/core.md} +32 -13
- package/dist/docs/reference/packages/covenant.ko.md +115 -0
- package/dist/docs/reference/{covenant.md → packages/covenant.md} +41 -26
- package/dist/docs/reference/packages/polydeukes.ko.md +134 -0
- package/dist/docs/reference/packages/polydeukes.md +139 -0
- package/dist/docs/troubleshooting.ko.md +142 -0
- package/dist/docs/troubleshooting.md +97 -122
- package/dist/docs/tutorials/first-judgment.ko.md +82 -0
- package/dist/docs/tutorials/first-judgment.md +81 -0
- package/dist/docs-catalog.d.ts +25 -0
- package/dist/docs-catalog.js +450 -0
- package/dist/docs-library.d.ts +23 -0
- package/dist/docs-library.js +347 -0
- package/dist/docs-markdown.d.ts +32 -0
- package/dist/docs-markdown.js +150 -0
- package/dist/docs-query.d.ts +11 -40
- package/dist/docs-query.js +28 -122
- package/dist/docs-types.d.ts +105 -0
- package/dist/docs-types.js +2 -0
- package/dist/explain.d.ts +3 -5
- package/dist/explain.js +48 -47
- package/dist/index.d.ts +2 -3
- package/dist/index.js +1 -2
- package/dist/init-claude-code.d.ts +5 -3
- package/dist/init-claude-code.js +226 -63
- package/dist/init-grok.d.ts +51 -0
- package/dist/init-grok.js +242 -0
- package/dist/load-config.d.ts +5 -1
- package/dist/load-config.js +2 -1
- package/dist/pre-state-reader.d.ts +22 -0
- package/dist/pre-state-reader.js +32 -0
- package/dist/scaffold-project.js +48 -8
- package/dist/schema/polydeukes.schema.json +38 -91
- package/package.json +7 -7
- package/dist/docs/configuration.md +0 -103
- package/dist/docs/installation.md +0 -212
- package/dist/docs/reference/configuration.md +0 -338
- package/dist/docs/reference/polydeukes.md +0 -287
package/dist/init-claude-code.js
CHANGED
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Nothing existing is overwritten. The settings file in particular is merged, never
|
|
16
16
|
* replaced: a consumer's other PreToolUse registrations and permissions are live
|
|
17
|
-
* configuration, and replacing them would disarm every other tool they wired.
|
|
17
|
+
* configuration, and replacing them would disarm every other tool they wired. A grok JSON
|
|
18
|
+
* whose command still names the grok mjs is rewritten to this hook's command so the two
|
|
19
|
+
* installers do not leave two spawn strings.
|
|
18
20
|
*/
|
|
19
21
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
20
22
|
import { findPackageJSON } from 'node:module';
|
|
@@ -22,6 +24,7 @@ import { dirname, join } from 'node:path';
|
|
|
22
24
|
import { MUTATING_TOOLS, SHELL_TOOLS } from '@polydeukes/adapter-claude-code';
|
|
23
25
|
import { isPlainObject } from '@polydeukes/core';
|
|
24
26
|
import { TOPICS } from './docs-query.js';
|
|
27
|
+
import { retargetGrokHookCommandToClaude } from './init-grok.js';
|
|
25
28
|
import { CONFIG_FILENAMES } from './load-config.js';
|
|
26
29
|
import { scaffoldProject } from './scaffold-project.js';
|
|
27
30
|
/** The published entry point the generated hook loads the judge through. */
|
|
@@ -110,7 +113,9 @@ This project is judged by Polydeukes, and the matching documentation ships insid
|
|
|
110
113
|
installed package. \`pdks docs\` answers offline, from the same version that does the
|
|
111
114
|
judging; a web search answers from whichever release it indexed.
|
|
112
115
|
|
|
113
|
-
Run \`pdks docs\` for the topic list, \`pdks docs <topic>\` for
|
|
116
|
+
Run \`pdks docs\` for the topic list, \`pdks docs <topic>\` for topic content,
|
|
117
|
+
\`pdks docs search "locale key pairing"\` to find a section, or
|
|
118
|
+
\`pdks docs show write-disciplines\` to retrieve the guide. Add \`--lang ko\` for Korean.
|
|
114
119
|
|
|
115
120
|
A local install puts the bin in \`node_modules/.bin\`, which a plain shell does not have on
|
|
116
121
|
PATH. If \`pdks\` is not found, run \`./node_modules/.bin/pdks docs <topic>\` — or your package
|
|
@@ -131,7 +136,7 @@ ${TOPICS.map((topic) => `| ${DOCS_TOPIC_PURPOSE[topic]} | \`pdks docs ${topic}\`
|
|
|
131
136
|
*/
|
|
132
137
|
export const GENERATED_SKILL = `---
|
|
133
138
|
name: discipline-draft
|
|
134
|
-
description: Turn a described discipline problem into a registered entry in polydeukes.config — a judged entry when the
|
|
139
|
+
description: Turn a described discipline problem into a registered entry in polydeukes.config — a judged entry when the declaration grammar and observed evidence can express it, a draft entry otherwise. Use when the user describes a recurring problem they want promised away ("I keep...", "stop X from happening", "we should never...", "how do I enforce Y").
|
|
135
140
|
---
|
|
136
141
|
|
|
137
142
|
# discipline-draft — from a problem description to a registered discipline
|
|
@@ -151,33 +156,67 @@ promises and classify each separately.
|
|
|
151
156
|
|
|
152
157
|
### 2. Classify the shape
|
|
153
158
|
|
|
154
|
-
|
|
159
|
+
Choose from the current catalogue, then check whether the intended surface can supply the
|
|
160
|
+
required evidence. A mechanism name constrains the declaration; it does not implement the
|
|
161
|
+
promise by itself. The extracted axes and body relations must be subsets of the admitted
|
|
162
|
+
sets below. Scope filtering is separate from the extracted axes.
|
|
155
163
|
|
|
156
|
-
|
|
|
164
|
+
| Mechanism | Admitted axes | Body relations | Evidence or structural condition |
|
|
157
165
|
| --- | --- | --- | --- |
|
|
158
|
-
|
|
|
159
|
-
|
|
|
160
|
-
|
|
|
161
|
-
|
|
|
162
|
-
|
|
|
166
|
+
| \`pairing\` | \`world\` | \`equal\` | Compare supplied files or channels; extract keys when values may differ. |
|
|
167
|
+
| \`companion\` | \`change\`, \`world\` | \`implies\` | Compare presence by key; a multi-file promise needs the observed change set. |
|
|
168
|
+
| \`monotonic-order\` | \`change\`, \`world\` | \`ordered\` | Extract a sequence with an explicit comparison field; order is not presence. |
|
|
169
|
+
| \`fingerprint-sync\` | \`world\` | \`equal\` | Compare supplied stamps; no generator or compiler runs during judgment. |
|
|
170
|
+
| \`producer-owned\` | \`actor\` | \`empty\`, \`nonEmpty\` | Requires host-provided actor evidence, not an artifact's self-reported producer. |
|
|
171
|
+
| \`self-absolution-ban\` | \`change\` | \`unchanged\`, \`empty\` | Extract protected fields or path changes; choose creation/deletion supply explicitly. |
|
|
172
|
+
| \`actor-scope\` | \`actor\` | \`empty\`, \`nonEmpty\` | Requires a proven actor; a missing actor is not proof of the main session. |
|
|
173
|
+
| \`precedent\` | \`history\`, \`world\` | \`nonEmpty\` | Requires an observed earlier call in a transcript or supplied channel. |
|
|
174
|
+
| \`phase-order\` | \`history\` | \`ordered\` | Compare observed call ordinals; missing phases need a separate presence promise. |
|
|
175
|
+
| \`turn-locality\` | \`history\` | \`nonEmpty\` | Requires observed turns and time or ordinal boundaries. |
|
|
176
|
+
| \`stated-ground\` | \`history\` | \`nonEmpty\` | Can require recorded text, not establish whether its reasoning is sound. |
|
|
177
|
+
| \`controlled-vocabulary\` | \`change\`, \`world\` | \`subset\` | Extract values and an explicit allowed set. |
|
|
178
|
+
| \`naming\` | \`change\` | \`empty\`, \`nonEmpty\` | Scope must read \`target.path\`; match the intended name pattern. |
|
|
179
|
+
| \`added-only\` | \`change\` | \`empty\` | Compare pre/post extractions and judge only newly added matches. |
|
|
180
|
+
| \`one-way-marker\` | \`change\` | \`subset\` | Existing markers must remain in the extracted post-change set. |
|
|
181
|
+
| \`delegated-scope\` | — | — | Reserved for a definition-time evaluator; not accepted in current declarations. |
|
|
182
|
+
| \`scoped-valve\` | \`change\`, \`actor\`, \`world\`, \`history\` | \`empty\`, \`nonEmpty\`, \`equal\`, \`subset\`, \`implies\`, \`ordered\`, \`unchanged\` | Requires a \`witness\` block expressing the exception condition. |
|
|
183
|
+
| \`forbidden-command\` | \`change\` | \`empty\` | Scope must read \`command\`; a text pattern is not shell semantic analysis. |
|
|
184
|
+
|
|
185
|
+
These four requests illustrate the classification boundary:
|
|
186
|
+
|
|
187
|
+
| Request | Classification | Proof |
|
|
188
|
+
| --- | --- | --- |
|
|
189
|
+
| The English and Korean locale files must carry identical keys. | \`pairing\`, with two supplied files. | An unmatched key breaks; translated values may differ. |
|
|
190
|
+
| Every status must belong to an allowed list. | \`controlled-vocabulary\`, with a supplied allowed set. | An unknown status breaks; an allowed status passes. |
|
|
191
|
+
| A successful package lookup must precede a manifest edit. | \`precedent\`, with observed session history. | Failed or absent lookups break; an unavailable transcript is a supply case. |
|
|
192
|
+
| A fresh benchmark must execute during judgment to prove a performance claim. | \`draft\`: the engine does not execute benchmarks. | Comparing an existing report would be a different promise. |
|
|
193
|
+
|
|
194
|
+
Run \`pdks docs show write-disciplines\` for the key-pairing walkthrough and
|
|
195
|
+
\`pdks docs show configuration --section disciplines\` for the declaration grammar.
|
|
196
|
+
Use \`--lang ko\` for Korean; these commands read the installed version offline.
|
|
163
197
|
|
|
164
|
-
|
|
165
|
-
That is usually what you want: a discipline adopted today should not indict
|
|
198
|
+
An \`added-only\` declaration forgives existing occurrences — only what the edit adds breaks
|
|
199
|
+
the promise. That is usually what you want: a discipline adopted today should not indict
|
|
200
|
+
yesterday's code.
|
|
166
201
|
|
|
167
|
-
|
|
168
|
-
belongs in the top-level \`protectedPaths:\` list — its own config block, never an entry
|
|
169
|
-
key. And a path that must never be CREATED is not expressible today: \`immutable\` allows
|
|
170
|
-
creation by design, so register that promise as a draft (step 4b).
|
|
202
|
+
One path-shaped promise takes no \`disciplines:\` entry at all: a path nobody may touch
|
|
203
|
+
belongs in the top-level \`protectedPaths:\` list — its own config block, never an entry key.
|
|
171
204
|
|
|
172
205
|
### 3. Check the observation boundary
|
|
173
206
|
|
|
174
|
-
|
|
207
|
+
Do not confuse an expressible relation with available evidence:
|
|
175
208
|
|
|
176
|
-
- **
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
209
|
+
- **Files outside the repository** — file-change protection observes the project root.
|
|
210
|
+
Use the host's permission policy for comprehensive protection outside it. A command-text
|
|
211
|
+
pattern may recognize a particular string, but does not observe all resulting writes.
|
|
212
|
+
- **Writes by child processes** — arbitrary writes inside a test runner or script are not
|
|
213
|
+
individually observed by the session surface. A commit comparison can observe the resulting
|
|
214
|
+
files when they enter its selected diff; it does not recover the originating tool history.
|
|
215
|
+
- **Missing history or actor channels** — choose the declaration's supply policy explicitly.
|
|
216
|
+
Commit observations have no session transcript; \`supply: pass\` records a skip, not success.
|
|
217
|
+
- **Fresh execution or semantic proof** — the engine compares supplied evidence. It does not
|
|
218
|
+
run a new benchmark or prove that a written explanation is true. Preserve that unmet promise
|
|
219
|
+
as a draft rather than silently replacing it with a weaker text check.
|
|
181
220
|
|
|
182
221
|
### 4a. Expressible now — register a judged entry
|
|
183
222
|
|
|
@@ -198,24 +237,156 @@ languages:
|
|
|
198
237
|
disciplines:
|
|
199
238
|
- id: 'no-focused-tests'
|
|
200
239
|
why: 'a committed .only silently shrinks the suite to one test'
|
|
201
|
-
|
|
240
|
+
declare:
|
|
241
|
+
mechanism: 'added-only'
|
|
242
|
+
scope: { source: 'target.path', include: ['^src/'] }
|
|
243
|
+
supply: { pre: 'empty', post: 'empty' }
|
|
244
|
+
extract:
|
|
245
|
+
before:
|
|
246
|
+
- { op: 'source', of: 'pre' }
|
|
247
|
+
- { op: 'lines' }
|
|
248
|
+
- { op: 'keyByPattern', re: '(\\.only\\()' }
|
|
249
|
+
after:
|
|
250
|
+
- { op: 'source', of: 'post' }
|
|
251
|
+
- { op: 'lines' }
|
|
252
|
+
- { op: 'keyByPattern', re: '(\\.only\\()' }
|
|
253
|
+
added:
|
|
254
|
+
- { op: 'onlyIn', of: 'after', notIn: 'before' }
|
|
255
|
+
relate:
|
|
256
|
+
- id: 'nothing-added'
|
|
257
|
+
relation: { op: 'empty', of: 'added' }
|
|
258
|
+
message: 'adds {key}: {value}'
|
|
259
|
+
enforce: advise
|
|
260
|
+
\`\`\`
|
|
261
|
+
|
|
262
|
+
A command-line ban reads the fixed source \`command\` and scopes on it — the scope is part of
|
|
263
|
+
the mechanism's shape, so a \`forbidden-command\` entry without it is refused at load time:
|
|
264
|
+
|
|
265
|
+
\`\`\`yaml
|
|
266
|
+
languages:
|
|
267
|
+
placeholder:
|
|
268
|
+
productionGlob: 'src/**'
|
|
269
|
+
testCmd: 'echo "set a verification command for {scope}"'
|
|
270
|
+
disciplines:
|
|
271
|
+
- id: 'no-force-push'
|
|
272
|
+
why: 'a force push rewrites history nobody reviewed'
|
|
273
|
+
declare:
|
|
274
|
+
mechanism: 'forbidden-command'
|
|
275
|
+
scope: { source: 'command' }
|
|
276
|
+
extract:
|
|
277
|
+
hits:
|
|
278
|
+
- { op: 'source', of: 'command' }
|
|
279
|
+
- { op: 'lines' }
|
|
280
|
+
- { op: 'matches', re: 'git push\\b.*--force(?![\\w-])' }
|
|
281
|
+
relate:
|
|
282
|
+
- { id: 'no-force', relation: { op: 'empty', of: 'hits' }, message: '{value}' }
|
|
283
|
+
enforce: advise
|
|
284
|
+
\`\`\`
|
|
285
|
+
|
|
286
|
+
The following examples implement the first three classification cases. Both locale files and
|
|
287
|
+
the allowed-status file must exist and contain valid JSON. File bindings use the proposed
|
|
288
|
+
contents for a file changed by the current observation, not a second stale disk read.
|
|
289
|
+
|
|
290
|
+
\`\`\`yaml
|
|
291
|
+
languages:
|
|
292
|
+
json:
|
|
293
|
+
productionGlob: 'locales/**/*.json'
|
|
294
|
+
testCmd: 'pnpm test'
|
|
295
|
+
disciplines:
|
|
296
|
+
- id: 'locale-key-parity'
|
|
297
|
+
why: 'the ko and en locales must carry the same keys'
|
|
298
|
+
declare:
|
|
299
|
+
mechanism: 'pairing'
|
|
300
|
+
scope: { source: 'target.path', include: ['^locales/(ko|en)[.]json$'] }
|
|
301
|
+
sources:
|
|
302
|
+
ko: { file: 'locales/ko.json' }
|
|
303
|
+
en: { file: 'locales/en.json' }
|
|
304
|
+
supply: { ko: 'error', en: 'error' }
|
|
305
|
+
extract:
|
|
306
|
+
koKeys: [{ op: 'source', of: 'ko' }, { op: 'json' }, { op: 'flattenKeys' }]
|
|
307
|
+
enKeys: [{ op: 'source', of: 'en' }, { op: 'json' }, { op: 'flattenKeys' }]
|
|
308
|
+
relate:
|
|
309
|
+
- id: 'parity'
|
|
310
|
+
relation: { op: 'equal', of: ['koKeys', 'enKeys'] }
|
|
311
|
+
messageBySide:
|
|
312
|
+
left: '{key} is in ko only'
|
|
313
|
+
right: '{key} is in en only'
|
|
202
314
|
enforce: advise
|
|
203
315
|
\`\`\`
|
|
204
316
|
|
|
317
|
+
\`\`\`yaml
|
|
318
|
+
languages:
|
|
319
|
+
json:
|
|
320
|
+
productionGlob: '*.json'
|
|
321
|
+
testCmd: 'pnpm test'
|
|
322
|
+
disciplines:
|
|
323
|
+
- id: 'status-vocabulary'
|
|
324
|
+
why: 'statuses.json may contain only values listed in allowed-statuses.json'
|
|
325
|
+
declare:
|
|
326
|
+
mechanism: 'controlled-vocabulary'
|
|
327
|
+
scope: { source: 'target.path', include: ['^statuses[.]json$'] }
|
|
328
|
+
sources: { allowed: { file: 'allowed-statuses.json' } }
|
|
329
|
+
supply: { post: 'error', allowed: 'error' }
|
|
330
|
+
extract:
|
|
331
|
+
selected: [{ op: 'source', of: 'post' }, { op: 'json' }, { op: 'items' }]
|
|
332
|
+
permitted: [{ op: 'source', of: 'allowed' }, { op: 'json' }, { op: 'items' }]
|
|
333
|
+
relate:
|
|
334
|
+
- id: 'allowed-status'
|
|
335
|
+
relation: { op: 'subset', of: 'selected', in: 'permitted' }
|
|
336
|
+
message: 'unknown status: {value}'
|
|
337
|
+
enforce: advise
|
|
338
|
+
\`\`\`
|
|
339
|
+
|
|
340
|
+
Here both status files are JSON arrays of strings. This declaration scopes on statuses.json;
|
|
341
|
+
editing only the allowed list does not trigger it. Broaden the observation deliberately if
|
|
342
|
+
changes to that list must recheck all dependent files.
|
|
343
|
+
|
|
344
|
+
\`\`\`yaml
|
|
345
|
+
languages:
|
|
346
|
+
typescript:
|
|
347
|
+
productionGlob: 'src/**'
|
|
348
|
+
testCmd: 'pnpm test'
|
|
349
|
+
disciplines:
|
|
350
|
+
- id: 'manifest-needs-npm-view'
|
|
351
|
+
why: 'a successful package lookup must precede a manifest edit'
|
|
352
|
+
declare:
|
|
353
|
+
mechanism: 'precedent'
|
|
354
|
+
scope: { source: 'target.path', include: ['^(packages/[^/]+/)?package[.]json$'] }
|
|
355
|
+
sources: { session: { transcript: true } }
|
|
356
|
+
supply: { session: 'pass' }
|
|
357
|
+
extract:
|
|
358
|
+
npmView:
|
|
359
|
+
- { op: 'source', of: 'session' }
|
|
360
|
+
- { op: 'toolUses', names: ['Bash'] }
|
|
361
|
+
- { op: 'filter', when: [{ field: 'succeeded', eq: true }] }
|
|
362
|
+
- { op: 'select', path: 'args.command' }
|
|
363
|
+
- { op: 'matches', re: '^npm view ' }
|
|
364
|
+
relate:
|
|
365
|
+
- id: 'npm-view'
|
|
366
|
+
relation: { op: 'nonEmpty', of: 'npmView' }
|
|
367
|
+
message: 'no successful npm view precedes this edit'
|
|
368
|
+
enforce: advise
|
|
369
|
+
\`\`\`
|
|
370
|
+
|
|
371
|
+
The precedent example proves only that an observed successful Bash call starts with npm view;
|
|
372
|
+
it does not prove that the lookup concerns the dependency being edited. The commit surface has
|
|
373
|
+
no transcript and therefore skips this example by its explicit supply policy.
|
|
374
|
+
|
|
205
375
|
**Write the regex yourself — the user states the promise, you author the pattern.** The
|
|
206
376
|
pattern is the part users find hardest, so never hand the prose back and ask for one. Three
|
|
207
377
|
authoring traps, each measured on a live config:
|
|
208
378
|
|
|
209
379
|
- **A pattern answers a syntactic question only.** "Is this string a forbidden word" is
|
|
210
380
|
syntax; "is this a new dependency version" is meaning, and a regex leaks both ways on a
|
|
211
|
-
semantic question. When the question is semantic, narrow
|
|
212
|
-
match IS a break
|
|
213
|
-
|
|
214
|
-
- **\`^\`
|
|
215
|
-
|
|
216
|
-
|
|
381
|
+
semantic question. When the question is semantic, narrow the declaration's own \`scope\`
|
|
382
|
+
block to the files where any match IS a break, or accept "editing this file at all" as
|
|
383
|
+
the trigger.
|
|
384
|
+
- **\`^\` means what the preceding step left.** After a \`lines\` step a declaration's
|
|
385
|
+
pattern sees one line at a time, so \`^\` anchors to that line; over an unsplit source it
|
|
386
|
+
anchors to the whole text and matches the first line only. A ban over the command line
|
|
387
|
+
puts \`lines\` before its \`matches\` for exactly that reason.
|
|
217
388
|
- **Author both directions.** Before registering, write down one string the pattern must
|
|
218
|
-
match and one nearby string it must not (\`
|
|
389
|
+
match and one nearby string it must not (\`only(\` vs \`only_helper(\`, a flag vs its
|
|
219
390
|
substring). A pattern checked in only the breaking direction over-fires in review-proof
|
|
220
391
|
ways.
|
|
221
392
|
|
|
@@ -223,27 +394,10 @@ authoring traps, each measured on a live config:
|
|
|
223
394
|
|
|
224
395
|
A draft is prose with a handle: \`id\`, \`why\`, and the literal marker \`draft: true\` — no other
|
|
225
396
|
keys. It produces no judgment and no telemetry; \`pdks explain\` lists it as unpromoted.
|
|
226
|
-
Record the
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
| --- | --- |
|
|
231
|
-
| pairing | every element of set A has a counterpart in set B (translation keys, i18n) |
|
|
232
|
-
| companion | if X appears in a unit, Y must appear with it |
|
|
233
|
-
| ordered | a sequence must keep its order (migration journals, version ladders) |
|
|
234
|
-
| fingerprint | a derived artifact must match the hash/stamp of its source |
|
|
235
|
-
| producer-owned | only a designated generator may write this artifact |
|
|
236
|
-
| self-absolution | the party being judged must not write its own verdict field |
|
|
237
|
-
| actor-scope | the same action is fine for one actor and a break for another |
|
|
238
|
-
| phase-order | several precedents, in a fixed order |
|
|
239
|
-
| turn-locality | the evidence must be in the same turn or time window |
|
|
240
|
-
| stated-ground | the reason must be written down before the action |
|
|
241
|
-
| controlled-vocabulary | only an enumerated set of words/values is allowed |
|
|
242
|
-
| naming-convention | names must match a pattern per kind |
|
|
243
|
-
| irreversible-marker | once present, a marker may never be removed |
|
|
244
|
-
| delegation-scope | a delegated task may touch only its granted scope |
|
|
245
|
-
| scope-valve | a defined exception valve, judged rather than ad hoc |
|
|
246
|
-
| claim-verification | the claim must be re-run/measured, not trusted |
|
|
397
|
+
Record the intended promise and the exact missing capability inside \`why\`. Do not classify
|
|
398
|
+
pairing, vocabulary, or history promises as drafts merely because they are absent from a short
|
|
399
|
+
example list. Check the catalogue, extraction steps, and observation channel first. A reserved
|
|
400
|
+
\`delegated-scope\` declaration cannot be registered as a judged entry.
|
|
247
401
|
|
|
248
402
|
\`\`\`yaml
|
|
249
403
|
languages:
|
|
@@ -251,30 +405,38 @@ languages:
|
|
|
251
405
|
productionGlob: 'src/**'
|
|
252
406
|
testCmd: 'echo "set a verification command for {scope}"'
|
|
253
407
|
disciplines:
|
|
254
|
-
- id: '
|
|
255
|
-
why: '
|
|
408
|
+
- id: 'benchmark-supports-performance-claim'
|
|
409
|
+
why: 'a performance claim needs a fresh benchmark run during judgment; the engine cannot execute it'
|
|
256
410
|
draft: true
|
|
257
411
|
\`\`\`
|
|
258
412
|
|
|
259
413
|
### 5. Prove it fires, then close
|
|
260
414
|
|
|
261
|
-
Run \`pdks explain\` and confirm the new entry is listed (a judged entry with its
|
|
262
|
-
surfaces; a draft as unpromoted).
|
|
415
|
+
Run \`pdks explain\` and confirm the new entry is listed (a judged entry with its mechanism
|
|
416
|
+
and surfaces; a draft as unpromoted).
|
|
263
417
|
|
|
264
418
|
For a judged entry, registration is not the finish — a pattern that never fires protects
|
|
265
|
-
nothing while looking installed. Fire it once for real, with the proof run
|
|
266
|
-
actually reach:
|
|
419
|
+
nothing while looking installed. Fire it once for real, with the proof run the declaration's
|
|
420
|
+
own mechanism can actually reach:
|
|
267
421
|
|
|
268
|
-
|
|
|
422
|
+
| Mechanism | Break it once | The entry's id shows up in |
|
|
269
423
|
| --- | --- | --- |
|
|
270
|
-
|
|
|
271
|
-
| \`
|
|
272
|
-
| \`
|
|
424
|
+
| a file-reading one (\`added-only\`, \`naming\`, …) | one scratch edit matching the must-match direction | \`pdks covenant check --worktree\` output — the exit stays 0 at advise, the id is the proof |
|
|
425
|
+
| \`forbidden-command\` | run one harmless command matching the pattern | the telemetry log tail — at advise the call proceeds and its row records the id |
|
|
426
|
+
| \`precedent\` | one in-scope edit made without the required precedent | the telemetry log tail — a declaration reading the session judges on the session surface only (the commit surface has none, so its \`supply\` policy records it \`skipped\`) |
|
|
273
427
|
|
|
274
|
-
Then undo the scratch break, repeat the same
|
|
275
|
-
must-NOT-match
|
|
428
|
+
Then undo the scratch break, repeat the same observation, and confirm a passing row for the
|
|
429
|
+
must-NOT-match case. Silence alone may mean a scope miss, unchanged files, or unavailable evidence;
|
|
430
|
+
check \`pdks explain\` and telemetry for \`config-fault\`, \`no-observation\`, or \`supply-pass\`. Close by telling the user which rung the entry landed on and
|
|
276
431
|
that \`enforce: block\` is theirs to add later if the advise record earns it.
|
|
277
432
|
|
|
433
|
+
## Updating this skill without losing local edits
|
|
434
|
+
|
|
435
|
+
An upgrade does not overwrite an existing skill; rerunning \`pdks init claude-code\` reports it
|
|
436
|
+
skipped. Generate a fresh copy in a disposable project using the installed package, compare it
|
|
437
|
+
with this file, and merge the changes you want. Keep a backup of local additions. Do not delete
|
|
438
|
+
the existing skill to force regeneration in the working project.
|
|
439
|
+
|
|
278
440
|
## Reading the advise record
|
|
279
441
|
|
|
280
442
|
An \`advised\` row means a promise was broken and the call went through anyway. Rows land in
|
|
@@ -419,5 +581,6 @@ export function initClaudeCode(spec) {
|
|
|
419
581
|
// a failure costs least.
|
|
420
582
|
writeIfAbsent(spec.projectRoot, DISCOVERY_RELATIVE, GENERATED_DISCOVERY, report);
|
|
421
583
|
writeIfAbsent(spec.projectRoot, SKILL_RELATIVE, GENERATED_SKILL, report);
|
|
584
|
+
retargetGrokHookCommandToClaude(spec.projectRoot);
|
|
422
585
|
return report;
|
|
423
586
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `initGrok` — the Grok session-surface installer.
|
|
3
|
+
*
|
|
4
|
+
* Preflight first, then the shared project-side scaffold, then the generated hook and the
|
|
5
|
+
* `.grok/hooks` JSON registration. When a Claude delegator is already on disk, the JSON
|
|
6
|
+
* command names that file instead of planting a second one — two command strings would
|
|
7
|
+
* spawn two judges per call.
|
|
8
|
+
*
|
|
9
|
+
* Nothing existing is overwritten, with one command-field exception: a grok JSON whose
|
|
10
|
+
* `command` is still the grok-mjs string is rewritten to the Claude-hook command when that
|
|
11
|
+
* Claude file is on disk — otherwise grok-then-claude leaves two command strings. Any entry
|
|
12
|
+
* naming the Claude-hook command also takes the matcher of the `.claude/settings.json` entry
|
|
13
|
+
* carrying that command, because the host collapses the two registrations only when command
|
|
14
|
+
* and matcher are byte-identical; with no such entry the matcher stays. Timeout stays either
|
|
15
|
+
* way, and a command that is not the grok-mjs string is left byte-identical.
|
|
16
|
+
*
|
|
17
|
+
* Rules and skills are not copied; `.claude/settings.json` is not written.
|
|
18
|
+
*/
|
|
19
|
+
import { type ScaffoldReport } from './scaffold-project.ts';
|
|
20
|
+
/** `initGrok` input — the target tree and the preflight seam. */
|
|
21
|
+
export type InitGrokSpec = {
|
|
22
|
+
/** Project root to install into — every write below is relative to it. */
|
|
23
|
+
projectRoot: string;
|
|
24
|
+
/**
|
|
25
|
+
* Preflight seam: throws when the package cannot be resolved from the given root.
|
|
26
|
+
* ABSENT uses the real resolution, anchored at that root and nowhere else — anchoring it
|
|
27
|
+
* at the installer's own module would answer for the installer's install graph rather
|
|
28
|
+
* than the target project's, which is precisely the case that must fail.
|
|
29
|
+
*/
|
|
30
|
+
resolvePolydeukes?: (projectRoot: string) => void;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Rewrite the grok JSON `command` from the grok-mjs string to the Claude-hook string.
|
|
34
|
+
*
|
|
35
|
+
* The Claude installer calls this after writing its delegator; this installer calls it on
|
|
36
|
+
* re-run when that file is already on disk. Only the installer-generated grok-mjs command
|
|
37
|
+
* is rewritten; any other string is the consumer's spawn target and the file is not touched.
|
|
38
|
+
* Every entry naming the Claude-hook command — rewritten now or by an earlier install — takes
|
|
39
|
+
* the matcher the Claude settings file registered that command under, so the host sees one
|
|
40
|
+
* pair rather than two and a re-run converges. Parse failure leaves the file as it was —
|
|
41
|
+
* existence is presence, not parse success.
|
|
42
|
+
*/
|
|
43
|
+
export declare function retargetGrokHookCommandToClaude(projectRoot: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Install the Grok session surface into `spec.projectRoot`, skipping whatever is already
|
|
46
|
+
* there and reporting both halves per artifact.
|
|
47
|
+
*
|
|
48
|
+
* Throws before any write when the package cannot be resolved from that root — that leaves
|
|
49
|
+
* zero files. Translating a throw into exit 2 with the install command is the bin's job.
|
|
50
|
+
*/
|
|
51
|
+
export declare function initGrok(spec: InitGrokSpec): ScaffoldReport;
|