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/claude-code-hook.js
CHANGED
|
@@ -26,11 +26,13 @@
|
|
|
26
26
|
import { mkdirSync, readFileSync } from 'node:fs';
|
|
27
27
|
import { homedir } from 'node:os';
|
|
28
28
|
import { join, resolve } from 'node:path';
|
|
29
|
-
import { COMMAND_ARGS,
|
|
30
|
-
import { appendRecordFailOpen, DEFAULT_TELEMETRY_LOG_PATH, normalizeProtectedPaths, readRecords, } from '@polydeukes/core';
|
|
31
|
-
import {
|
|
29
|
+
import { COMMAND_ARGS, MUTATING_TOOLS, runAdapterPath, SHELL_TOOLS, sessionChannelReader, sessionSourceReader, transcriptFromJsonlFile, transcriptPathFromPayload, } from '@polydeukes/adapter-claude-code';
|
|
30
|
+
import { appendRecordFailOpen, DEFAULT_TELEMETRY_LOG_PATH, isPlainObject, normalizeProtectedPaths, readRecords, } from '@polydeukes/core';
|
|
31
|
+
import { ttlWitness } from '@polydeukes/covenant';
|
|
32
|
+
import { findUnattributed, readBaseline, snapshotBaseline, writeBaseline } from './baseline.js';
|
|
32
33
|
import { loadCovenantModule, resolveCovenantDist } from './covenant-module.js';
|
|
33
34
|
import { loadConfig } from './load-config.js';
|
|
35
|
+
import { sessionPreStateReader } from './pre-state-reader.js';
|
|
34
36
|
/** The label every post-hoc state comparison row carries. */
|
|
35
37
|
const BASELINE_LABEL = 'baseline';
|
|
36
38
|
/**
|
|
@@ -109,7 +111,7 @@ function updateBaseline(spec) {
|
|
|
109
111
|
function comparisonSpec(spec) {
|
|
110
112
|
let config;
|
|
111
113
|
try {
|
|
112
|
-
config = loadConfig(spec.repoRoot).config;
|
|
114
|
+
config = loadConfig({ rootDir: spec.repoRoot }).config;
|
|
113
115
|
}
|
|
114
116
|
catch {
|
|
115
117
|
return undefined;
|
|
@@ -129,9 +131,9 @@ function comparisonSpec(spec) {
|
|
|
129
131
|
*/
|
|
130
132
|
export function assembleSessionRegistrations(spec) {
|
|
131
133
|
const { config, rootDir, covenant, transcriptPath, transcript, witness } = spec;
|
|
132
|
-
// The live transcript is the
|
|
133
|
-
//
|
|
134
|
-
//
|
|
134
|
+
// The live transcript is the session a history declaration reads AND the one the witness
|
|
135
|
+
// reads, so erasing or forging it disables every history discipline while opening or
|
|
136
|
+
// shutting the human valve on the same file. It must NOT join this list: it lives deep
|
|
135
137
|
// under HOME, and a path entry makes every ancestor protected — which measured as an
|
|
136
138
|
// over-block refusing `cd /home/<user>`, `echo $HOME`, and every edit whose content
|
|
137
139
|
// carried a bare `~`. The dedicated `transcript-mod` registration below covers that one
|
|
@@ -156,7 +158,7 @@ export function assembleSessionRegistrations(spec) {
|
|
|
156
158
|
}),
|
|
157
159
|
// Routing is the matches predicate, never path mention, so the home directory cannot
|
|
158
160
|
// become a protected ancestor. No transcript in the payload means nothing to protect —
|
|
159
|
-
// the valve and
|
|
161
|
+
// the valve and every history declaration already forfeited on the same absence.
|
|
160
162
|
...(transcriptPath === undefined
|
|
161
163
|
? []
|
|
162
164
|
: [
|
|
@@ -179,17 +181,56 @@ export function assembleSessionRegistrations(spec) {
|
|
|
179
181
|
rootDir,
|
|
180
182
|
shellTools: SHELL_TOOLS,
|
|
181
183
|
commandArgs: COMMAND_ARGS,
|
|
184
|
+
readPreState: sessionPreStateReader,
|
|
185
|
+
// One PreToolUse call is the whole observation, so the derived change set is a
|
|
186
|
+
// singleton and a change-set declaration cannot be judged here — it records `skipped`,
|
|
187
|
+
// the shape the commit surface gives a history declaration.
|
|
188
|
+
observesChangeSet: false,
|
|
182
189
|
witness,
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
// `command`, which the compiler judges directly.
|
|
190
|
+
// The session itself, injected rather than its path: a declaration reading a
|
|
191
|
+
// `transcript` binding sees it flattened, and passing a path would leak JSONL
|
|
192
|
+
// knowledge into covenant.
|
|
187
193
|
transcript,
|
|
188
|
-
evaluatePrecedent,
|
|
189
194
|
}),
|
|
190
195
|
];
|
|
191
196
|
return registrations;
|
|
192
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* This runtime's mutating+shell roster, rewritten onto the Claude vocabulary the adapter
|
|
200
|
+
* already judges. Claude names are not keys, so an existing Write/Edit/Bash envelope
|
|
201
|
+
* passes through. A name outside the table is left alone — that is a declared limit,
|
|
202
|
+
* recorded as the adapter's funnel pass, never a parse fault. The map lives here, not in
|
|
203
|
+
* the adapter, so the adapter stays Claude-vocabulary-only.
|
|
204
|
+
*/
|
|
205
|
+
const GROK_TOOL_NAME_MAP = {
|
|
206
|
+
write: 'Write',
|
|
207
|
+
search_replace: 'Edit',
|
|
208
|
+
run_terminal_command: 'Bash',
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Rewrite Grok tool names in a raw PreToolUse payload. Invalid JSON is left as the original
|
|
212
|
+
* string so the existing fail-closed path still runs — this function must not throw.
|
|
213
|
+
*/
|
|
214
|
+
function rewriteGrokToolNames(rawPayload) {
|
|
215
|
+
let parsed;
|
|
216
|
+
try {
|
|
217
|
+
parsed = JSON.parse(rawPayload);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return rawPayload;
|
|
221
|
+
}
|
|
222
|
+
if (!isPlainObject(parsed))
|
|
223
|
+
return rawPayload;
|
|
224
|
+
for (const key of ['tool_name', 'toolName']) {
|
|
225
|
+
const value = parsed[key];
|
|
226
|
+
if (typeof value !== 'string')
|
|
227
|
+
continue;
|
|
228
|
+
const mapped = GROK_TOOL_NAME_MAP[value];
|
|
229
|
+
if (mapped !== undefined)
|
|
230
|
+
parsed[key] = mapped;
|
|
231
|
+
}
|
|
232
|
+
return JSON.stringify(parsed);
|
|
233
|
+
}
|
|
193
234
|
/**
|
|
194
235
|
* Judge one declared tool call before it runs. Async because the dispatcher spawns covenant
|
|
195
236
|
* bodies — a synchronous runner would mean reimplementing the judge, which the
|
|
@@ -212,20 +253,20 @@ async function judgeHookCall(spec) {
|
|
|
212
253
|
spec.telemetryPath ?? envTelemetryPath ?? join(spec.repoRoot, DEFAULT_TELEMETRY_LOG_PATH);
|
|
213
254
|
// Discovery + parse + validation are the loader's job; a throw here (absent, ambiguous,
|
|
214
255
|
// unparseable, or invalid config) falls into the fail-closed catch.
|
|
215
|
-
const { config } = loadConfig(spec.repoRoot);
|
|
256
|
+
const { config } = loadConfig({ rootDir: spec.repoRoot });
|
|
216
257
|
telemetryPath =
|
|
217
258
|
spec.telemetryPath ?? envTelemetryPath ?? resolve(spec.repoRoot, config.telemetry.logPath);
|
|
218
259
|
// Settled for the rest of the happy path. The `let` above exists so the catch can still
|
|
219
260
|
// record when a failure lands before this point; a closure cannot narrow it, so the
|
|
220
261
|
// dispatch seam below takes this const instead.
|
|
221
262
|
const logPath = telemetryPath;
|
|
222
|
-
const rawPayload = spec.rawPayload ?? readFileSync(0, 'utf-8');
|
|
263
|
+
const rawPayload = rewriteGrokToolNames(spec.rawPayload ?? readFileSync(0, 'utf-8'));
|
|
223
264
|
// The transcript path travels in the raw payload only — up-translation drops it, so the
|
|
224
265
|
// adapter reads it from the string. Every failure narrows to `undefined`, which leaves
|
|
225
266
|
// the dispatcher on its `noopTranscript` default: lost evidence closes the valve rather
|
|
226
267
|
// than opening it.
|
|
227
|
-
const transcriptPath = transcriptPathFromPayload(rawPayload);
|
|
228
|
-
const transcript = transcriptPath === undefined ? undefined : transcriptFromJsonlFile(transcriptPath);
|
|
268
|
+
const transcriptPath = transcriptPathFromPayload({ rawPayload });
|
|
269
|
+
const transcript = transcriptPath === undefined ? undefined : transcriptFromJsonlFile({ path: transcriptPath });
|
|
229
270
|
// One witness predicate shared by every registration: a witness is a session-wide
|
|
230
271
|
// permission the human granted, not a per-covenant one. Absent `witness` config leaves
|
|
231
272
|
// this undefined, and no verdict can be witnessed open at all. The predicate receives
|
|
@@ -260,6 +301,16 @@ async function judgeHookCall(spec) {
|
|
|
260
301
|
transcript,
|
|
261
302
|
witness,
|
|
262
303
|
});
|
|
304
|
+
// The world axis: files read from disk under the repository root, channels read beside
|
|
305
|
+
// the session's transcript. The disk is the pre-edit state on this surface; the rule
|
|
306
|
+
// that the judged change's own `post` overrides it belongs to the judge, so the root
|
|
307
|
+
// supplies what it read and nothing more. No `changes` list either — one PreToolUse call
|
|
308
|
+
// is the whole observation, and the judge derives that set from the input.
|
|
309
|
+
const { files, channels } = covenant.supplySources({
|
|
310
|
+
plan: covenant.planSources({ registrations }),
|
|
311
|
+
read: sessionSourceReader({ repoRoot: spec.repoRoot }),
|
|
312
|
+
readChannel: sessionChannelReader({ transcriptPath }),
|
|
313
|
+
});
|
|
263
314
|
return await runAdapterPath({
|
|
264
315
|
rawPayload,
|
|
265
316
|
telemetryPath: logPath,
|
|
@@ -268,6 +319,7 @@ async function judgeHookCall(spec) {
|
|
|
268
319
|
registrations,
|
|
269
320
|
telemetryPath: logPath,
|
|
270
321
|
transcript,
|
|
322
|
+
world: { files, channels },
|
|
271
323
|
}),
|
|
272
324
|
});
|
|
273
325
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `polydeukes/claude-code` — the session surface's entry point.
|
|
3
|
+
*
|
|
4
|
+
* The delegator a consumer project's PreToolUse hook imports: one verb and its spec type.
|
|
5
|
+
*/
|
|
6
|
+
export { type ClaudeCodeHookOutcome, type ClaudeCodeHookSpec, runClaudeCodeHook, } from './claude-code-hook.ts';
|
package/dist/covenant-check.d.ts
CHANGED
|
@@ -10,26 +10,21 @@
|
|
|
10
10
|
* fail-closed: a missing config, an unbuilt body, or a collector failure exits 2 with one
|
|
11
11
|
* blocked record. An empty domain is an explicit pass with no records.
|
|
12
12
|
*/
|
|
13
|
+
import { type Observation } from '@polydeukes/adapter-git';
|
|
13
14
|
import type { CovenantRegistration } from '@polydeukes/covenant';
|
|
14
|
-
import { type CovenantModule } from './covenant-module.
|
|
15
|
-
import { loadConfig } from './load-config.
|
|
15
|
+
import { type CovenantModule } from './covenant-module.ts';
|
|
16
|
+
import { loadConfig } from './load-config.ts';
|
|
16
17
|
/**
|
|
17
18
|
* Which observation of the commit surface a run judges. Only the collector differs between
|
|
18
19
|
* them; the IR, the assembly, and the dispatcher are one path.
|
|
19
20
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* range grammar resolves it.
|
|
21
|
+
* The adapter that owns the git grammar owns the type: its supply body reads a path the way
|
|
22
|
+
* each observation sees the tree, and this root names the same fact for its callers.
|
|
23
23
|
*/
|
|
24
|
-
export type CheckDomain =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
} | {
|
|
29
|
-
kind: 'range';
|
|
30
|
-
base: string;
|
|
31
|
-
head: string;
|
|
32
|
-
ancestry?: 'merge-base';
|
|
24
|
+
export type CheckDomain = Observation;
|
|
25
|
+
/** {@link runCovenantCheck} result — the exit code the check process leaves with. */
|
|
26
|
+
export type CovenantCheckOutcome = {
|
|
27
|
+
exitCode: 0 | 2;
|
|
33
28
|
};
|
|
34
29
|
/** `runCovenantCheck` input. */
|
|
35
30
|
export type CovenantCheckSpec = {
|
|
@@ -74,6 +69,4 @@ export declare function assembleCommitRegistrations(spec: CommitAssemblySpec): C
|
|
|
74
69
|
* diff by default, the working tree or a ref range on request. Async because the dispatcher
|
|
75
70
|
* spawns covenant bodies. An empty domain is an explicit pass: nothing to judge, no records.
|
|
76
71
|
*/
|
|
77
|
-
export declare function runCovenantCheck(spec: CovenantCheckSpec): Promise<
|
|
78
|
-
exitCode: 0 | 2;
|
|
79
|
-
}>;
|
|
72
|
+
export declare function runCovenantCheck(spec: CovenantCheckSpec): Promise<CovenantCheckOutcome>;
|
package/dist/covenant-check.js
CHANGED
|
@@ -11,10 +11,11 @@
|
|
|
11
11
|
* blocked record. An empty domain is an explicit pass with no records.
|
|
12
12
|
*/
|
|
13
13
|
import { resolve } from 'node:path';
|
|
14
|
-
import { collectRangeChanges, collectStagedChanges, collectWorktreeChanges, covenantInputFromStagedChanges, resolveGitAdapterSettings, STAGED_DELETE, STAGED_WRITE, } from '@polydeukes/adapter-git';
|
|
14
|
+
import { collectRangeChanges, collectStagedChanges, collectWorktreeChanges, covenantInputFromStagedChanges, observationSourceReader, resolveGitAdapterSettings, STAGED_DELETE, STAGED_WRITE, } from '@polydeukes/adapter-git';
|
|
15
15
|
import { appendRecordFailOpen, DEFAULT_TELEMETRY_LOG_PATH, normalizeProtectedPaths, } from '@polydeukes/core';
|
|
16
16
|
import { loadCovenantModule, resolveCovenantDist } from './covenant-module.js';
|
|
17
17
|
import { loadConfig } from './load-config.js';
|
|
18
|
+
import { unobservedPreStateReader } from './pre-state-reader.js';
|
|
18
19
|
/**
|
|
19
20
|
* The TTY witness predicate, or undefined when no valve can exist (no witness configured
|
|
20
21
|
* or no TTY seam). It fires on the first registration that broke, names it from the
|
|
@@ -61,7 +62,9 @@ function recordFailClosed(telemetryPath) {
|
|
|
61
62
|
*/
|
|
62
63
|
export function assembleCommitRegistrations(spec) {
|
|
63
64
|
const { config, rootDir, covenant, witness } = spec;
|
|
64
|
-
const { protectedPaths: gitAdditivePaths } = resolveGitAdapterSettings(
|
|
65
|
+
const { protectedPaths: gitAdditivePaths } = resolveGitAdapterSettings({
|
|
66
|
+
namespace: config.adapters?.git,
|
|
67
|
+
});
|
|
65
68
|
// Union of the common list and the git-additive one, common first so first-occurrence
|
|
66
69
|
// dedupe is deterministic. The session hook reads the common list alone.
|
|
67
70
|
const protectedPaths = normalizeProtectedPaths({
|
|
@@ -74,14 +77,13 @@ export function assembleCommitRegistrations(spec) {
|
|
|
74
77
|
mutatingToolNames: [STAGED_WRITE, STAGED_DELETE],
|
|
75
78
|
witness,
|
|
76
79
|
}),
|
|
77
|
-
// No shell axis here, so command-family entries are left out. Context-family entries
|
|
78
|
-
// stay in: with no transcript the compiler gives them skip registrations, which record
|
|
79
|
-
// `skipped` on a match.
|
|
80
80
|
...covenant.compileDisciplineRegistrations({
|
|
81
|
-
disciplines
|
|
81
|
+
disciplines,
|
|
82
82
|
rootDir,
|
|
83
83
|
shellTools: [],
|
|
84
84
|
commandArgs: [],
|
|
85
|
+
readPreState: unobservedPreStateReader,
|
|
86
|
+
observesChangeSet: true,
|
|
85
87
|
witness,
|
|
86
88
|
}),
|
|
87
89
|
];
|
|
@@ -104,7 +106,7 @@ function settleConfig(spec) {
|
|
|
104
106
|
let telemetryPath;
|
|
105
107
|
try {
|
|
106
108
|
telemetryPath = spec.telemetryPath ?? resolve(spec.repoRoot, DEFAULT_TELEMETRY_LOG_PATH);
|
|
107
|
-
const { config } = loadConfig(spec.repoRoot);
|
|
109
|
+
const { config } = loadConfig({ rootDir: spec.repoRoot });
|
|
108
110
|
telemetryPath = spec.telemetryPath ?? resolve(spec.repoRoot, config.telemetry.logPath);
|
|
109
111
|
return { settled: true, telemetryPath, config };
|
|
110
112
|
}
|
|
@@ -118,12 +120,32 @@ function settleConfig(spec) {
|
|
|
118
120
|
*/
|
|
119
121
|
function collectDomain(repoRoot, domain) {
|
|
120
122
|
if (domain.kind === 'worktree')
|
|
121
|
-
return collectWorktreeChanges(repoRoot);
|
|
123
|
+
return collectWorktreeChanges({ repoRoot });
|
|
122
124
|
if (domain.kind === 'range') {
|
|
123
125
|
const separator = domain.ancestry === 'merge-base' ? '...' : '..';
|
|
124
|
-
return collectRangeChanges(
|
|
126
|
+
return collectRangeChanges({
|
|
127
|
+
repoRoot,
|
|
128
|
+
range: `${domain.base}${separator}${domain.head}`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return collectStagedChanges({ repoRoot });
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The observation's change set: the paths of the collected changes that carry file-change
|
|
135
|
+
* evidence, in collection order.
|
|
136
|
+
*
|
|
137
|
+
* The same definition the judge derives its own set from, so both surfaces name the same
|
|
138
|
+
* changes. A deletion carries evidence and stays; a binary blob, which the collector gives
|
|
139
|
+
* a call with no evidence, produces no world of its own — listing it would hand the
|
|
140
|
+
* change-set relations a path no world can ever answer for.
|
|
141
|
+
*/
|
|
142
|
+
function changedPaths(changes) {
|
|
143
|
+
const paths = [];
|
|
144
|
+
for (const call of covenantInputFromStagedChanges({ changes }).toolCalls) {
|
|
145
|
+
if (call.fileChange !== undefined)
|
|
146
|
+
paths.push(call.fileChange.path);
|
|
125
147
|
}
|
|
126
|
-
return
|
|
148
|
+
return paths;
|
|
127
149
|
}
|
|
128
150
|
/**
|
|
129
151
|
* Assemble the registrations and dispatch every collected change. Any throw here (an
|
|
@@ -132,7 +154,7 @@ function collectDomain(repoRoot, domain) {
|
|
|
132
154
|
async function judgeChanges(spec, domain, telemetryPath, config, changes) {
|
|
133
155
|
try {
|
|
134
156
|
// Inside the try so an invalid adapter namespace fails closed.
|
|
135
|
-
const { enforce } = resolveGitAdapterSettings(config.adapters?.git);
|
|
157
|
+
const { enforce } = resolveGitAdapterSettings({ namespace: config.adapters?.git });
|
|
136
158
|
// Real Node resolution of the covenant package, so the commit surface runs the same
|
|
137
159
|
// judges the session hook does; tests inject a directory instead. Awaited before any
|
|
138
160
|
// registration is composed, so a dist the barrel cannot load fails the run closed here
|
|
@@ -155,14 +177,24 @@ async function judgeChanges(spec, domain, telemetryPath, config, changes) {
|
|
|
155
177
|
covenant,
|
|
156
178
|
witness,
|
|
157
179
|
});
|
|
180
|
+
// One plan and one supply for the run: the per-change loop shares them, so the tree is
|
|
181
|
+
// read once per named file rather than once per change. `changes` carries the whole
|
|
182
|
+
// observation because this surface dispatches one change at a time to keep telemetry at
|
|
183
|
+
// one row per file — a set no judge could derive from the input it is handed.
|
|
184
|
+
const { files } = covenant.supplySources({
|
|
185
|
+
plan: covenant.planSources({ registrations }),
|
|
186
|
+
read: observationSourceReader({ repoRoot: spec.repoRoot, observation: domain }),
|
|
187
|
+
});
|
|
188
|
+
const world = { files, changes: changedPaths(changes) };
|
|
158
189
|
for (const change of changes) {
|
|
159
|
-
const input = covenantInputFromStagedChanges([change]);
|
|
190
|
+
const input = covenantInputFromStagedChanges({ changes: [change] });
|
|
160
191
|
const { exitCode, results } = await covenant.dispatchCovenants({
|
|
161
192
|
stdinPayload: JSON.stringify(input),
|
|
162
193
|
registrations,
|
|
163
194
|
telemetryPath,
|
|
164
195
|
dispatcherLabel: 'covenant-check',
|
|
165
196
|
enforce,
|
|
197
|
+
world,
|
|
166
198
|
});
|
|
167
199
|
if (exitCode === 2)
|
|
168
200
|
blocked = true;
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* mirror where real Node resolution would always land on the healthy build.
|
|
13
13
|
*/
|
|
14
14
|
import type * as covenant from '@polydeukes/covenant';
|
|
15
|
-
/** The covenant surface both roots assemble against. */
|
|
16
|
-
export type CovenantModule = typeof covenant
|
|
15
|
+
/** The covenant surface both roots assemble against — the members they call, and no more. */
|
|
16
|
+
export type CovenantModule = Pick<typeof covenant, 'dispatchCovenants' | 'compileDisciplineRegistrations' | 'selfModRegistration' | 'shellModRegistration' | 'transcriptModRegistration' | 'planSources' | 'supplySources'>;
|
|
17
17
|
/** Where real Node resolution puts the covenant package's built barrel. */
|
|
18
18
|
export declare function resolveCovenantDist(): string;
|
|
19
19
|
/**
|
package/dist/covenant-module.js
CHANGED
|
@@ -11,6 +11,14 @@
|
|
|
11
11
|
* The `covenantDist` seam selects WHICH dist is imported, so a fixture can inject a gutted
|
|
12
12
|
* mirror where real Node resolution would always land on the healthy build.
|
|
13
13
|
*/
|
|
14
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
15
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
16
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
17
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return path;
|
|
21
|
+
};
|
|
14
22
|
import { createRequire } from 'node:module';
|
|
15
23
|
import { join } from 'node:path';
|
|
16
24
|
import { pathToFileURL } from 'node:url';
|
|
@@ -26,7 +34,7 @@ export function resolveCovenantDist() {
|
|
|
26
34
|
*/
|
|
27
35
|
export async function loadCovenantModule(distDir) {
|
|
28
36
|
try {
|
|
29
|
-
return (await import(pathToFileURL(join(distDir, 'index.js')).href));
|
|
37
|
+
return (await import(__rewriteRelativeImportExtension(pathToFileURL(join(distDir, 'index.js')).href)));
|
|
30
38
|
}
|
|
31
39
|
catch (error) {
|
|
32
40
|
throw new Error(`the covenant judges could not be loaded from ${distDir} — run 'pnpm build' to rebuild them: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# 폴리데우케스 문서
|
|
2
|
+
|
|
3
|
+
[English](./README.md) · **한국어**
|
|
4
|
+
|
|
5
|
+
> AI 코딩 파트너와 함께 개발하기 위한 규율 프레임워크입니다. 궁금한 곳부터 읽으십시오.
|
|
6
|
+
|
|
7
|
+
<a id="start-here"></a>
|
|
8
|
+
## 여기서 시작하십시오
|
|
9
|
+
|
|
10
|
+
| 하려는 것 | 읽을 문서 |
|
|
11
|
+
|---|---|
|
|
12
|
+
| 첫 눈에 보이는 판정을 보고 싶다 | [첫 판정](./tutorials/first-judgment.ko.md) — 설치하고 Claude Code를 배선한 뒤 보호된 편집이 판정되는 모습을 봅니다 |
|
|
13
|
+
| Claude Code, Grok, git을 연결하고 싶다 | [표면 연결하기](./how-to/connect-surfaces.ko.md) — 세션과 커밋 배선, Grok 포함 |
|
|
14
|
+
| 프로젝트 설정을 다듬고 싶다 | [프로젝트 설정하기](./how-to/configure-project.ko.md) — 발견, IDE 지원, advise와 block의 선택 |
|
|
15
|
+
| 실제 규율을 쓰고 싶다 | [규율 작성하기](./how-to/write-disciplines.ko.md) — 실전 선언 예제, 특히 locale key pairing |
|
|
16
|
+
| 차단이나 건너뜀에서 회복하고 싶다 | [문제 해결](./troubleshooting.ko.md) — fail-closed 상태, 증인 밸브, 로그 |
|
|
17
|
+
| 이 프레임워크가 왜 있는지 알고 싶다 | [왜 폴리데우케스인가?](./why-polydeukes.ko.md) — 설계 백서 |
|
|
18
|
+
| 이 문서에 기여하고 싶다 | [문서에 기여하기](./contributing.ko.md) — 영한 쌍, 안정 ID, 카탈로그, 검사 |
|
|
19
|
+
|
|
20
|
+
<a id="reference"></a>
|
|
21
|
+
## 레퍼런스
|
|
22
|
+
|
|
23
|
+
설정 키, 하위 명령, 종료 코드를 설명합니다. 계획이 아니라 현재 동작을 기준으로 서술합니다.
|
|
24
|
+
|
|
25
|
+
| 문서 | 답하는 것 |
|
|
26
|
+
|---|---|
|
|
27
|
+
| [설정 레퍼런스](./reference/configuration/index.ko.md) | `polydeukes.config.yaml`에 무엇을 넣을 수 있고 각 키가 무엇을 하는지 |
|
|
28
|
+
| [`polydeukes` (`pdks` CLI)](./reference/packages/polydeukes.ko.md) | 패키지 계약. 서브커맨드는 [`reference/cli/`](./reference/cli/covenant-check.ko.md) |
|
|
29
|
+
| [`@polydeukes/core`](./reference/packages/core.ko.md) | 프로토콜, 입력 IR, 설정 스키마, 텔레메트리 |
|
|
30
|
+
| [`@polydeukes/covenant`](./reference/packages/covenant.ko.md) | 판정기입니다. 디스패처와 규율 라이브러리, 메타 약속, 밸브 |
|
|
31
|
+
| [`@polydeukes/adapter-claude-code`](./reference/packages/adapter-claude-code.ko.md) | 세션 표면입니다. 훅 페이로드에서 입력 IR로 |
|
|
32
|
+
| [`@polydeukes/adapter-git`](./reference/packages/adapter-git.ko.md) | 커밋 표면입니다. 스테이징·작업 트리·범위 diff에서 입력 IR로 |
|
|
33
|
+
|
|
34
|
+
<a id="shape-of-the-thing"></a>
|
|
35
|
+
## 한 페이지로 보는 구조
|
|
36
|
+
|
|
37
|
+
폴리데우케스는 개발자나 AI 에이전트가 하려는 일을 판정하고 결과를 기록합니다.
|
|
38
|
+
기본값으로는 작업을 차단하지 않습니다. 설계의 바탕은 다음 세 가지입니다.
|
|
39
|
+
|
|
40
|
+
**약속은 가두기 위한 울타리가 아닙니다.** 여기서 확인하는 규율은 개발자가 이미 스스로 지켜 온 것들입니다. AI에게만큼 사람에게도 똑같이 적용되고, 프레임워크를 만든
|
|
41
|
+
사람도 매일 그 판정을 받습니다.
|
|
42
|
+
|
|
43
|
+
**판정과 차단은 별개의 결정입니다.** 선언된 규율은 적용 범위에 해당하는 호출마다 판정합니다.
|
|
44
|
+
위반했을 때 호출을 차단할지는 별도로 정합니다. 기본값으로는 사유를 기록하고 호출을 계속합니다.
|
|
45
|
+
작성자는 `enforce: block`으로 차단을 선택할 수 있습니다. 별도 선택 없이 차단하는 것은
|
|
46
|
+
프레임워크 자체를 보호하는 경우뿐입니다.
|
|
47
|
+
|
|
48
|
+
**모든 판정은 행 하나를 남깁니다.** `.polydeukes/roi.log`가 판정 결과마다 한 줄씩, 낱말 여섯 개짜리 어휘로 담습니다. 이 프로젝트가 자기 결함을 찾는
|
|
49
|
+
방법이 그 기록입니다. 백서에 적힌 결함들도 전부 코드를 읽어서가 아니라 행을 세어서 나왔습니다.
|
|
50
|
+
|
|
51
|
+
<a id="two-surfaces"></a>
|
|
52
|
+
## 두 표면
|
|
53
|
+
|
|
54
|
+
| 표면 | 판정 대상 | 배선 방법 | 대상 |
|
|
55
|
+
|---|---|---|---|
|
|
56
|
+
| **세션** | 도구 호출, 실행되기 전에 | `pdks init claude-code` 또는 `pdks init grok` | AI 파트너와 함께 개발하는 프로젝트 |
|
|
57
|
+
| **커밋** | diff — 스테이징 영역, 작업 트리, ref 범위 | pre-commit 훅, 또는 필요할 때 직접 실행 | 혼자 개발하는 사람, 그리고 CI |
|
|
58
|
+
|
|
59
|
+
커밋 판정기는 필요할 때 직접 실행할 수도 있습니다. 작업 후에는 `pdks covenant check --worktree`,
|
|
60
|
+
PR 전에는 `--range`를 사용합니다. 같은 판정 기준으로 결과를 보고하며 증인 입력은 요청하지 않습니다.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Polydeukes documentation
|
|
2
|
+
|
|
3
|
+
**English** · [한국어](./README.ko.md)
|
|
4
|
+
|
|
5
|
+
> A development discipline framework for building alongside an AI coding partner. Start where your
|
|
6
|
+
question is.
|
|
7
|
+
|
|
8
|
+
<a id="start-here"></a>
|
|
9
|
+
## Start here
|
|
10
|
+
|
|
11
|
+
| If you want to | Read |
|
|
12
|
+
|---|---|
|
|
13
|
+
| Get to a first visible judgment | [First judgment](./tutorials/first-judgment.md) — install, wire Claude Code, and watch one protected edit get judged |
|
|
14
|
+
| Connect Claude Code, Grok, or git | [Connect the surfaces](./how-to/connect-surfaces.md) — session and commit setup, including Grok |
|
|
15
|
+
| Shape the project config | [Configure the project](./how-to/configure-project.md) — discovery, IDE support, and advise versus block |
|
|
16
|
+
| Write a real discipline | [Write disciplines](./how-to/write-disciplines.md) — worked declarations, especially locale key pairing |
|
|
17
|
+
| Recover from a block or skip | [Troubleshooting](./troubleshooting.md) — the fail-closed states, the witness valve, and the log |
|
|
18
|
+
| Understand why the framework exists | [Why Polydeukes?](./why-polydeukes.md) — the design whitepaper |
|
|
19
|
+
| Contribute to these docs | [Contributing](./contributing.md) — bilingual pairs, stable IDs, catalog, and checks |
|
|
20
|
+
|
|
21
|
+
<a id="reference"></a>
|
|
22
|
+
## Reference
|
|
23
|
+
|
|
24
|
+
Every key, every subcommand, every exit code. These describe the present state only — nothing here
|
|
25
|
+
is aspirational.
|
|
26
|
+
|
|
27
|
+
| Document | Answers |
|
|
28
|
+
|---|---|
|
|
29
|
+
| [Configuration reference](./reference/configuration/index.md) | What may go in `polydeukes.config.yaml`, and what each key does |
|
|
30
|
+
| [`polydeukes` (the `pdks` CLI)](./reference/packages/polydeukes.md) | Package contract; subcommands live under [`reference/cli/`](./reference/cli/covenant-check.md) |
|
|
31
|
+
| [`@polydeukes/core`](./reference/packages/core.md) | The protocol, the input IR, the config schema, telemetry |
|
|
32
|
+
| [`@polydeukes/covenant`](./reference/packages/covenant.md) | The judge — dispatcher, discipline library, meta-covenants, the valve |
|
|
33
|
+
| [`@polydeukes/adapter-claude-code`](./reference/packages/adapter-claude-code.md) | Session surface — hook payloads become the input IR |
|
|
34
|
+
| [`@polydeukes/adapter-git`](./reference/packages/adapter-git.md) | Commit surface — staged, worktree, and range diffs become the input IR |
|
|
35
|
+
|
|
36
|
+
<a id="shape-of-the-thing"></a>
|
|
37
|
+
## The shape of the thing, in one page
|
|
38
|
+
|
|
39
|
+
Polydeukes judges what a developer or an AI agent is about to do, records the verdict, and by
|
|
40
|
+
default lets the work proceed. Three ideas carry the rest.
|
|
41
|
+
|
|
42
|
+
**A covenant is a promise, not a fence.** The disciplines it enforces are the ones a good developer
|
|
43
|
+
already imposes on themselves. They bind the human exactly as much as the AI, and the framework's
|
|
44
|
+
own authors get judged by them daily.
|
|
45
|
+
|
|
46
|
+
**Judging and stopping are separate decisions.** Every declared discipline is judged on every
|
|
47
|
+
matching call. What a break then does is a second question: by default it is recorded with its
|
|
48
|
+
reason and the call continues. `enforce: block` is a promotion the author chooses. The only things
|
|
49
|
+
that stop a call unasked are the framework's own protections.
|
|
50
|
+
|
|
51
|
+
**Every judgment leaves a row.** `.polydeukes/roi.log` holds one line per verdict, in a vocabulary
|
|
52
|
+
of six words. That record is how this project finds its own defects — including the ones described
|
|
53
|
+
in the whitepaper, which were all found by counting rows rather than by reading code.
|
|
54
|
+
|
|
55
|
+
<a id="two-surfaces"></a>
|
|
56
|
+
## Two surfaces
|
|
57
|
+
|
|
58
|
+
| Surface | Judges | Wired by | For |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| **Session** | A tool call, before it runs | `pdks init claude-code` or `pdks init grok` | A project developed with an AI partner |
|
|
61
|
+
| **Commit** | A diff — staged, the working tree, or a ref range | A pre-commit hook, or run on demand | A human developing alone, and CI |
|
|
62
|
+
|
|
63
|
+
The commit judge also answers on demand: `pdks covenant check --worktree` after a task, `--range`
|
|
64
|
+
before a PR. Same verdict a commit would receive, delivered as a report with no prompt and no gate.
|