polydeukes 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +30 -70
- package/README.md +31 -84
- package/dist/baseline.d.ts +37 -1
- package/dist/baseline.js +68 -1
- package/dist/bin.d.ts +3 -4
- package/dist/bin.js +79 -93
- package/dist/covenant/bash-line.d.ts +130 -0
- package/dist/covenant/bash-line.js +566 -0
- package/dist/covenant/declaration-engine.d.ts +108 -0
- package/dist/covenant/declaration-engine.js +415 -0
- package/dist/covenant/discipline.d.ts +103 -0
- package/dist/covenant/discipline.js +667 -0
- package/dist/covenant/dispatch.d.ts +153 -0
- package/dist/covenant/dispatch.js +237 -0
- package/dist/covenant/extract-steps.d.ts +122 -0
- package/dist/covenant/extract-steps.js +0 -0
- package/dist/covenant/mention.d.ts +117 -0
- package/dist/covenant/mention.js +209 -0
- package/dist/covenant/module.d.ts +25 -0
- package/dist/covenant/module.js +23 -0
- package/dist/covenant/mutation-rules.d.ts +40 -0
- package/dist/covenant/mutation-rules.js +146 -0
- package/dist/covenant/relations.d.ts +46 -0
- package/dist/covenant/relations.js +68 -0
- package/dist/covenant/run-covenant.d.ts +95 -0
- package/dist/covenant/run-covenant.js +164 -0
- package/dist/covenant/self-mod.d.ts +55 -0
- package/dist/covenant/self-mod.js +84 -0
- package/dist/covenant/shell-evidence.d.ts +38 -0
- package/dist/covenant/shell-evidence.js +247 -0
- package/dist/covenant/shell-mod.d.ts +84 -0
- package/dist/covenant/shell-mod.js +213 -0
- package/dist/covenant/supply.d.ts +54 -0
- package/dist/covenant/supply.js +50 -0
- package/dist/covenant/transcript-mod.d.ts +60 -0
- package/dist/covenant/transcript-mod.js +254 -0
- package/dist/covenant/ttl-witness.d.ts +47 -0
- package/dist/covenant/ttl-witness.js +80 -0
- package/dist/covenant-check.d.ts +72 -39
- package/dist/covenant-check.js +260 -121
- package/dist/diff-ir.d.ts +28 -0
- package/dist/diff-ir.js +310 -0
- package/dist/docs/README.ko.md +60 -0
- package/dist/docs/README.md +64 -0
- package/dist/docs/catalog.json +480 -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 +98 -0
- package/dist/docs/how-to/connect-surfaces.ko.md +120 -0
- package/dist/docs/how-to/connect-surfaces.md +121 -0
- package/dist/docs/how-to/write-disciplines.ko.md +179 -0
- package/dist/docs/how-to/write-disciplines.md +181 -0
- package/dist/docs/index.json +2280 -0
- package/dist/docs/reference/cli/covenant-check.ko.md +148 -0
- package/dist/docs/reference/cli/covenant-check.md +153 -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 +84 -0
- package/dist/docs/reference/cli/explain.md +89 -0
- package/dist/docs/reference/cli/init.ko.md +119 -0
- package/dist/docs/reference/cli/init.md +119 -0
- package/dist/docs/reference/configuration/index.ko.md +553 -0
- package/dist/docs/reference/{configuration.md → configuration/index.md} +215 -90
- package/dist/docs/reference/packages/adapter-claude-code.ko.md +110 -0
- package/dist/docs/reference/{adapter-claude-code.md → packages/adapter-claude-code.md} +47 -19
- package/dist/docs/reference/packages/adapter-grok.ko.md +77 -0
- package/dist/docs/reference/packages/adapter-grok.md +79 -0
- package/dist/docs/reference/packages/core.ko.md +174 -0
- package/dist/docs/reference/{core.md → packages/core.md} +74 -12
- package/dist/docs/reference/packages/polydeukes.ko.md +195 -0
- package/dist/docs/reference/packages/polydeukes.md +204 -0
- package/dist/docs/reference/packages/sdk-ts.ko.md +161 -0
- package/dist/docs/reference/packages/sdk-ts.md +164 -0
- package/dist/docs/troubleshooting.ko.md +144 -0
- package/dist/docs/troubleshooting.md +103 -151
- 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 +1 -1
- package/dist/explain.js +37 -22
- package/dist/load-config.d.ts +1 -1
- package/dist/load-config.js +1 -1
- package/dist/pre-state-reader.d.ts +1 -1
- package/dist/pre-state-reader.js +1 -1
- package/dist/scaffold-project.d.ts +2 -2
- package/dist/scaffold-project.js +9 -3
- package/dist/schema/polydeukes.schema.json +12 -0
- package/dist/worktree-reader.d.ts +19 -0
- package/dist/worktree-reader.js +30 -0
- package/package.json +3 -17
- package/dist/claude-code-hook.d.ts +0 -79
- package/dist/claude-code-hook.js +0 -372
- package/dist/claude-code.d.ts +0 -6
- package/dist/claude-code.js +0 -6
- package/dist/covenant-module.d.ts +0 -25
- package/dist/covenant-module.js +0 -42
- package/dist/docs/configuration.md +0 -103
- package/dist/docs/installation.md +0 -241
- package/dist/docs/reference/adapter-git.md +0 -100
- package/dist/docs/reference/covenant.md +0 -116
- package/dist/docs/reference/polydeukes.md +0 -315
- 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 -469
- package/dist/init-grok.d.ts +0 -51
- package/dist/init-grok.js +0 -242
package/dist/docs-query.js
CHANGED
|
@@ -1,138 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* indistinguishable from a real one by the time it reaches a reader.
|
|
11
|
-
*
|
|
12
|
-
* Every failure throws so the bin can leave stdout at zero bytes and exit 2. Text written
|
|
13
|
-
* halfway is read as the document and quoted as the document — the same direction the
|
|
14
|
-
* judging surface fails in, for the same reason.
|
|
15
|
-
*/
|
|
16
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
17
|
-
import { join } from 'node:path';
|
|
18
|
-
/** The finite query domain — the topic list `pdks docs` prints with no argument. */
|
|
19
|
-
export const TOPICS = ['install', 'config', 'discipline', 'covenant', 'witness'];
|
|
20
|
-
/** The mapping, as data: which document answers a topic, and what to read next. */
|
|
21
|
-
const TOPIC_MAP = {
|
|
22
|
-
install: {
|
|
23
|
-
sections: [{ file: 'installation.md' }],
|
|
24
|
-
seeAlso: 'reference/polydeukes.md',
|
|
25
|
-
},
|
|
26
|
-
config: {
|
|
27
|
-
sections: [{ file: 'reference/configuration.md' }],
|
|
28
|
-
seeAlso: 'reference/core.md',
|
|
29
|
-
},
|
|
30
|
-
discipline: {
|
|
31
|
-
sections: [{ file: 'reference/configuration.md', heading: '## `disciplines`' }],
|
|
32
|
-
seeAlso: 'reference/covenant.md',
|
|
33
|
-
},
|
|
34
|
-
covenant: {
|
|
35
|
-
sections: [{ file: 'configuration.md', heading: '## What enforcement looks like' }],
|
|
36
|
-
seeAlso: 'reference/polydeukes.md',
|
|
37
|
-
},
|
|
38
|
-
witness: {
|
|
39
|
-
sections: [
|
|
40
|
-
{ file: 'reference/configuration.md', heading: '## `witness`' },
|
|
41
|
-
{ file: 'troubleshooting.md', heading: '## Opening a blocked call — the witness' },
|
|
42
|
-
],
|
|
43
|
-
seeAlso: 'reference/covenant.md',
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
/** A fenced block opens and closes on a line whose trimmed form starts with the marker. */
|
|
47
|
-
const FENCE = /^(?:`{3,}|~{3,})/;
|
|
48
|
-
/** An ATX heading, and its level in the capture. */
|
|
49
|
-
const HEADING = /^(#{1,6}) /;
|
|
50
|
-
function isTopic(value) {
|
|
51
|
-
return TOPICS.includes(value);
|
|
52
|
-
}
|
|
53
|
-
function headingLevel(line) {
|
|
54
|
-
return HEADING.exec(line)?.[1].length ?? 0;
|
|
1
|
+
import { runDocs } from './docs-library.js';
|
|
2
|
+
export { DOCS_TOPICS as TOPICS } from './docs-types.js';
|
|
3
|
+
/** Answer a legacy topic using the same catalog as search and show. */
|
|
4
|
+
export function queryDocs(spec) {
|
|
5
|
+
return runDocs({
|
|
6
|
+
docsRoot: spec.docsRoot,
|
|
7
|
+
args: spec.topic === undefined ? [] : [spec.topic],
|
|
8
|
+
version: '',
|
|
9
|
+
});
|
|
55
10
|
}
|
|
56
11
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* `heading` is matched by exact string equality. A document that renames its heading kills
|
|
61
|
-
* the query here rather than letting a normalizing matcher hand back a neighbouring section
|
|
62
|
-
* with full confidence.
|
|
63
|
-
*
|
|
64
|
-
* Both scans — for the start and for the boundary — run outside code fences. `#` lines
|
|
65
|
-
* inside a fence are content: the guides really carry them, and a fence-blind scanner cuts
|
|
66
|
-
* the answer at one of those lines while still looking like a success.
|
|
12
|
+
* Extract a section by its exact heading, retaining the original internal helper's contract.
|
|
13
|
+
* Topic retrieval uses stable IDs instead; this helper remains for existing internal callers.
|
|
67
14
|
*/
|
|
68
15
|
export function extractSection(markdown, heading) {
|
|
69
16
|
const lines = markdown.split('\n');
|
|
70
|
-
const level =
|
|
71
|
-
let
|
|
17
|
+
const level = /^(#{1,6}) /.exec(heading)?.[1].length ?? 0;
|
|
18
|
+
let fence;
|
|
72
19
|
let start = -1;
|
|
73
|
-
for (let
|
|
74
|
-
const line = lines[
|
|
75
|
-
const
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
openMarker = trimmed[0];
|
|
82
|
-
}
|
|
83
|
-
else if (trimmed[0] === openMarker) {
|
|
84
|
-
openMarker = undefined;
|
|
85
|
-
}
|
|
20
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
21
|
+
const line = lines[index];
|
|
22
|
+
const marker = /^(`{3,}|~{3,})/.exec(line.trim())?.[1];
|
|
23
|
+
if (marker) {
|
|
24
|
+
if (fence === undefined)
|
|
25
|
+
fence = { marker: marker[0], length: marker.length };
|
|
26
|
+
else if (fence.marker === marker[0] && marker.length >= fence.length)
|
|
27
|
+
fence = undefined;
|
|
86
28
|
continue;
|
|
87
29
|
}
|
|
88
|
-
if (
|
|
30
|
+
if (fence !== undefined)
|
|
89
31
|
continue;
|
|
90
|
-
}
|
|
91
32
|
if (start === -1) {
|
|
92
|
-
if (line === heading)
|
|
93
|
-
start =
|
|
94
|
-
}
|
|
33
|
+
if (line === heading)
|
|
34
|
+
start = index;
|
|
95
35
|
continue;
|
|
96
36
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
37
|
+
const nextLevel = /^(#{1,6}) /.exec(line)?.[1].length ?? 0;
|
|
38
|
+
if (nextLevel > 0 && nextLevel <= level)
|
|
39
|
+
return lines.slice(start, index).join('\n');
|
|
100
40
|
}
|
|
101
|
-
if (start === -1)
|
|
41
|
+
if (start === -1)
|
|
102
42
|
throw new Error(`heading not found: ${heading}`);
|
|
103
|
-
}
|
|
104
|
-
// A section that closes the document ends at end of file; the topic map points at one.
|
|
105
43
|
return lines.slice(start).join('\n');
|
|
106
44
|
}
|
|
107
|
-
function readSection(docsRoot, section) {
|
|
108
|
-
const path = join(docsRoot, section.file);
|
|
109
|
-
if (!existsSync(path)) {
|
|
110
|
-
// Named, never swallowed into empty text: a silently incomplete bundle would otherwise
|
|
111
|
-
// reach a reader as the document itself.
|
|
112
|
-
throw new Error(`bundled document missing: ${section.file}`);
|
|
113
|
-
}
|
|
114
|
-
const markdown = readFileSync(path, 'utf-8');
|
|
115
|
-
return section.heading === undefined ? markdown : extractSection(markdown, section.heading);
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Answer one documentation query.
|
|
119
|
-
*
|
|
120
|
-
* With no topic the result is the listing — how an AI discovers what it may ask at all.
|
|
121
|
-
* With one, it is the mapped section body followed by the bundled reference to read next.
|
|
122
|
-
*/
|
|
123
|
-
export function queryDocs(spec) {
|
|
124
|
-
if (spec.topic === undefined) {
|
|
125
|
-
return { text: `Polydeukes docs:\n${TOPICS.map((t) => ` pdks docs ${t}`).join('\n')}\n` };
|
|
126
|
-
}
|
|
127
|
-
if (!isTopic(spec.topic)) {
|
|
128
|
-
throw new Error(`unknown docs topic '${spec.topic}' — known topics: ${TOPICS.join(', ')}`);
|
|
129
|
-
}
|
|
130
|
-
const entry = TOPIC_MAP[spec.topic];
|
|
131
|
-
const body = entry.sections.map((section) => readSection(spec.docsRoot, section)).join('\n');
|
|
132
|
-
// Resolved against the bundle, not printed as the bare relative name. A reader given
|
|
133
|
-
// `reference/core.md` has to guess where the bundle lives before it can open anything, and
|
|
134
|
-
// this line is the only way most of the reference layer is reached at all. A path a file-read
|
|
135
|
-
// tool can take is the difference between a pointer and a dead end, and a dead end sends the
|
|
136
|
-
// reader back to the web search this command replaces.
|
|
137
|
-
return { text: `${body}\nSee also: ${join(spec.docsRoot, entry.seeAlso)}\n` };
|
|
138
|
-
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/** Languages included in every bundled document. */
|
|
2
|
+
export type DocsLanguage = 'en' | 'ko';
|
|
3
|
+
/** Backward-compatible topic names; their targets belong to the catalog. */
|
|
4
|
+
export declare const DOCS_TOPICS: readonly ['install', 'config', 'discipline', 'covenant', 'witness'];
|
|
5
|
+
/** Source-root-relative Markdown path and localized metadata used for retrieval and ranking. */
|
|
6
|
+
export type DocsTranslation = {
|
|
7
|
+
path: string;
|
|
8
|
+
title: string;
|
|
9
|
+
summary: string;
|
|
10
|
+
terms?: string[];
|
|
11
|
+
};
|
|
12
|
+
/** Catalog entry identifying a document across languages and selecting it for bundling. */
|
|
13
|
+
export type DocsDocument = {
|
|
14
|
+
id: string;
|
|
15
|
+
category: string;
|
|
16
|
+
order: number;
|
|
17
|
+
bundled: boolean;
|
|
18
|
+
en: DocsTranslation;
|
|
19
|
+
ko?: DocsTranslation;
|
|
20
|
+
};
|
|
21
|
+
/** Source inventory shared by the documentation build and offline query commands. */
|
|
22
|
+
export type DocsCatalog = {
|
|
23
|
+
schemaVersion: number;
|
|
24
|
+
documents: DocsDocument[];
|
|
25
|
+
topics: Record<string, DocsTopic>;
|
|
26
|
+
redirects?: DocsRedirect[];
|
|
27
|
+
};
|
|
28
|
+
/** Ordered content references for a legacy topic, plus a document ID for further reading. */
|
|
29
|
+
export type DocsTopic = {
|
|
30
|
+
references: DocsReference[];
|
|
31
|
+
seeAlso: string;
|
|
32
|
+
};
|
|
33
|
+
/** Stable document ID, optionally narrowed to a section; omission selects the whole document. */
|
|
34
|
+
export type DocsReference = {
|
|
35
|
+
documentId: string;
|
|
36
|
+
sectionId?: string;
|
|
37
|
+
};
|
|
38
|
+
/** Move-notice path and canonical destination, both relative to the documentation root. */
|
|
39
|
+
export type DocsRedirect = {
|
|
40
|
+
path: string;
|
|
41
|
+
target: string;
|
|
42
|
+
};
|
|
43
|
+
/** Anchored Markdown slice with zero-based line bounds: inclusive start, exclusive end. */
|
|
44
|
+
export type DocsSection = {
|
|
45
|
+
id: string;
|
|
46
|
+
title: string;
|
|
47
|
+
level: number;
|
|
48
|
+
startLine: number;
|
|
49
|
+
endLine: number;
|
|
50
|
+
text: string;
|
|
51
|
+
};
|
|
52
|
+
/** Bilingual metadata returned for a document included in a completed build. */
|
|
53
|
+
export type DocsBundleDocument = {
|
|
54
|
+
id: string;
|
|
55
|
+
bundled: boolean;
|
|
56
|
+
category: string;
|
|
57
|
+
order: number;
|
|
58
|
+
translations: Record<DocsLanguage, DocsTranslation>;
|
|
59
|
+
};
|
|
60
|
+
/** Indexed section location; the hash covers its entire translated document, not the section. */
|
|
61
|
+
export type DocsBundleSection = {
|
|
62
|
+
documentId: string;
|
|
63
|
+
language: DocsLanguage;
|
|
64
|
+
sectionId: string;
|
|
65
|
+
title: string;
|
|
66
|
+
level: number;
|
|
67
|
+
path: string;
|
|
68
|
+
hash: string;
|
|
69
|
+
};
|
|
70
|
+
/** Persisted bundle metadata and document hashes, reconstructed on load to detect inconsistency. */
|
|
71
|
+
export type DocsIndex = {
|
|
72
|
+
schemaVersion: 1;
|
|
73
|
+
documents: Array<{
|
|
74
|
+
id: string;
|
|
75
|
+
bundled: boolean;
|
|
76
|
+
category: string;
|
|
77
|
+
order: number;
|
|
78
|
+
translations: Record<DocsLanguage, DocsTranslation>;
|
|
79
|
+
hashes: Record<DocsLanguage, string>;
|
|
80
|
+
}>;
|
|
81
|
+
sections: DocsBundleSection[];
|
|
82
|
+
};
|
|
83
|
+
/** In-memory catalog and index with loaded Markdown keyed by stable document ID. */
|
|
84
|
+
export type LoadedDocsBundle = {
|
|
85
|
+
catalog: DocsCatalog;
|
|
86
|
+
index: DocsIndex;
|
|
87
|
+
documents: Map<string, LoadedDocsDocument>;
|
|
88
|
+
topics: Record<string, DocsTopic>;
|
|
89
|
+
};
|
|
90
|
+
/** Loaded bilingual Markdown with parsed sections and empty search-term lists where omitted. */
|
|
91
|
+
export type LoadedDocsDocument = {
|
|
92
|
+
id: string;
|
|
93
|
+
bundled: boolean;
|
|
94
|
+
category: string;
|
|
95
|
+
order: number;
|
|
96
|
+
translations: Record<DocsLanguage, {
|
|
97
|
+
path: string;
|
|
98
|
+
title: string;
|
|
99
|
+
summary: string;
|
|
100
|
+
terms: string[];
|
|
101
|
+
markdown: string;
|
|
102
|
+
hash: string;
|
|
103
|
+
sections: DocsSection[];
|
|
104
|
+
}>;
|
|
105
|
+
};
|
package/dist/explain.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type ExplainSpec = {
|
|
|
17
17
|
* Read the config at `repoRoot`, assemble both surfaces, and render them.
|
|
18
18
|
*
|
|
19
19
|
* The session assembly is given a transcript path, so its `transcript-mod` registration
|
|
20
|
-
* exists here exactly as it does under a normal
|
|
20
|
+
* exists here exactly as it does under a normal call payload — the path is never read,
|
|
21
21
|
* because the injected transcript is the no-op one.
|
|
22
22
|
*/
|
|
23
23
|
export declare function explain(spec: ExplainSpec): Promise<{
|
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.
|
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.
|
|
@@ -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.
|
|
@@ -79,6 +79,12 @@ witness:
|
|
|
79
79
|
# The disciplines you judge by, and the three rungs one climbs — shown as three entries so
|
|
80
80
|
# each rung is a line you can copy. Uncomment to start; ids must stay distinct.
|
|
81
81
|
#
|
|
82
|
+
# Three lists, chosen by what a declaration reads. \`disciplines:\` is judged on both
|
|
83
|
+
# surfaces (the changed file's own pre/post and file sources). \`sessionDisciplines:\` is
|
|
84
|
+
# judged only where a live call is observed before it runs (command line, transcript,
|
|
85
|
+
# actor). \`changeSetDisciplines:\` is judged only over a finished change set
|
|
86
|
+
# (\`--diff\`). The loader refuses an entry written in a list its channels do not fit.
|
|
87
|
+
#
|
|
82
88
|
# disciplines:
|
|
83
89
|
# # A draft: prose only, no predicate. Registered and read, never judged.
|
|
84
90
|
# - 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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polydeukes",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "A development discipline framework for building alongside an AI coding partner — deterministic covenants, a verifiable work ledger, local memory, and adversarial verification. Alpha.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"harness",
|
|
@@ -28,18 +28,7 @@
|
|
|
28
28
|
"pdks": "./dist/bin.js",
|
|
29
29
|
"polydeukes": "./dist/bin.js"
|
|
30
30
|
},
|
|
31
|
-
"main": "./dist/index.js",
|
|
32
|
-
"module": "./dist/index.js",
|
|
33
|
-
"types": "./dist/index.d.ts",
|
|
34
31
|
"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
32
|
"./schema.json": "./dist/schema/polydeukes.schema.json"
|
|
44
33
|
},
|
|
45
34
|
"files": [
|
|
@@ -54,15 +43,12 @@
|
|
|
54
43
|
},
|
|
55
44
|
"dependencies": {
|
|
56
45
|
"yaml": "2.9.0",
|
|
57
|
-
"@polydeukes/
|
|
58
|
-
"@polydeukes/covenant": "^0.6.0",
|
|
59
|
-
"@polydeukes/core": "^0.6.0",
|
|
60
|
-
"@polydeukes/adapter-claude-code": "^0.6.0"
|
|
46
|
+
"@polydeukes/core": "^0.7.1"
|
|
61
47
|
},
|
|
62
48
|
"devDependencies": {
|
|
63
49
|
"@types/node": "^24.0.0",
|
|
64
50
|
"typescript": "7.0.2",
|
|
65
|
-
"vitest": "^
|
|
51
|
+
"vitest": "^5.0.0"
|
|
66
52
|
},
|
|
67
53
|
"scripts": {
|
|
68
54
|
"build": "tsc -p tsconfig.build.json && node scripts/copy-docs.mjs && node scripts/copy-schema.mjs",
|