@polydeukes/core 0.4.0 → 0.6.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 +2 -1
- package/README.md +2 -1
- package/dist/algebra.d.ts +174 -0
- package/dist/algebra.js +438 -0
- package/dist/catalogue.d.ts +69 -0
- package/dist/catalogue.js +191 -0
- package/dist/config.d.ts +67 -67
- package/dist/config.js +85 -174
- package/dist/exit-codes.d.ts +10 -10
- package/dist/exit-codes.js +10 -10
- package/dist/fail-policy.d.ts +7 -7
- package/dist/fail-policy.js +7 -7
- package/dist/index.d.ts +13 -99
- package/dist/index.js +7 -57
- package/dist/is-plain-object.d.ts +4 -1
- package/dist/is-plain-object.js +4 -1
- package/dist/protected-paths.d.ts +3 -3
- package/dist/protected-paths.js +3 -3
- package/dist/protocol.d.ts +152 -0
- package/dist/protocol.js +121 -0
- package/dist/source-names.d.ts +10 -0
- package/dist/source-names.js +18 -0
- package/dist/telemetry.d.ts +46 -21
- package/dist/telemetry.js +79 -30
- package/dist/transcript.d.ts +18 -28
- package/dist/transcript.js +12 -20
- package/dist/validation.d.ts +24 -0
- package/dist/validation.js +44 -0
- package/package.json +3 -2
- package/schema/algebra-declaration.schema.json +402 -0
- package/schema/polydeukes.schema.json +54 -81
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `catalogue.ts` — the eighteen judgment-mechanism names and the shape each one admits.
|
|
3
|
+
*
|
|
4
|
+
* A mechanism name is a coordinate the machine checks, not a label: {@link deriveShape}
|
|
5
|
+
* reads a declaration's shape from its syntax alone — which sources its `source` steps
|
|
6
|
+
* name, which relations its body relates, whether it carries a witness block — and
|
|
7
|
+
* {@link validateMechanism} refuses a declaration whose derived shape falls outside the
|
|
8
|
+
* spec of the name it carries. Nothing here runs an extraction or opens a world.
|
|
9
|
+
*/
|
|
10
|
+
import type { AlgebraDeclaration, RelationName } from './algebra.ts';
|
|
11
|
+
/** The four axes a declaration can read, closed. This tuple is the single source of the list. */
|
|
12
|
+
export declare const AXIS_NAMES: readonly ['change', 'actor', 'world', 'history'];
|
|
13
|
+
/** One of the four axes — the closed vocabulary of the axis position. */
|
|
14
|
+
export type Axis = (typeof AXIS_NAMES)[number];
|
|
15
|
+
/** The judgment mechanisms, closed. A name outside this tuple is refused, never coerced. */
|
|
16
|
+
export declare const MECHANISM_NAMES: readonly ['pairing', 'companion', 'monotonic-order', 'fingerprint-sync', 'producer-owned', 'self-absolution-ban', 'actor-scope', 'precedent', 'phase-order', 'turn-locality', 'stated-ground', 'controlled-vocabulary', 'naming', 'added-only', 'one-way-marker', 'delegated-scope', 'scoped-valve', 'forbidden-command'];
|
|
17
|
+
/** One of the eighteen mechanism names. */
|
|
18
|
+
export type MechanismName = (typeof MECHANISM_NAMES)[number];
|
|
19
|
+
/**
|
|
20
|
+
* What one mechanism name admits: the axes it may read, the relations it may relate, and
|
|
21
|
+
* the structural markers it requires.
|
|
22
|
+
*
|
|
23
|
+
* `requiresWitness` asks for the valve block; `scopeSource` pins what the declaration may
|
|
24
|
+
* scope on; `reserved` names what will define the name, and stands in place of a
|
|
25
|
+
* shape — a reserved name admits no declaration at all.
|
|
26
|
+
*/
|
|
27
|
+
export type MechanismShape = {
|
|
28
|
+
axes: ReadonlySet<Axis>;
|
|
29
|
+
relations: ReadonlySet<RelationName>;
|
|
30
|
+
requiresWitness?: true;
|
|
31
|
+
scopeSource?: 'target.path' | 'command';
|
|
32
|
+
reserved?: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* The blocks {@link deriveShape} reads. A declaration names itself with its `discipline`,
|
|
36
|
+
* and an entry under `declare` names itself with the entry id instead — the shape is the
|
|
37
|
+
* same either way, so the derivation takes the blocks rather than the whole document.
|
|
38
|
+
*/
|
|
39
|
+
export type DerivableDeclaration = Pick<AlgebraDeclaration, 'extract' | 'relate' | 'sources' | 'witness'>;
|
|
40
|
+
/** The derived shape of one declaration: what its syntax says it reads and relates. */
|
|
41
|
+
export type DerivedShape = {
|
|
42
|
+
axes: ReadonlySet<Axis>;
|
|
43
|
+
relations: ReadonlySet<RelationName>;
|
|
44
|
+
witness: boolean;
|
|
45
|
+
};
|
|
46
|
+
/** Every name's spec. The `Record` type pins the keys to {@link MECHANISM_NAMES}. */
|
|
47
|
+
export declare const MECHANISM_SHAPES: Record<MechanismName, MechanismShape>;
|
|
48
|
+
/**
|
|
49
|
+
* Read one declaration's shape from its syntax (pure).
|
|
50
|
+
*
|
|
51
|
+
* The axis of a source name is where the name comes from: the fixed name `actor` is the
|
|
52
|
+
* actor axis and the other six fixed names the change axis, a name the declaration's own
|
|
53
|
+
* `sources` block binds is the world axis unless the binding is of the transcript kind,
|
|
54
|
+
* which is the history axis. A name that is neither is refused by
|
|
55
|
+
* {@link validateMechanism} — skipping it would derive the empty set, which is a subset of
|
|
56
|
+
* every spec, and an axis-restricted name would load on a typo. The witness block's
|
|
57
|
+
* `extract` reads a world too, so its source steps count; its `relate` does not, because the
|
|
58
|
+
* valve's relation is not the judgment's.
|
|
59
|
+
*/
|
|
60
|
+
export declare function deriveShape(declaration: DerivableDeclaration): DerivedShape;
|
|
61
|
+
/**
|
|
62
|
+
* Check the declaration's `mechanism` against the catalogue (throws on a mismatch).
|
|
63
|
+
*
|
|
64
|
+
* The order is the author's repair order: an unknown name first (nothing else is
|
|
65
|
+
* meaningful without a spec), then the reserved name, then the structural markers, then
|
|
66
|
+
* the axes and relations the derived shape must stay inside. Membership is subset, not
|
|
67
|
+
* equality — a name admitting two relations accepts a declaration using one of them.
|
|
68
|
+
*/
|
|
69
|
+
export declare function validateMechanism(declaration: AlgebraDeclaration, location: string): void;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `catalogue.ts` — the eighteen judgment-mechanism names and the shape each one admits.
|
|
3
|
+
*
|
|
4
|
+
* A mechanism name is a coordinate the machine checks, not a label: {@link deriveShape}
|
|
5
|
+
* reads a declaration's shape from its syntax alone — which sources its `source` steps
|
|
6
|
+
* name, which relations its body relates, whether it carries a witness block — and
|
|
7
|
+
* {@link validateMechanism} refuses a declaration whose derived shape falls outside the
|
|
8
|
+
* spec of the name it carries. Nothing here runs an extraction or opens a world.
|
|
9
|
+
*/
|
|
10
|
+
import { isPlainObject } from './is-plain-object.js';
|
|
11
|
+
import { FIXED_SOURCE_NAMES } from './source-names.js';
|
|
12
|
+
import { ConfigValidationError } from './validation.js';
|
|
13
|
+
/** The four axes a declaration can read, closed. This tuple is the single source of the list. */
|
|
14
|
+
export const AXIS_NAMES = ['change', 'actor', 'world', 'history'];
|
|
15
|
+
/** The judgment mechanisms, closed. A name outside this tuple is refused, never coerced. */
|
|
16
|
+
export const MECHANISM_NAMES = [
|
|
17
|
+
'pairing',
|
|
18
|
+
'companion',
|
|
19
|
+
'monotonic-order',
|
|
20
|
+
'fingerprint-sync',
|
|
21
|
+
'producer-owned',
|
|
22
|
+
'self-absolution-ban',
|
|
23
|
+
'actor-scope',
|
|
24
|
+
'precedent',
|
|
25
|
+
'phase-order',
|
|
26
|
+
'turn-locality',
|
|
27
|
+
'stated-ground',
|
|
28
|
+
'controlled-vocabulary',
|
|
29
|
+
'naming',
|
|
30
|
+
'added-only',
|
|
31
|
+
'one-way-marker',
|
|
32
|
+
'delegated-scope',
|
|
33
|
+
'scoped-valve',
|
|
34
|
+
'forbidden-command',
|
|
35
|
+
];
|
|
36
|
+
const CHANGE = new Set(['change']);
|
|
37
|
+
const WORLD = new Set(['world']);
|
|
38
|
+
const ACTOR = new Set(['actor']);
|
|
39
|
+
const HISTORY = new Set(['history']);
|
|
40
|
+
const HISTORY_WORLD = new Set(['history', 'world']);
|
|
41
|
+
const CHANGE_WORLD = new Set(['change', 'world']);
|
|
42
|
+
/** The one fixed source name whose value is the actor rather than the change. */
|
|
43
|
+
const ACTOR_SOURCE = 'actor';
|
|
44
|
+
/** Every name's spec. The `Record` type pins the keys to {@link MECHANISM_NAMES}. */
|
|
45
|
+
export const MECHANISM_SHAPES = {
|
|
46
|
+
pairing: { axes: WORLD, relations: new Set(['equal']) },
|
|
47
|
+
companion: { axes: CHANGE_WORLD, relations: new Set(['implies']) },
|
|
48
|
+
'monotonic-order': { axes: CHANGE_WORLD, relations: new Set(['ordered']) },
|
|
49
|
+
'fingerprint-sync': { axes: WORLD, relations: new Set(['equal']) },
|
|
50
|
+
'producer-owned': { axes: ACTOR, relations: new Set(['empty', 'nonEmpty']) },
|
|
51
|
+
'self-absolution-ban': {
|
|
52
|
+
axes: CHANGE,
|
|
53
|
+
relations: new Set(['unchanged', 'empty']),
|
|
54
|
+
},
|
|
55
|
+
'actor-scope': { axes: ACTOR, relations: new Set(['empty', 'nonEmpty']) },
|
|
56
|
+
// The spawn sidecar is a world-axis channel carrying session history, so a precedent
|
|
57
|
+
// read off it is still a precedent; the transcript source carries the history axis.
|
|
58
|
+
precedent: { axes: HISTORY_WORLD, relations: new Set(['nonEmpty']) },
|
|
59
|
+
'phase-order': { axes: HISTORY, relations: new Set(['ordered']) },
|
|
60
|
+
'turn-locality': { axes: HISTORY, relations: new Set(['nonEmpty']) },
|
|
61
|
+
'stated-ground': { axes: HISTORY, relations: new Set(['nonEmpty']) },
|
|
62
|
+
'controlled-vocabulary': { axes: CHANGE_WORLD, relations: new Set(['subset']) },
|
|
63
|
+
naming: {
|
|
64
|
+
axes: CHANGE,
|
|
65
|
+
relations: new Set(['empty', 'nonEmpty']),
|
|
66
|
+
scopeSource: 'target.path',
|
|
67
|
+
},
|
|
68
|
+
'added-only': { axes: CHANGE, relations: new Set(['empty']) },
|
|
69
|
+
'one-way-marker': { axes: CHANGE, relations: new Set(['subset']) },
|
|
70
|
+
'delegated-scope': {
|
|
71
|
+
axes: new Set(),
|
|
72
|
+
relations: new Set(),
|
|
73
|
+
reserved: 'the definition-time evaluator',
|
|
74
|
+
},
|
|
75
|
+
'scoped-valve': {
|
|
76
|
+
axes: new Set(AXIS_NAMES),
|
|
77
|
+
relations: new Set([
|
|
78
|
+
'empty',
|
|
79
|
+
'nonEmpty',
|
|
80
|
+
'equal',
|
|
81
|
+
'subset',
|
|
82
|
+
'implies',
|
|
83
|
+
'ordered',
|
|
84
|
+
'unchanged',
|
|
85
|
+
]),
|
|
86
|
+
requiresWitness: true,
|
|
87
|
+
},
|
|
88
|
+
// A command-line ban scopes on the command it reads: a world with no shell call carries
|
|
89
|
+
// no `command`, and a scope-less reader would refuse every file-changing call as unjudgeable.
|
|
90
|
+
'forbidden-command': {
|
|
91
|
+
axes: CHANGE,
|
|
92
|
+
relations: new Set(['empty']),
|
|
93
|
+
scopeSource: 'command',
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
/** The names a rejection message lists so the author sees what is admitted. */
|
|
97
|
+
function quotedList(names) {
|
|
98
|
+
return names.map((name) => `'${name}'`).join(', ');
|
|
99
|
+
}
|
|
100
|
+
/** Whether one `sources` binding names the session history rather than a world value. */
|
|
101
|
+
function isTranscriptSource(binding) {
|
|
102
|
+
return isPlainObject(binding) && binding.transcript === true;
|
|
103
|
+
}
|
|
104
|
+
/** The source names one extract block's `source` steps name, in declaration order. */
|
|
105
|
+
function sourceNamesOf(extract) {
|
|
106
|
+
if (extract === undefined)
|
|
107
|
+
return [];
|
|
108
|
+
const names = [];
|
|
109
|
+
for (const steps of Object.values(extract)) {
|
|
110
|
+
for (const step of steps) {
|
|
111
|
+
if (isPlainObject(step) && step.op === 'source' && typeof step.of === 'string') {
|
|
112
|
+
names.push(step.of);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return names;
|
|
117
|
+
}
|
|
118
|
+
/** Every source name the body and the valve read, in declaration order. */
|
|
119
|
+
function sourceNames(declaration) {
|
|
120
|
+
return [...sourceNamesOf(declaration.extract), ...sourceNamesOf(declaration.witness?.extract)];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Read one declaration's shape from its syntax (pure).
|
|
124
|
+
*
|
|
125
|
+
* The axis of a source name is where the name comes from: the fixed name `actor` is the
|
|
126
|
+
* actor axis and the other six fixed names the change axis, a name the declaration's own
|
|
127
|
+
* `sources` block binds is the world axis unless the binding is of the transcript kind,
|
|
128
|
+
* which is the history axis. A name that is neither is refused by
|
|
129
|
+
* {@link validateMechanism} — skipping it would derive the empty set, which is a subset of
|
|
130
|
+
* every spec, and an axis-restricted name would load on a typo. The witness block's
|
|
131
|
+
* `extract` reads a world too, so its source steps count; its `relate` does not, because the
|
|
132
|
+
* valve's relation is not the judgment's.
|
|
133
|
+
*/
|
|
134
|
+
export function deriveShape(declaration) {
|
|
135
|
+
const bindings = declaration.sources ?? {};
|
|
136
|
+
const axes = new Set();
|
|
137
|
+
for (const name of sourceNames(declaration)) {
|
|
138
|
+
if (FIXED_SOURCE_NAMES.includes(name)) {
|
|
139
|
+
axes.add(name === ACTOR_SOURCE ? 'actor' : 'change');
|
|
140
|
+
}
|
|
141
|
+
else if (name in bindings) {
|
|
142
|
+
axes.add(isTranscriptSource(bindings[name]) ? 'history' : 'world');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const relations = new Set(declaration.relate.map((entry) => entry.relation.op));
|
|
146
|
+
return { axes, relations, witness: declaration.witness !== undefined };
|
|
147
|
+
}
|
|
148
|
+
/** Refuse a `source` step naming neither a fixed source nor a binding of this declaration. */
|
|
149
|
+
function checkSourceNames(declaration, location) {
|
|
150
|
+
const declared = new Set(Object.keys(declaration.sources ?? {}));
|
|
151
|
+
for (const name of sourceNames(declaration)) {
|
|
152
|
+
if (FIXED_SOURCE_NAMES.includes(name) || declared.has(name))
|
|
153
|
+
continue;
|
|
154
|
+
const bindings = declared.size === 0 ? 'and this declaration binds none' : `or ${quotedList([...declared])}`;
|
|
155
|
+
throw new ConfigValidationError(`${location} reads the source '${name}', which is neither one of ${quotedList(FIXED_SOURCE_NAMES)} ${bindings}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Check the declaration's `mechanism` against the catalogue (throws on a mismatch).
|
|
160
|
+
*
|
|
161
|
+
* The order is the author's repair order: an unknown name first (nothing else is
|
|
162
|
+
* meaningful without a spec), then the reserved name, then the structural markers, then
|
|
163
|
+
* the axes and relations the derived shape must stay inside. Membership is subset, not
|
|
164
|
+
* equality — a name admitting two relations accepts a declaration using one of them.
|
|
165
|
+
*/
|
|
166
|
+
export function validateMechanism(declaration, location) {
|
|
167
|
+
const mechanism = declaration.mechanism;
|
|
168
|
+
if (!MECHANISM_NAMES.includes(mechanism)) {
|
|
169
|
+
throw new ConfigValidationError(`${location} mechanism is '${mechanism}' — must be one of ${quotedList(MECHANISM_NAMES)}`);
|
|
170
|
+
}
|
|
171
|
+
const spec = MECHANISM_SHAPES[mechanism];
|
|
172
|
+
if (spec.reserved !== undefined) {
|
|
173
|
+
throw new ConfigValidationError(`${location} mechanism '${mechanism}' is reserved for ${spec.reserved}, which does not exist yet`);
|
|
174
|
+
}
|
|
175
|
+
checkSourceNames(declaration, location);
|
|
176
|
+
const shape = deriveShape(declaration);
|
|
177
|
+
if (spec.requiresWitness === true && !shape.witness) {
|
|
178
|
+
throw new ConfigValidationError(`${location} mechanism '${mechanism}' needs a witness block — the valve is its whole shape`);
|
|
179
|
+
}
|
|
180
|
+
if (spec.scopeSource !== undefined && declaration.scope?.source !== spec.scopeSource) {
|
|
181
|
+
const actual = declaration.scope === undefined
|
|
182
|
+
? 'this declaration has no scope block'
|
|
183
|
+
: `this declaration scopes on '${declaration.scope.source}'`;
|
|
184
|
+
throw new ConfigValidationError(`${location} mechanism '${mechanism}' scopes on '${spec.scopeSource}' — ${actual}`);
|
|
185
|
+
}
|
|
186
|
+
const outsideAxes = [...shape.axes].filter((axis) => !spec.axes.has(axis));
|
|
187
|
+
const outsideRelations = [...shape.relations].filter((relation) => !spec.relations.has(relation));
|
|
188
|
+
if (outsideAxes.length > 0 || outsideRelations.length > 0) {
|
|
189
|
+
throw new ConfigValidationError(`${location} mechanism '${mechanism}' expects relations ${quotedList([...spec.relations])} on axes ${quotedList([...spec.axes])}; this declaration relates ${quotedList([...shape.relations])} on ${quotedList([...shape.axes])}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Config schema
|
|
2
|
+
* Config schema and the `defineConfig()` validator — config as data.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* it values the compiler never saw). It stays a pure function — zero file I/O, zero runtime
|
|
9
|
-
* dependencies (hand-rolled validation; the published JSON Schema is a sibling artifact the
|
|
10
|
-
* source never reads).
|
|
4
|
+
* The single settings surface the areas share. The input is pure JSON-representable data,
|
|
5
|
+
* and `defineConfig` is the runtime validator for parsed unknown values the compiler never
|
|
6
|
+
* saw. It stays a pure function — no file I/O and no runtime dependencies: validation is
|
|
7
|
+
* hand-rolled, and the published JSON Schema is a sibling artifact this source never reads.
|
|
11
8
|
*/
|
|
12
|
-
|
|
9
|
+
import { type AlgebraDeclaration } from './algebra.ts';
|
|
10
|
+
export { ConfigValidationError } from './validation.ts';
|
|
11
|
+
/** Conventional default telemetry log path — local-only observation data. */
|
|
13
12
|
export declare const DEFAULT_TELEMETRY_LOG_PATH = ".polydeukes/roi.log";
|
|
14
13
|
/**
|
|
15
|
-
* `LanguageProfile` — the unit of the language axis
|
|
14
|
+
* `LanguageProfile` — the unit of the language axis.
|
|
16
15
|
*
|
|
17
16
|
* `testCmd` is a shell command template: every literal `{scope}` token is substituted at
|
|
18
17
|
* resolve time, and the core only carries the resulting string — it never interprets it.
|
|
@@ -27,80 +26,86 @@ export type LanguageProfile = {
|
|
|
27
26
|
testCmd: string;
|
|
28
27
|
};
|
|
29
28
|
/**
|
|
30
|
-
* `
|
|
29
|
+
* `AlgebraDeclarationBody` — an algebra declaration minus its name.
|
|
31
30
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* An entry names its declaration with the entry's `id`, so the block under `declare`
|
|
32
|
+
* carries every other key and never `discipline`.
|
|
34
33
|
*/
|
|
35
|
-
export type
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
export type AlgebraDeclarationBody = Omit<AlgebraDeclaration, 'discipline'>;
|
|
35
|
+
/**
|
|
36
|
+
* `EnforceLevel` — an entry's own rung on the promotion ladder. `advise` records a break
|
|
37
|
+
* without stopping it; `block` pins the entry at block whatever default the ladder later
|
|
38
|
+
* adopts. Absence means advise; `block` is the promotion rung.
|
|
39
|
+
*/
|
|
40
|
+
export type EnforceLevel = 'block' | 'advise';
|
|
38
41
|
/**
|
|
39
|
-
* `DisciplineEntry` — one user-declared discipline
|
|
42
|
+
* `DisciplineEntry` — one user-declared discipline. Pure JSON data.
|
|
40
43
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* strings but never executes them.
|
|
44
|
+
* The judgment is the `declare` block and nothing else; the other three keys name the
|
|
45
|
+
* entry, explain it, and set its level. Compilation is the covenant package's job — the
|
|
46
|
+
* core validates the block's grammar but never runs an extraction.
|
|
45
47
|
*/
|
|
46
48
|
export type DisciplineEntry = {
|
|
47
49
|
/** unique handle — telemetry label and verdict reason prefix */
|
|
48
50
|
id: string;
|
|
49
|
-
/** prose rationale
|
|
51
|
+
/** prose rationale — never judged, and carried into the break message */
|
|
50
52
|
why?: string;
|
|
51
|
-
/**
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
53
|
+
/** the author's level; composes with the observer's surface level, lenient side winning */
|
|
54
|
+
enforce?: EnforceLevel;
|
|
55
|
+
/** one judgment written as data — the entry's `id` is the declaration's name */
|
|
56
|
+
declare: AlgebraDeclarationBody;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* `DisciplineDraft` — an unpromoted discipline: the promotion ladder's first rung,
|
|
60
|
+
* registered as prose ahead of any declaration.
|
|
61
|
+
*
|
|
62
|
+
* A draft is declared, never inferred — only the literal `draft: true` makes one, and an
|
|
63
|
+
* entry with neither a `declare` block nor the marker stays a validation error. It carries
|
|
64
|
+
* no declaration, produces no registration, no judgment, and no telemetry row;
|
|
65
|
+
* `pdks explain` renders it as unpromoted.
|
|
66
|
+
*/
|
|
67
|
+
export type DisciplineDraft = {
|
|
68
|
+
/** unique handle in the same label space as judged entries and meta-covenant labels */
|
|
69
|
+
id: string;
|
|
70
|
+
/** the draft's whole body — required prose, unlike the judged families' optional why */
|
|
71
|
+
why: string;
|
|
72
|
+
/** the explicit marker; only the literal true exists (false is rejected as dead data) */
|
|
73
|
+
draft: true;
|
|
69
74
|
};
|
|
70
75
|
/**
|
|
71
|
-
* `PolydeukesConfig` — the input shape a user writes
|
|
76
|
+
* `PolydeukesConfig` — the input shape a user writes. JSON-serializable data.
|
|
72
77
|
*
|
|
73
78
|
* Language keys (`typescript`, `python`, …) are user *values*, not the core's vocabulary —
|
|
74
79
|
* no language or tool literal appears in the core source.
|
|
75
80
|
*/
|
|
76
81
|
export type PolydeukesConfig = {
|
|
77
|
-
/** IDE schema reference
|
|
82
|
+
/** IDE schema reference — accepted and ignored, never part of the resolution */
|
|
78
83
|
$schema?: string;
|
|
79
84
|
/** language axis, first-class. keys are user values ('typescript', 'python', …) */
|
|
80
85
|
languages: Record<string, LanguageProfile>;
|
|
81
|
-
/** raw protected path patterns —
|
|
86
|
+
/** raw protected path patterns — normalized downstream, never here */
|
|
82
87
|
protectedPaths?: string[];
|
|
83
88
|
/**
|
|
84
|
-
* adapter namespaces
|
|
85
|
-
*
|
|
86
|
-
*
|
|
89
|
+
* adapter namespaces — keys are ecosystem values (never validated), each value is that
|
|
90
|
+
* adapter's own settings object, passed through verbatim (the vocabulary belongs to the
|
|
91
|
+
* adapter, whose own validator judges the contents)
|
|
87
92
|
*/
|
|
88
93
|
adapters?: Record<string, Record<string, unknown>>;
|
|
89
94
|
telemetry?: {
|
|
90
|
-
/** conventional default applies when omitted
|
|
95
|
+
/** conventional default applies when omitted */
|
|
91
96
|
logPath?: string;
|
|
92
97
|
};
|
|
93
98
|
/** user-declared disciplines — validated here, compiled by the covenant package */
|
|
94
|
-
disciplines?: DisciplineEntry[];
|
|
99
|
+
disciplines?: (DisciplineEntry | DisciplineDraft)[];
|
|
95
100
|
/**
|
|
96
|
-
* TTL witness values for the covenant valve seam
|
|
97
|
-
*
|
|
101
|
+
* TTL witness values for the covenant valve seam — consumed at assembly time,
|
|
102
|
+
* validated here
|
|
98
103
|
*/
|
|
99
104
|
witness?: {
|
|
100
105
|
/**
|
|
101
106
|
* the agreed phrase a human types alone on a message's first line — quoting it
|
|
102
|
-
* mid-sentence is a mention, not an invocation
|
|
103
|
-
*
|
|
107
|
+
* mid-sentence is a mention, not an invocation. Non-empty after trimming; the value
|
|
108
|
+
* itself is free, since provenance rather than secrecy is the defence
|
|
104
109
|
*/
|
|
105
110
|
token: string;
|
|
106
111
|
/** validity window in minutes from the user message's timestamp — finite and > 0 */
|
|
@@ -110,11 +115,11 @@ export type PolydeukesConfig = {
|
|
|
110
115
|
/**
|
|
111
116
|
* `ResolvedLanguageProfile` — a {@link LanguageProfile} with its template compiled.
|
|
112
117
|
*
|
|
113
|
-
* Consumers keep the callable shape (`testCmd(scope)`)
|
|
118
|
+
* Consumers keep the callable shape (`testCmd(scope)`).
|
|
114
119
|
*/
|
|
115
120
|
export type ResolvedLanguageProfile = {
|
|
116
121
|
productionGlob: string | string[];
|
|
117
|
-
/** compiled from the template — consumers keep the callable shape
|
|
122
|
+
/** compiled from the template — consumers keep the callable shape */
|
|
118
123
|
testCmd: (scope: string) => string;
|
|
119
124
|
};
|
|
120
125
|
/**
|
|
@@ -130,28 +135,23 @@ export type ResolvedConfig = {
|
|
|
130
135
|
telemetry: {
|
|
131
136
|
logPath: string;
|
|
132
137
|
};
|
|
133
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* validated judged entries only — drafts are split out at resolution time so the
|
|
140
|
+
* covenant compiler has no path that receives one. Present whenever the input
|
|
141
|
+
* declared a `disciplines` array, holding exactly its judged entries in order.
|
|
142
|
+
*/
|
|
134
143
|
disciplines?: DisciplineEntry[];
|
|
144
|
+
/** validated drafts in declaration order (absent when the input carries none) */
|
|
145
|
+
drafts?: DisciplineDraft[];
|
|
135
146
|
/** validated witness data, passed through verbatim (absent stays absent) */
|
|
136
147
|
witness?: {
|
|
137
148
|
token: string;
|
|
138
149
|
ttlMinutes: number;
|
|
139
150
|
};
|
|
140
151
|
};
|
|
141
|
-
/**
|
|
142
|
-
* `ConfigValidationError` — raised when a config fails structural validation (PRD §4.3).
|
|
143
|
-
*
|
|
144
|
-
* The message names the offending field path so the developer sees exactly what is wrong.
|
|
145
|
-
* This throw is a developer-time error (config authoring), a different axis from the
|
|
146
|
-
* covenant runtime's fail-closed exit code — a bad config should fail loud and early.
|
|
147
|
-
*/
|
|
148
|
-
export declare class ConfigValidationError extends Error {
|
|
149
|
-
constructor(message: string);
|
|
150
|
-
}
|
|
151
152
|
/**
|
|
152
153
|
* Validate parsed unknown data as a {@link PolydeukesConfig} and return a
|
|
153
|
-
* {@link ResolvedConfig} with defaults filled and templates compiled
|
|
154
|
-
* Pure — no file I/O.
|
|
154
|
+
* {@link ResolvedConfig} with defaults filled and templates compiled. Pure — no file I/O.
|
|
155
155
|
*
|
|
156
156
|
* Throws {@link ConfigValidationError} (naming the offending field path) when the top level
|
|
157
157
|
* is not a plain object, any object level carries an unknown key, `languages` is
|