polydeukes 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +16 -28
- package/README.md +16 -28
- package/dist/baseline.d.ts +37 -1
- package/dist/baseline.js +68 -1
- package/dist/bin.d.ts +3 -4
- package/dist/bin.js +73 -90
- package/dist/covenant/bash-line.d.ts +130 -0
- package/dist/covenant/bash-line.js +566 -0
- package/dist/covenant/declaration-engine.d.ts +108 -0
- package/dist/covenant/declaration-engine.js +415 -0
- package/dist/covenant/discipline.d.ts +109 -0
- package/dist/covenant/discipline.js +667 -0
- package/dist/covenant/dispatch.d.ts +156 -0
- package/dist/covenant/dispatch.js +250 -0
- package/dist/covenant/extract-steps.d.ts +122 -0
- package/dist/covenant/extract-steps.js +0 -0
- package/dist/covenant/mention.d.ts +117 -0
- package/dist/covenant/mention.js +209 -0
- package/dist/covenant/module.d.ts +25 -0
- package/dist/covenant/module.js +23 -0
- package/dist/covenant/mutation-rules.d.ts +40 -0
- package/dist/covenant/mutation-rules.js +146 -0
- package/dist/covenant/relations.d.ts +46 -0
- package/dist/covenant/relations.js +68 -0
- package/dist/covenant/run-covenant.d.ts +95 -0
- package/dist/covenant/run-covenant.js +164 -0
- package/dist/covenant/self-mod.d.ts +55 -0
- package/dist/covenant/self-mod.js +84 -0
- package/dist/covenant/shell-evidence.d.ts +38 -0
- package/dist/covenant/shell-evidence.js +247 -0
- package/dist/covenant/shell-mod.d.ts +84 -0
- package/dist/covenant/shell-mod.js +213 -0
- package/dist/covenant/supply.d.ts +54 -0
- package/dist/covenant/supply.js +50 -0
- package/dist/covenant/transcript-mod.d.ts +60 -0
- package/dist/covenant/transcript-mod.js +254 -0
- package/dist/covenant/ttl-witness.d.ts +47 -0
- package/dist/covenant/ttl-witness.js +80 -0
- package/dist/covenant-check.d.ts +74 -41
- package/dist/covenant-check.js +372 -129
- package/dist/diff-ir.d.ts +28 -0
- package/dist/diff-ir.js +310 -0
- package/dist/docs/README.ko.md +10 -9
- package/dist/docs/README.md +10 -9
- package/dist/docs/catalog.json +76 -28
- package/dist/docs/concepts/judgment.ko.md +2 -2
- package/dist/docs/concepts/judgment.md +2 -2
- package/dist/docs/how-to/configure-project.ko.md +16 -16
- package/dist/docs/how-to/configure-project.md +15 -12
- package/dist/docs/how-to/connect-surfaces.ko.md +67 -34
- package/dist/docs/how-to/connect-surfaces.md +67 -34
- package/dist/docs/how-to/write-disciplines.ko.md +64 -7
- package/dist/docs/how-to/write-disciplines.md +65 -7
- package/dist/docs/index.json +744 -362
- package/dist/docs/reference/cli/covenant-check.ko.md +112 -65
- package/dist/docs/reference/cli/covenant-check.md +114 -59
- package/dist/docs/reference/cli/docs.ko.md +2 -2
- package/dist/docs/reference/cli/docs.md +2 -2
- package/dist/docs/reference/cli/explain.ko.md +20 -15
- package/dist/docs/reference/cli/explain.md +22 -15
- package/dist/docs/reference/cli/init.ko.md +91 -52
- package/dist/docs/reference/cli/init.md +81 -55
- package/dist/docs/reference/configuration/index.ko.md +182 -71
- package/dist/docs/reference/configuration/index.md +188 -76
- package/dist/docs/reference/packages/adapter-claude-code.ko.md +39 -11
- package/dist/docs/reference/packages/adapter-claude-code.md +40 -14
- package/dist/docs/reference/packages/adapter-codex.ko.md +99 -0
- package/dist/docs/reference/packages/adapter-codex.md +100 -0
- package/dist/docs/reference/packages/adapter-grok.ko.md +81 -0
- package/dist/docs/reference/packages/adapter-grok.md +83 -0
- package/dist/docs/reference/packages/core.ko.md +50 -4
- package/dist/docs/reference/packages/core.md +55 -6
- package/dist/docs/reference/packages/polydeukes.ko.md +139 -77
- package/dist/docs/reference/packages/polydeukes.md +147 -81
- package/dist/docs/reference/packages/sdk-ts.ko.md +164 -0
- package/dist/docs/reference/packages/sdk-ts.md +167 -0
- package/dist/docs/troubleshooting.ko.md +51 -19
- package/dist/docs/troubleshooting.md +53 -17
- package/dist/docs/tutorials/first-judgment.ko.md +6 -4
- package/dist/docs/tutorials/first-judgment.md +6 -4
- package/dist/explain.d.ts +1 -1
- package/dist/explain.js +37 -22
- package/dist/load-config.d.ts +25 -1
- package/dist/load-config.js +21 -3
- package/dist/pre-state-reader.d.ts +1 -1
- package/dist/pre-state-reader.js +1 -1
- package/dist/scaffold-project.d.ts +2 -2
- package/dist/scaffold-project.js +12 -4
- package/dist/schema/polydeukes.schema.json +12 -0
- package/dist/worktree-reader.d.ts +19 -0
- package/dist/worktree-reader.js +30 -0
- package/package.json +5 -18
- package/dist/claude-code-hook.d.ts +0 -79
- package/dist/claude-code-hook.js +0 -372
- package/dist/claude-code.d.ts +0 -6
- package/dist/claude-code.js +0 -6
- package/dist/covenant-module.d.ts +0 -25
- package/dist/covenant-module.js +0 -42
- package/dist/docs/reference/packages/adapter-git.ko.md +0 -101
- package/dist/docs/reference/packages/adapter-git.md +0 -108
- package/dist/docs/reference/packages/covenant.ko.md +0 -115
- package/dist/docs/reference/packages/covenant.md +0 -123
- package/dist/index.d.ts +0 -22
- package/dist/index.js +0 -21
- package/dist/init-claude-code.d.ts +0 -52
- package/dist/init-claude-code.js +0 -586
- package/dist/init-grok.d.ts +0 -51
- package/dist/init-grok.js +0 -242
package/dist/explain.js
CHANGED
|
@@ -9,15 +9,18 @@
|
|
|
9
9
|
* nothing and reads no disk. Every failure throws: an answer that cannot be given is never
|
|
10
10
|
* given halfway.
|
|
11
11
|
*/
|
|
12
|
-
import { join } from 'node:path';
|
|
13
|
-
import { resolveGitAdapterSettings } from '@polydeukes/adapter-git';
|
|
14
12
|
import { AXIS_NAMES, deriveShape, noopTranscript, RELATION_NAMES } from '@polydeukes/core';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { loadCovenantModule, resolveCovenantDist } from './covenant-module.js';
|
|
13
|
+
import { covenantModule } from './covenant/module.js';
|
|
14
|
+
import { assembleChangeSetRegistrations, assembleCheckRegistrations } from './covenant-check.js';
|
|
18
15
|
import { loadConfig } from './load-config.js';
|
|
19
16
|
/** The three meta-covenant labels: registrations that protect the judging chain itself. */
|
|
20
17
|
const META_LABELS = new Set(['self-mod', 'shell-mod', 'transcript-mod']);
|
|
18
|
+
/**
|
|
19
|
+
* What the session assembly here is given as its evidence path. The real one is loaded per
|
|
20
|
+
* call by an adapter, so this renderer names the IR key instead of a file: the assembly
|
|
21
|
+
* needs a present value to build the conditional registration, and nothing prints it.
|
|
22
|
+
*/
|
|
23
|
+
const SESSION_EVIDENCE_PATH_KEY = 'session.evidencePath';
|
|
21
24
|
/**
|
|
22
25
|
* The description of a declaration entry: its catalogue coordinate (the mechanism, the axes
|
|
23
26
|
* its sources derive, and the relations its entries decide), then what it routes on, how
|
|
@@ -58,7 +61,7 @@ function row(kind, label, width, description) {
|
|
|
58
61
|
/** The description of a meta-covenant registration — how much surface it covers. */
|
|
59
62
|
function metaDescription(registration, surface) {
|
|
60
63
|
if (registration.label === 'transcript-mod') {
|
|
61
|
-
return 'content predicate · conditional:
|
|
64
|
+
return 'content predicate · conditional: session.evidencePath';
|
|
62
65
|
}
|
|
63
66
|
return `paths ${registration.protectedPaths.length} (${surface})`;
|
|
64
67
|
}
|
|
@@ -105,47 +108,59 @@ function renderSurface(spec) {
|
|
|
105
108
|
* Read the config at `repoRoot`, assemble both surfaces, and render them.
|
|
106
109
|
*
|
|
107
110
|
* The session assembly is given a transcript path, so its `transcript-mod` registration
|
|
108
|
-
* exists here exactly as it does under a normal
|
|
111
|
+
* exists here exactly as it does under a normal call payload — the path is never read,
|
|
109
112
|
* because the injected transcript is the no-op one.
|
|
110
113
|
*/
|
|
111
114
|
export async function explain(spec) {
|
|
112
115
|
const { config, configPath } = loadConfig({ rootDir: spec.repoRoot });
|
|
113
|
-
//
|
|
114
|
-
// that would judge
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
const
|
|
116
|
+
// The judge module the two runners assemble against, so what this renders is the table
|
|
117
|
+
// that would judge.
|
|
118
|
+
const covenant = covenantModule;
|
|
119
|
+
const shared = config.disciplines ?? [];
|
|
120
|
+
const sessionOnly = config.sessionDisciplines ?? [];
|
|
121
|
+
const changeSetOnly = config.changeSetDisciplines ?? [];
|
|
118
122
|
const drafts = config.drafts ?? [];
|
|
119
|
-
const session =
|
|
123
|
+
const session = assembleCheckRegistrations({
|
|
120
124
|
config,
|
|
121
125
|
rootDir: spec.repoRoot,
|
|
122
126
|
covenant,
|
|
123
|
-
|
|
127
|
+
surface: 'session',
|
|
128
|
+
// The session key alone, and no roster: a roster is what an adapter loads onto each
|
|
129
|
+
// call, so a reader of the config would be shown one agent's names chosen by this
|
|
130
|
+
// renderer. The evidence path is the same kind of per-call value and is named by the
|
|
131
|
+
// key rather than by a filename: what a config reader is being shown is that the
|
|
132
|
+
// session surface carries a transcript-mod registration, and the renderer prints the
|
|
133
|
+
// key, never the path. The empty lists are the session a run supplies.
|
|
134
|
+
session: { evidencePath: SESSION_EVIDENCE_PATH_KEY, userMessages: [], toolCalls: [] },
|
|
124
135
|
transcript: noopTranscript,
|
|
125
136
|
});
|
|
126
|
-
const
|
|
137
|
+
const changeSet = assembleChangeSetRegistrations({
|
|
127
138
|
config,
|
|
128
139
|
rootDir: spec.repoRoot,
|
|
129
140
|
covenant,
|
|
141
|
+
surface: 'changeSet',
|
|
130
142
|
});
|
|
131
|
-
const gitSettings = resolveGitAdapterSettings({ namespace: config.adapters?.git });
|
|
132
143
|
const text = [
|
|
133
144
|
`pdks explain — ${configPath}`,
|
|
134
145
|
'',
|
|
135
146
|
renderSurface({
|
|
136
|
-
header: '
|
|
147
|
+
header: 'input: call IR (one call, stdin) · ' +
|
|
148
|
+
`disciplines ${shared.length} · sessionDisciplines ${sessionOnly.length} · ` +
|
|
149
|
+
'disciplines: advise unless enforce: block · meta: block',
|
|
137
150
|
registrations: session,
|
|
138
151
|
drafts,
|
|
139
|
-
disciplines,
|
|
152
|
+
disciplines: [...shared, ...sessionOnly],
|
|
140
153
|
selfModScope: 'common; includes the config file itself',
|
|
141
154
|
}),
|
|
142
155
|
'',
|
|
143
156
|
renderSurface({
|
|
144
|
-
header:
|
|
145
|
-
|
|
157
|
+
header: 'input: --diff (change set, stdin) · ' +
|
|
158
|
+
`disciplines ${shared.length} · changeSetDisciplines ${changeSetOnly.length} · ` +
|
|
159
|
+
'disciplines: advise unless enforce: block',
|
|
160
|
+
registrations: changeSet,
|
|
146
161
|
drafts,
|
|
147
|
-
disciplines,
|
|
148
|
-
selfModScope: 'common
|
|
162
|
+
disciplines: [...shared, ...changeSetOnly],
|
|
163
|
+
selfModScope: 'common; includes the config file itself',
|
|
149
164
|
}),
|
|
150
165
|
'',
|
|
151
166
|
].join('\n');
|
package/dist/load-config.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* This lives in its own module rather than in the package barrel because ESM re-exports are
|
|
6
6
|
* eager: importing `loadConfig` from the barrel would instantiate both composition roots,
|
|
7
|
-
* putting the session adapter on the
|
|
7
|
+
* putting the session adapter on the change-set surface's load path where it is never used. A
|
|
8
8
|
* workspace missing only that dist would then kill `pdks covenant check` before its
|
|
9
9
|
* fail-closed handler could record a row. Both composition roots import this module directly
|
|
10
10
|
* for the same reason.
|
|
@@ -20,6 +20,17 @@ export declare const CONFIG_FILENAMES: readonly ['polydeukes.config.yaml', 'poly
|
|
|
20
20
|
export type LoadConfigSpec = {
|
|
21
21
|
rootDir: string;
|
|
22
22
|
};
|
|
23
|
+
/** {@link discoverConfigPath} input — the directory the candidates are looked for in. */
|
|
24
|
+
export type DiscoverConfigPathSpec = {
|
|
25
|
+
rootDir: string;
|
|
26
|
+
};
|
|
27
|
+
/** {@link parseConfigSource} input — the config text and the path it was discovered at. */
|
|
28
|
+
export type ParseConfigSourceSpec = {
|
|
29
|
+
/** The config file's whole text. */
|
|
30
|
+
source: string;
|
|
31
|
+
/** rootDir-relative path the source came from — the self-protection entry and error context. */
|
|
32
|
+
configPath: string;
|
|
33
|
+
};
|
|
23
34
|
/** `LoadedConfig` — the loader's return value. */
|
|
24
35
|
export type LoadedConfig = {
|
|
25
36
|
/** defineConfig() resolution — protectedPaths already includes configPath */
|
|
@@ -43,3 +54,16 @@ export type LoadedConfig = {
|
|
|
43
54
|
* protection surface, guaranteed here so no assembler has to remember.
|
|
44
55
|
*/
|
|
45
56
|
export declare function loadConfig(spec: LoadConfigSpec): LoadedConfig;
|
|
57
|
+
/**
|
|
58
|
+
* The discovery half: the rootDir-relative filename of the one candidate present, or the
|
|
59
|
+
* throw that names the zero or the collision. Exported so a caller that has to know WHICH
|
|
60
|
+
* file failed to load — the runner's config-repair branch — asks the same question the
|
|
61
|
+
* loader does rather than a second spelling of it.
|
|
62
|
+
*/
|
|
63
|
+
export declare function discoverConfigPath(spec: DiscoverConfigPathSpec): string;
|
|
64
|
+
/**
|
|
65
|
+
* The parse-and-validate half, over a text rather than a file: parse, `$schema` strip,
|
|
66
|
+
* `defineConfig`, self-protection attach. Exported so the runner can ask whether a text a
|
|
67
|
+
* call is about to write would load, without opening any file.
|
|
68
|
+
*/
|
|
69
|
+
export declare function parseConfigSource(spec: ParseConfigSourceSpec): LoadedConfig;
|
package/dist/load-config.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* This lives in its own module rather than in the package barrel because ESM re-exports are
|
|
6
6
|
* eager: importing `loadConfig` from the barrel would instantiate both composition roots,
|
|
7
|
-
* putting the session adapter on the
|
|
7
|
+
* putting the session adapter on the change-set surface's load path where it is never used. A
|
|
8
8
|
* workspace missing only that dist would then kill `pdks covenant check` before its
|
|
9
9
|
* fail-closed handler could record a row. Both composition roots import this module directly
|
|
10
10
|
* for the same reason.
|
|
@@ -39,6 +39,17 @@ export const CONFIG_FILENAMES = [
|
|
|
39
39
|
* protection surface, guaranteed here so no assembler has to remember.
|
|
40
40
|
*/
|
|
41
41
|
export function loadConfig(spec) {
|
|
42
|
+
const configPath = discoverConfigPath({ rootDir: spec.rootDir });
|
|
43
|
+
const source = readFileSync(join(spec.rootDir, configPath), 'utf-8');
|
|
44
|
+
return parseConfigSource({ source, configPath });
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The discovery half: the rootDir-relative filename of the one candidate present, or the
|
|
48
|
+
* throw that names the zero or the collision. Exported so a caller that has to know WHICH
|
|
49
|
+
* file failed to load — the runner's config-repair branch — asks the same question the
|
|
50
|
+
* loader does rather than a second spelling of it.
|
|
51
|
+
*/
|
|
52
|
+
export function discoverConfigPath(spec) {
|
|
42
53
|
const { rootDir } = spec;
|
|
43
54
|
const found = CONFIG_FILENAMES.filter((name) => existsSync(join(rootDir, name)));
|
|
44
55
|
if (found.length === 0) {
|
|
@@ -47,8 +58,15 @@ export function loadConfig(spec) {
|
|
|
47
58
|
if (found.length > 1) {
|
|
48
59
|
throw new Error(`ambiguous Polydeukes config in ${rootDir} — found ${found.join(' and ')}; keep exactly one`);
|
|
49
60
|
}
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
return found[0];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The parse-and-validate half, over a text rather than a file: parse, `$schema` strip,
|
|
65
|
+
* `defineConfig`, self-protection attach. Exported so the runner can ask whether a text a
|
|
66
|
+
* call is about to write would load, without opening any file.
|
|
67
|
+
*/
|
|
68
|
+
export function parseConfigSource(spec) {
|
|
69
|
+
const { source, configPath } = spec;
|
|
52
70
|
// Default core schema — custom tags stay unresolved and surface as errors or
|
|
53
71
|
// warnings depending on version; both escalate to a throw (config-as-data:
|
|
54
72
|
// uncomputable, so it cannot lie).
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function sessionPreStateReader(location: string): string | null | undefined;
|
|
15
15
|
/**
|
|
16
|
-
* The
|
|
16
|
+
* The change-set surface's reader: it observes a staged diff, whose payloads already carry the
|
|
17
17
|
* pre their own observation saw, and it registers no shell axis — so no derivation ever asks
|
|
18
18
|
* for a pre-state here. Should one arrive, the working tree is not what this surface judges,
|
|
19
19
|
* and answering from it would compare the diff against the wrong baseline; `undefined` says
|
package/dist/pre-state-reader.js
CHANGED
|
@@ -21,7 +21,7 @@ export function sessionPreStateReader(location) {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* The
|
|
24
|
+
* The change-set surface's reader: it observes a staged diff, whose payloads already carry the
|
|
25
25
|
* pre their own observation saw, and it registers no shell axis — so no derivation ever asks
|
|
26
26
|
* for a pre-state here. Should one arrive, the working tree is not what this surface judges,
|
|
27
27
|
* and answering from it would compare the diff against the wrong baseline; `undefined` says
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The half of an installation every distribution path shares: the data config the judges
|
|
5
5
|
* read, and the telemetry ignore line. What differs between paths is REGISTRATION — how the
|
|
6
|
-
* agent is told to spawn a judge at all — and that lives
|
|
7
|
-
*
|
|
6
|
+
* agent is told to spawn a judge at all — and that lives in each agent's own adapter
|
|
7
|
+
* installer. The split is what lets a second path reuse this function unchanged
|
|
8
8
|
* instead of scaffolding a config a second time, so nothing that registers anything belongs
|
|
9
9
|
* here.
|
|
10
10
|
*
|
package/dist/scaffold-project.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The half of an installation every distribution path shares: the data config the judges
|
|
5
5
|
* read, and the telemetry ignore line. What differs between paths is REGISTRATION — how the
|
|
6
|
-
* agent is told to spawn a judge at all — and that lives
|
|
7
|
-
*
|
|
6
|
+
* agent is told to spawn a judge at all — and that lives in each agent's own adapter
|
|
7
|
+
* installer. The split is what lets a second path reuse this function unchanged
|
|
8
8
|
* instead of scaffolding a config a second time, so nothing that registers anything belongs
|
|
9
9
|
* here.
|
|
10
10
|
*
|
|
@@ -32,7 +32,7 @@ const GITIGNORE_ENTRY = `# Polydeukes telemetry — local observation data, neve
|
|
|
32
32
|
* {@link schemaDirective} prepends the `yaml-language-server` line when the schema is where
|
|
33
33
|
* that line would name it.
|
|
34
34
|
*/
|
|
35
|
-
const GENERATED_CONFIG = `# Polydeukes protection policy — generated by \`pdks init
|
|
35
|
+
const GENERATED_CONFIG = `# Polydeukes protection policy — generated by \`pdks init\`.
|
|
36
36
|
#
|
|
37
37
|
# This file is data, never code. The judges read it, every verdict traces back to an entry
|
|
38
38
|
# below, and editing it is how you change what is judged.
|
|
@@ -50,7 +50,8 @@ languages:
|
|
|
50
50
|
# The protection list. A tool call whose proven target is one of these paths is blocked, and
|
|
51
51
|
# so is a shell command that mentions one without a read-only head.
|
|
52
52
|
#
|
|
53
|
-
# .claude/hooks, .claude/settings.json, .grok/hooks — the gate definitions
|
|
53
|
+
# .claude/hooks, .claude/settings.json, .grok/hooks, .codex/hooks — the gate definitions
|
|
54
|
+
# themselves.
|
|
54
55
|
# Editing them does not evade a judgment, it removes the judgment; the session surface
|
|
55
56
|
# is the only layer that can watch it happen.
|
|
56
57
|
#
|
|
@@ -62,6 +63,7 @@ protectedPaths:
|
|
|
62
63
|
- '.claude/hooks'
|
|
63
64
|
- '.claude/settings.json'
|
|
64
65
|
- '.grok/hooks'
|
|
66
|
+
- '.codex/hooks'
|
|
65
67
|
|
|
66
68
|
# The time-boxed witness — the human valve on a blocked verdict. A human types this token so
|
|
67
69
|
# it stands alone on a message's FIRST line, the window holds for ttlMinutes, then blocking
|
|
@@ -79,6 +81,12 @@ witness:
|
|
|
79
81
|
# The disciplines you judge by, and the three rungs one climbs — shown as three entries so
|
|
80
82
|
# each rung is a line you can copy. Uncomment to start; ids must stay distinct.
|
|
81
83
|
#
|
|
84
|
+
# Three lists, chosen by what a declaration reads. \`disciplines:\` is judged on both
|
|
85
|
+
# surfaces (the changed file's own pre/post and file sources). \`sessionDisciplines:\` is
|
|
86
|
+
# judged only where a live call is observed before it runs (command line, transcript,
|
|
87
|
+
# actor). \`changeSetDisciplines:\` is judged only over a finished change set
|
|
88
|
+
# (\`--diff\`). The loader refuses an entry written in a list its channels do not fit.
|
|
89
|
+
#
|
|
82
90
|
# disciplines:
|
|
83
91
|
# # A draft: prose only, no predicate. Registered and read, never judged.
|
|
84
92
|
# - id: 'no-todo-in-shipped-code-draft'
|
|
@@ -33,6 +33,18 @@
|
|
|
33
33
|
"uniqueItems": true,
|
|
34
34
|
"items": { "$ref": "#/$defs/discipline" }
|
|
35
35
|
},
|
|
36
|
+
"sessionDisciplines": {
|
|
37
|
+
"description": "Disciplines only the session surface observes — every entry whose declaration binds a transcript, channel, command, or actor evidence channel. Fully identical entries are copy-paste duplicates (by-id uniqueness and the placement rule are enforced by the runtime validator).",
|
|
38
|
+
"type": "array",
|
|
39
|
+
"uniqueItems": true,
|
|
40
|
+
"items": { "$ref": "#/$defs/discipline" }
|
|
41
|
+
},
|
|
42
|
+
"changeSetDisciplines": {
|
|
43
|
+
"description": "Disciplines only the change-set surface observes — every entry whose declaration binds the changes source. Fully identical entries are copy-paste duplicates (by-id uniqueness and the placement rule are enforced by the runtime validator).",
|
|
44
|
+
"type": "array",
|
|
45
|
+
"uniqueItems": true,
|
|
46
|
+
"items": { "$ref": "#/$defs/discipline" }
|
|
47
|
+
},
|
|
36
48
|
"witness": { "$ref": "#/$defs/witness" }
|
|
37
49
|
},
|
|
38
50
|
"$defs": {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The change-set surface's supply body for named file sources — one repo-relative path read from
|
|
3
|
+
* the working tree.
|
|
4
|
+
*/
|
|
5
|
+
import type { SourceReader } from '@polydeukes/core';
|
|
6
|
+
/** {@link worktreeReader} input — the repository the paths are relative to. */
|
|
7
|
+
export type WorktreeReaderSpec = {
|
|
8
|
+
repoRoot: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* A reader over the working tree under `repoRoot`, with the same absence table the session
|
|
12
|
+
* surface's disk reader pins in its own package.
|
|
13
|
+
*
|
|
14
|
+
* Symlinks are followed: on disk the observable fact IS the target's text, and the session
|
|
15
|
+
* surface reads the same way. A path the tree holds but cannot give as judgeable text —
|
|
16
|
+
* a directory, or bytes carrying a NUL — answers `undefined`, which the declaration's
|
|
17
|
+
* `supply` policy disposes of.
|
|
18
|
+
*/
|
|
19
|
+
export declare function worktreeReader(spec: WorktreeReaderSpec): SourceReader;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The change-set surface's supply body for named file sources — one repo-relative path read from
|
|
3
|
+
* the working tree.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
/**
|
|
8
|
+
* A reader over the working tree under `repoRoot`, with the same absence table the session
|
|
9
|
+
* surface's disk reader pins in its own package.
|
|
10
|
+
*
|
|
11
|
+
* Symlinks are followed: on disk the observable fact IS the target's text, and the session
|
|
12
|
+
* surface reads the same way. A path the tree holds but cannot give as judgeable text —
|
|
13
|
+
* a directory, or bytes carrying a NUL — answers `undefined`, which the declaration's
|
|
14
|
+
* `supply` policy disposes of.
|
|
15
|
+
*/
|
|
16
|
+
export function worktreeReader(spec) {
|
|
17
|
+
return (path) => {
|
|
18
|
+
let bytes;
|
|
19
|
+
try {
|
|
20
|
+
bytes = readFileSync(join(spec.repoRoot, path));
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
const { code } = error;
|
|
24
|
+
if (code === 'ENOENT' || code === 'EISDIR' || code === 'ENOTDIR')
|
|
25
|
+
return undefined;
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
return bytes.includes(0) ? undefined : bytes.toString('utf-8');
|
|
29
|
+
};
|
|
30
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polydeukes",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A development discipline framework for building alongside an AI coding partner — deterministic covenants, a verifiable work ledger, local memory, and adversarial verification.
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "A development discipline framework for building alongside an AI coding partner — deterministic covenants, a verifiable work ledger, local memory, and adversarial verification. Beta.",
|
|
5
|
+
"author": "huskyhoochu <dfg1499@gmail.com>",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"harness",
|
|
7
8
|
"guard",
|
|
@@ -28,18 +29,7 @@
|
|
|
28
29
|
"pdks": "./dist/bin.js",
|
|
29
30
|
"polydeukes": "./dist/bin.js"
|
|
30
31
|
},
|
|
31
|
-
"main": "./dist/index.js",
|
|
32
|
-
"module": "./dist/index.js",
|
|
33
|
-
"types": "./dist/index.d.ts",
|
|
34
32
|
"exports": {
|
|
35
|
-
".": {
|
|
36
|
-
"types": "./dist/index.d.ts",
|
|
37
|
-
"import": "./dist/index.js"
|
|
38
|
-
},
|
|
39
|
-
"./claude-code": {
|
|
40
|
-
"types": "./dist/claude-code.d.ts",
|
|
41
|
-
"import": "./dist/claude-code.js"
|
|
42
|
-
},
|
|
43
33
|
"./schema.json": "./dist/schema/polydeukes.schema.json"
|
|
44
34
|
},
|
|
45
35
|
"files": [
|
|
@@ -54,15 +44,12 @@
|
|
|
54
44
|
},
|
|
55
45
|
"dependencies": {
|
|
56
46
|
"yaml": "2.9.0",
|
|
57
|
-
"@polydeukes/
|
|
58
|
-
"@polydeukes/core": "^0.6.1",
|
|
59
|
-
"@polydeukes/covenant": "^0.6.1",
|
|
60
|
-
"@polydeukes/adapter-git": "^0.6.1"
|
|
47
|
+
"@polydeukes/core": "^0.8.0"
|
|
61
48
|
},
|
|
62
49
|
"devDependencies": {
|
|
63
50
|
"@types/node": "^24.0.0",
|
|
64
51
|
"typescript": "7.0.2",
|
|
65
|
-
"vitest": "^
|
|
52
|
+
"vitest": "^5.0.0"
|
|
66
53
|
},
|
|
67
54
|
"scripts": {
|
|
68
55
|
"build": "tsc -p tsconfig.build.json && node scripts/copy-docs.mjs && node scripts/copy-schema.mjs",
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `runClaudeCodeHook` — the assembled session-surface judgment runner.
|
|
3
|
-
*
|
|
4
|
-
* The session counterpart of {@link runCovenantCheck}, and the one place where the Claude
|
|
5
|
-
* Code adapter (tool vocabulary, up-translation) and the covenant package (dispatcher +
|
|
6
|
-
* judge bodies) meet. Packages stay one-way — each depends only on core — so their
|
|
7
|
-
* composition lives here, in the umbrella, and the repository's PreToolUse hook shrinks to
|
|
8
|
-
* a delegator that calls this function. That is what makes the session surface installable:
|
|
9
|
-
* a consumer registers a hook that resolves this package instead of copying assembly.
|
|
10
|
-
*
|
|
11
|
-
* The protection-policy data (protectedPaths / disciplines / witness) is read from the root
|
|
12
|
-
* data config through {@link loadConfig}, which also attaches the config file to its own
|
|
13
|
-
* surface.
|
|
14
|
-
*
|
|
15
|
-
* The valve is the TTL witness, judged over the JSONL transcript provider. The judge body
|
|
16
|
-
* always spawns, and only an outcome that translated to blocked consults the witness —
|
|
17
|
-
* `witnessed` rows are would-block only. Its defence is provenance rather than secrecy: only
|
|
18
|
-
* a real human utterance carries the transcript marking `findUserMessages()` admits.
|
|
19
|
-
*
|
|
20
|
-
* fail-closed: ANY failure — an unbuilt judge body, an unreadable stdin, a missing or
|
|
21
|
-
* invalid config file — resolves to `{ exitCode: 2 }` with one `blocked` record under the
|
|
22
|
-
* `hook` label. Nothing throws: an uncaught rejection would exit the delegator non-blocking,
|
|
23
|
-
* the cheapest bypass vector there is. Recovery from an unbuilt clone is `pnpm build` (it
|
|
24
|
-
* mentions no protected path, so it is never blocked).
|
|
25
|
-
*/
|
|
26
|
-
import { type CanonicalTranscript } from '@polydeukes/core';
|
|
27
|
-
import { type CovenantRegistration } from '@polydeukes/covenant';
|
|
28
|
-
import { type CovenantModule } from './covenant-module.ts';
|
|
29
|
-
import { loadConfig } from './load-config.ts';
|
|
30
|
-
/** {@link runClaudeCodeHook} result — the exit code the hook process leaves with. */
|
|
31
|
-
export type ClaudeCodeHookOutcome = {
|
|
32
|
-
exitCode: 0 | 2;
|
|
33
|
-
};
|
|
34
|
-
/** `runClaudeCodeHook` input — the `CovenantCheckSpec` shape, session side. */
|
|
35
|
-
export type ClaudeCodeHookSpec = {
|
|
36
|
-
/** Repository root — config discovery and discipline glob scoping both anchor here. */
|
|
37
|
-
repoRoot: string;
|
|
38
|
-
/** One raw PreToolUse payload. ABSENT means read fd 0 (the hook's real stdin). */
|
|
39
|
-
rawPayload?: string;
|
|
40
|
-
/** Overrides the config's telemetry log path (tests and assembly injection). */
|
|
41
|
-
telemetryPath?: string;
|
|
42
|
-
/** Overrides the resolved covenant dist directory (tests and assembly injection). */
|
|
43
|
-
covenantDist?: string;
|
|
44
|
-
};
|
|
45
|
-
/** {@link assembleSessionRegistrations} input — what the session surface's assembly needs. */
|
|
46
|
-
export type SessionAssemblySpec = {
|
|
47
|
-
config: ReturnType<typeof loadConfig>['config'];
|
|
48
|
-
rootDir: string;
|
|
49
|
-
/**
|
|
50
|
-
* The covenant surface the registrations are built from — the module the caller loaded
|
|
51
|
-
* from the resolved dist, so what judges a call is what that dist carries, and what
|
|
52
|
-
* `explain` renders is what would judge it.
|
|
53
|
-
*/
|
|
54
|
-
covenant: CovenantModule;
|
|
55
|
-
/** The payload's transcript path. ABSENT leaves the transcript-mod registration out. */
|
|
56
|
-
transcriptPath?: string;
|
|
57
|
-
transcript?: CanonicalTranscript;
|
|
58
|
-
witness?: CovenantRegistration['witness'];
|
|
59
|
-
};
|
|
60
|
-
/**
|
|
61
|
-
* The session surface's registration set. One assembly, two consumers: the runner below
|
|
62
|
-
* dispatches it, `explain` renders it — so what a reader is shown is the table the judgment
|
|
63
|
-
* actually uses, never a second opinion about it.
|
|
64
|
-
*/
|
|
65
|
-
export declare function assembleSessionRegistrations(spec: SessionAssemblySpec): CovenantRegistration[];
|
|
66
|
-
/**
|
|
67
|
-
* The session-surface entry point: the post-hoc state comparison wrapped around the judgment.
|
|
68
|
-
*
|
|
69
|
-
* The comparison sits OUTSIDE {@link judgeHookCall}'s fail-closed try on both ends. Inside
|
|
70
|
-
* it, a comparison failure would become a blocked call — the opposite of a mechanism whose
|
|
71
|
-
* whole purpose is to record rather than stop — so each side carries its own catch and
|
|
72
|
-
* neither can reach the verdict. Observation is fail-open, the direction
|
|
73
|
-
* `appendRecordFailOpen` already established: the worst outcome is a missing datum.
|
|
74
|
-
*
|
|
75
|
-
* Order is the contract. The comparison runs first, so it reads the window the previous call
|
|
76
|
-
* left and its rows land ahead of this call's judgment; the re-establishment runs last, so
|
|
77
|
-
* this call's own judged writes are folded in rather than alarmed on next time.
|
|
78
|
-
*/
|
|
79
|
-
export declare function runClaudeCodeHook(spec: ClaudeCodeHookSpec): Promise<ClaudeCodeHookOutcome>;
|