@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
package/dist/config.js
CHANGED
|
@@ -1,31 +1,19 @@
|
|
|
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
|
*/
|
|
9
|
+
import { validateAlgebraDeclaration } from './algebra.js';
|
|
12
10
|
import { isPlainObject } from './is-plain-object.js';
|
|
13
|
-
|
|
11
|
+
import { ConfigValidationError, isNonEmptyString, isStringArray, rejectUnknownKeys, } from './validation.js';
|
|
12
|
+
export { ConfigValidationError } from './validation.js';
|
|
13
|
+
/** Conventional default telemetry log path — local-only observation data. */
|
|
14
14
|
export const DEFAULT_TELEMETRY_LOG_PATH = '.polydeukes/roi.log';
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
*
|
|
18
|
-
* The message names the offending field path so the developer sees exactly what is wrong.
|
|
19
|
-
* This throw is a developer-time error (config authoring), a different axis from the
|
|
20
|
-
* covenant runtime's fail-closed exit code — a bad config should fail loud and early.
|
|
21
|
-
*/
|
|
22
|
-
export class ConfigValidationError extends Error {
|
|
23
|
-
constructor(message) {
|
|
24
|
-
super(message);
|
|
25
|
-
this.name = 'ConfigValidationError';
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
/** The exact key vocabulary of each object level — anything else is a typo, rejected loudly. */
|
|
15
|
+
/** Labels the assembly reserves for the judging chain's own registrations. */
|
|
16
|
+
const META_COVENANT_LABELS = ['self-mod', 'shell-mod', 'transcript-mod'];
|
|
29
17
|
const TOP_LEVEL_KEYS = new Set([
|
|
30
18
|
'$schema',
|
|
31
19
|
'languages',
|
|
@@ -38,44 +26,18 @@ const TOP_LEVEL_KEYS = new Set([
|
|
|
38
26
|
const PROFILE_KEYS = new Set(['productionGlob', 'testCmd']);
|
|
39
27
|
const TELEMETRY_KEYS = new Set(['logPath']);
|
|
40
28
|
const WITNESS_KEYS = new Set(['token', 'ttlMinutes']);
|
|
41
|
-
const DISCIPLINE_KEYS = new Set([
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
'in',
|
|
45
|
-
'except',
|
|
46
|
-
'forbid',
|
|
47
|
-
'immutable',
|
|
48
|
-
'forbidCommand',
|
|
49
|
-
'when',
|
|
50
|
-
'requirePrecedent',
|
|
51
|
-
]);
|
|
52
|
-
const PREDICATE_KEYS = ['forbid', 'immutable', 'forbidCommand', 'requirePrecedent'];
|
|
53
|
-
/** Predicate families that `in`/`except` may scope — delta and context (COVENANT-13 §4.1). */
|
|
54
|
-
const SCOPED_PREDICATE_KEYS = new Set(['forbid', 'requirePrecedent']);
|
|
55
|
-
/** Throw on the first key outside the allowed vocabulary, naming the key and its location. */
|
|
56
|
-
function rejectUnknownKeys(record, allowed, location) {
|
|
57
|
-
for (const key of Object.keys(record)) {
|
|
58
|
-
if (!allowed.has(key)) {
|
|
59
|
-
throw new ConfigValidationError(`unknown key '${key}' in ${location}`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
/** True when the value is an array whose every element is a string. */
|
|
64
|
-
function isStringArray(value) {
|
|
65
|
-
return Array.isArray(value) && value.every((entry) => typeof entry === 'string');
|
|
66
|
-
}
|
|
29
|
+
const DISCIPLINE_KEYS = new Set(['id', 'why', 'enforce', 'declare']);
|
|
30
|
+
const DRAFT_KEYS = new Set(['id', 'why', 'draft']);
|
|
31
|
+
const ENFORCE_LEVELS = new Set(['block', 'advise']);
|
|
67
32
|
/** True when the glob value is a present, non-empty string or a non-empty array of non-empty strings. */
|
|
68
33
|
function isValidGlob(glob) {
|
|
69
34
|
if (typeof glob === 'string') {
|
|
70
|
-
return glob
|
|
35
|
+
return isNonEmptyString(glob);
|
|
71
36
|
}
|
|
72
|
-
|
|
73
|
-
return glob.length > 0 && glob.every((entry) => typeof entry === 'string' && entry.length > 0);
|
|
74
|
-
}
|
|
75
|
-
return false;
|
|
37
|
+
return Array.isArray(glob) && glob.length > 0 && glob.every(isNonEmptyString);
|
|
76
38
|
}
|
|
77
39
|
/**
|
|
78
|
-
* Compile a `{scope}` template into the callable consumers use
|
|
40
|
+
* Compile a `{scope}` template into the callable consumers use.
|
|
79
41
|
*
|
|
80
42
|
* Exactly the literal token `{scope}` is substituted, at every occurrence (`replaceAll`
|
|
81
43
|
* semantics). Other braces (`${VAR}`, `{a,b}`, `awk '{print}'`) are the shell's own
|
|
@@ -86,49 +48,63 @@ function compileTestCmd(template) {
|
|
|
86
48
|
// via GetSubstitution, breaking literal insertion for scopes containing `$`.
|
|
87
49
|
return (scope) => template.replaceAll('{scope}', () => scope);
|
|
88
50
|
}
|
|
89
|
-
/**
|
|
90
|
-
function
|
|
91
|
-
|
|
92
|
-
|
|
51
|
+
/** Validate a draft entry and return it as data. */
|
|
52
|
+
function validateDraft(entry, id, location) {
|
|
53
|
+
for (const key of Object.keys(entry)) {
|
|
54
|
+
if (!DRAFT_KEYS.has(key)) {
|
|
55
|
+
// Named as the draft rule, not as an unknown key: `declare` et al. are legal
|
|
56
|
+
// discipline keys, just not on a draft.
|
|
57
|
+
throw new ConfigValidationError(`${location} allows only id, why, draft on a draft entry (found '${key}')`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (entry.draft !== true) {
|
|
61
|
+
throw new ConfigValidationError(`${location} draft must be the literal true`);
|
|
62
|
+
}
|
|
63
|
+
if (typeof entry.why !== 'string' || entry.why.length === 0) {
|
|
64
|
+
throw new ConfigValidationError(`${location} why must be a non-empty string on a draft entry — the prose is its whole body`);
|
|
65
|
+
}
|
|
66
|
+
return { id, why: entry.why, draft: true };
|
|
67
|
+
}
|
|
68
|
+
/** Validate the head of a judged entry — the closed key set, `why`, and `enforce`. */
|
|
69
|
+
function validateEntryHead(entry, location) {
|
|
70
|
+
rejectUnknownKeys(entry, DISCIPLINE_KEYS, location);
|
|
71
|
+
if (entry.why !== undefined && typeof entry.why !== 'string') {
|
|
72
|
+
throw new ConfigValidationError(`${location} why must be a string`);
|
|
93
73
|
}
|
|
94
|
-
|
|
95
|
-
|
|
74
|
+
if (entry.enforce !== undefined &&
|
|
75
|
+
(typeof entry.enforce !== 'string' || !ENFORCE_LEVELS.has(entry.enforce))) {
|
|
76
|
+
throw new ConfigValidationError(`${location} enforce must be 'block' or 'advise'`);
|
|
96
77
|
}
|
|
97
78
|
}
|
|
98
79
|
/**
|
|
99
|
-
* Validate a
|
|
80
|
+
* Validate a judged entry's declaration by delegating the block to the algebra validator.
|
|
100
81
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* inspects it (CONFIG-07 layering). An unrecognized evidence key fails closed at
|
|
106
|
-
* assembly time, not here.
|
|
82
|
+
* The entry's `id` supplies the declaration's name, so the block carrying its own
|
|
83
|
+
* `discipline` is refused rather than silently overwritten. The delegated messages arrive
|
|
84
|
+
* with the entry's location in front of them, which is what places a failure among many
|
|
85
|
+
* entries.
|
|
107
86
|
*/
|
|
108
|
-
function
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const keys = Object.keys(evidence);
|
|
113
|
-
if (keys.length !== 1) {
|
|
114
|
-
throw new ConfigValidationError(`${location} requirePrecedent must have exactly one evidence key`);
|
|
87
|
+
function validateDeclareEntry(entry, location) {
|
|
88
|
+
const block = entry.declare;
|
|
89
|
+
if (!isPlainObject(block)) {
|
|
90
|
+
throw new ConfigValidationError(`${location} declare must be an object`);
|
|
115
91
|
}
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
if (typeof command !== 'string' || command.length === 0) {
|
|
119
|
-
throw new ConfigValidationError(`${location} requirePrecedent.command must be a non-empty string pattern`);
|
|
120
|
-
}
|
|
121
|
-
rejectUncompilableRegex(command, `${location} requirePrecedent.command`);
|
|
92
|
+
if ('discipline' in block) {
|
|
93
|
+
throw new ConfigValidationError(`${location} declare must not carry discipline — the entry id is the name`);
|
|
122
94
|
}
|
|
95
|
+
validateAlgebraDeclaration({ discipline: entry.id, ...block }, `${location} declare`);
|
|
123
96
|
}
|
|
124
97
|
/**
|
|
125
|
-
* Validate the `disciplines` array
|
|
126
|
-
* naming the offending entry/key; the validated data passes
|
|
98
|
+
* Validate the `disciplines` array and split judged entries from drafts. Throws
|
|
99
|
+
* {@link ConfigValidationError} naming the offending entry/key; the validated data passes
|
|
100
|
+
* through verbatim, in declaration order.
|
|
127
101
|
*/
|
|
128
102
|
function validateDisciplines(disciplines) {
|
|
129
103
|
if (!Array.isArray(disciplines)) {
|
|
130
104
|
throw new ConfigValidationError('disciplines must be an array');
|
|
131
105
|
}
|
|
106
|
+
const judged = [];
|
|
107
|
+
const drafts = [];
|
|
132
108
|
const seenIds = new Set();
|
|
133
109
|
disciplines.forEach((entry, index) => {
|
|
134
110
|
if (!isPlainObject(entry)) {
|
|
@@ -141,93 +117,26 @@ function validateDisciplines(disciplines) {
|
|
|
141
117
|
if (seenIds.has(entry.id)) {
|
|
142
118
|
throw new ConfigValidationError(`${location} duplicates the id of an earlier entry`);
|
|
143
119
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const predicates = PREDICATE_KEYS.filter((key) => entry[key] !== undefined);
|
|
150
|
-
if (predicates.length !== 1) {
|
|
151
|
-
throw new ConfigValidationError(`${location} must have exactly one predicate key ` +
|
|
152
|
-
`(forbid | immutable | forbidCommand | requirePrecedent)`);
|
|
153
|
-
}
|
|
154
|
-
const predicate = predicates[0];
|
|
155
|
-
if (!SCOPED_PREDICATE_KEYS.has(predicate) &&
|
|
156
|
-
(entry.in !== undefined || entry.except !== undefined)) {
|
|
157
|
-
throw new ConfigValidationError(`${location} allows in/except only on a forbid or requirePrecedent entry`);
|
|
158
|
-
}
|
|
159
|
-
// `when` is the context family's trigger; on any other family it would be dead data
|
|
160
|
-
// implying a trigger that is never applied.
|
|
161
|
-
if (entry.when !== undefined && predicate !== 'requirePrecedent') {
|
|
162
|
-
throw new ConfigValidationError(`${location} allows when only on a requirePrecedent entry`);
|
|
163
|
-
}
|
|
164
|
-
if (entry.in !== undefined && !isValidGlob(entry.in)) {
|
|
165
|
-
throw new ConfigValidationError(`${location} in must be a non-empty glob or glob array`);
|
|
120
|
+
// The three meta-covenant registrations share the telemetry label space with
|
|
121
|
+
// discipline ids; a colliding id would make gain aggregation and any label-keyed
|
|
122
|
+
// reader (pdks explain) ambiguous.
|
|
123
|
+
if (META_COVENANT_LABELS.includes(entry.id)) {
|
|
124
|
+
throw new ConfigValidationError(`${location} id collides with a meta-covenant label`);
|
|
166
125
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// An empty pattern matches at every position, so the entry would break every
|
|
174
|
-
// in-scope change — rejected like every sibling pattern field.
|
|
175
|
-
if (forbid.length === 0) {
|
|
176
|
-
throw new ConfigValidationError(`${location} forbid must be a non-empty string pattern`);
|
|
177
|
-
}
|
|
178
|
-
rejectUncompilableRegex(forbid, `${location} forbid`);
|
|
179
|
-
}
|
|
180
|
-
else if (isPlainObject(forbid)) {
|
|
181
|
-
// Only the { added } direction exists before COVENANT-12.
|
|
182
|
-
const keys = Object.keys(forbid);
|
|
183
|
-
if (keys.length !== 1 || keys[0] !== 'added' || typeof forbid.added !== 'string') {
|
|
184
|
-
throw new ConfigValidationError(`${location} forbid object must have exactly one key 'added' with a string pattern`);
|
|
185
|
-
}
|
|
186
|
-
if (forbid.added.length === 0) {
|
|
187
|
-
throw new ConfigValidationError(`${location} forbid.added must be a non-empty string pattern`);
|
|
188
|
-
}
|
|
189
|
-
rejectUncompilableRegex(forbid.added, `${location} forbid.added`);
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
throw new ConfigValidationError(`${location} forbid must be a string pattern or an { added } object`);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
else if (predicate === 'immutable') {
|
|
196
|
-
if (!isValidGlob(entry.immutable)) {
|
|
197
|
-
throw new ConfigValidationError(`${location} immutable must be a non-empty glob or glob array`);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
else if (predicate === 'forbidCommand') {
|
|
201
|
-
if (typeof entry.forbidCommand !== 'string') {
|
|
202
|
-
throw new ConfigValidationError(`${location} forbidCommand must be a string pattern`);
|
|
203
|
-
}
|
|
204
|
-
if (entry.forbidCommand.length === 0) {
|
|
205
|
-
// An empty pattern matches every command line — one typo would block every
|
|
206
|
-
// shell call the entry sees.
|
|
207
|
-
throw new ConfigValidationError(`${location} forbidCommand must be a non-empty string pattern`);
|
|
208
|
-
}
|
|
209
|
-
rejectUncompilableRegex(entry.forbidCommand, `${location} forbidCommand`);
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
if (entry.when !== undefined) {
|
|
213
|
-
if (typeof entry.when !== 'string') {
|
|
214
|
-
throw new ConfigValidationError(`${location} when must be a string pattern`);
|
|
215
|
-
}
|
|
216
|
-
if (entry.when.length === 0) {
|
|
217
|
-
// An empty pattern matches at every position, so the trigger would fire on any
|
|
218
|
-
// file that merely grows — reject it like every sibling pattern field.
|
|
219
|
-
throw new ConfigValidationError(`${location} when must be a non-empty string pattern`);
|
|
220
|
-
}
|
|
221
|
-
rejectUncompilableRegex(entry.when, `${location} when`);
|
|
222
|
-
}
|
|
223
|
-
validateRequirePrecedent(entry.requirePrecedent, location);
|
|
126
|
+
seenIds.add(entry.id);
|
|
127
|
+
// Selected by the marker's value, so an explicit `draft: undefined` is absence,
|
|
128
|
+
// like every other optional key in this validator.
|
|
129
|
+
if (entry.draft !== undefined) {
|
|
130
|
+
drafts.push(validateDraft(entry, entry.id, location));
|
|
131
|
+
return;
|
|
224
132
|
}
|
|
133
|
+
validateEntryHead(entry, location);
|
|
134
|
+
validateDeclareEntry(entry, location);
|
|
135
|
+
judged.push(entry);
|
|
225
136
|
});
|
|
226
|
-
return
|
|
137
|
+
return { judged, drafts };
|
|
227
138
|
}
|
|
228
|
-
/**
|
|
229
|
-
* Validate the `languages` map and compile each profile's `{scope}` template (PRD §4.1).
|
|
230
|
-
*/
|
|
139
|
+
/** Validate the `languages` map and compile each profile's `{scope}` template. */
|
|
231
140
|
function validateLanguages(languages) {
|
|
232
141
|
if (!isPlainObject(languages) || Object.keys(languages).length === 0) {
|
|
233
142
|
throw new ConfigValidationError('languages must be a non-empty object');
|
|
@@ -268,8 +177,8 @@ function validateProtectedPaths(protectedPaths) {
|
|
|
268
177
|
return protectedPaths;
|
|
269
178
|
}
|
|
270
179
|
/**
|
|
271
|
-
* Validate the `adapters` map
|
|
272
|
-
*
|
|
180
|
+
* Validate the `adapters` map — each namespace is a plain object whose contents belong to
|
|
181
|
+
* that adapter's own validator, so they pass through verbatim.
|
|
273
182
|
*/
|
|
274
183
|
function validateAdapters(adapters) {
|
|
275
184
|
// Array first: the removed directory-list form deserves a migration hint, not a
|
|
@@ -302,7 +211,7 @@ function validateTelemetry(telemetry) {
|
|
|
302
211
|
}
|
|
303
212
|
return telemetry.logPath;
|
|
304
213
|
}
|
|
305
|
-
/** Validate the `witness` section
|
|
214
|
+
/** Validate the `witness` section — both values are consumed at assembly time. */
|
|
306
215
|
function validateWitness(witness) {
|
|
307
216
|
if (!isPlainObject(witness)) {
|
|
308
217
|
throw new ConfigValidationError('witness must be an object');
|
|
@@ -319,8 +228,7 @@ function validateWitness(witness) {
|
|
|
319
228
|
}
|
|
320
229
|
/**
|
|
321
230
|
* Validate parsed unknown data as a {@link PolydeukesConfig} and return a
|
|
322
|
-
* {@link ResolvedConfig} with defaults filled and templates compiled
|
|
323
|
-
* Pure — no file I/O.
|
|
231
|
+
* {@link ResolvedConfig} with defaults filled and templates compiled. Pure — no file I/O.
|
|
324
232
|
*
|
|
325
233
|
* Throws {@link ConfigValidationError} (naming the offending field path) when the top level
|
|
326
234
|
* is not a plain object, any object level carries an unknown key, `languages` is
|
|
@@ -334,15 +242,17 @@ export function defineConfig(config) {
|
|
|
334
242
|
throw new ConfigValidationError('config must be a plain object');
|
|
335
243
|
}
|
|
336
244
|
rejectUnknownKeys(config, TOP_LEVEL_KEYS, 'config');
|
|
337
|
-
// `$schema` is an IDE schema reference
|
|
338
|
-
//
|
|
245
|
+
// `$schema` is an IDE schema reference: accepted, type-checked, and ignored — it never
|
|
246
|
+
// appears in the resolution output.
|
|
339
247
|
if (config.$schema !== undefined && typeof config.$schema !== 'string') {
|
|
340
248
|
throw new ConfigValidationError('$schema must be a string');
|
|
341
249
|
}
|
|
342
250
|
const resolvedLanguages = validateLanguages(config.languages);
|
|
343
251
|
const protectedPaths = config.protectedPaths !== undefined ? validateProtectedPaths(config.protectedPaths) : undefined;
|
|
344
252
|
const adapters = config.adapters !== undefined ? validateAdapters(config.adapters) : undefined;
|
|
345
|
-
const
|
|
253
|
+
const split = config.disciplines !== undefined ? validateDisciplines(config.disciplines) : undefined;
|
|
254
|
+
const disciplines = split?.judged;
|
|
255
|
+
const drafts = split !== undefined && split.drafts.length > 0 ? split.drafts : undefined;
|
|
346
256
|
const logPath = config.telemetry !== undefined ? validateTelemetry(config.telemetry) : undefined;
|
|
347
257
|
const witness = config.witness !== undefined ? validateWitness(config.witness) : undefined;
|
|
348
258
|
return {
|
|
@@ -353,6 +263,7 @@ export function defineConfig(config) {
|
|
|
353
263
|
logPath: logPath ?? DEFAULT_TELEMETRY_LOG_PATH,
|
|
354
264
|
},
|
|
355
265
|
...(disciplines !== undefined && { disciplines }),
|
|
266
|
+
...(drafts !== undefined && { drafts }),
|
|
356
267
|
...(witness !== undefined && { witness }),
|
|
357
268
|
};
|
|
358
269
|
}
|
package/dist/exit-codes.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* exit-codes — the covenant protocol's exit-code vocabulary
|
|
2
|
+
* exit-codes — the covenant protocol's exit-code vocabulary.
|
|
3
3
|
*
|
|
4
|
-
* The three codes are distinct and ordered by severity. The covenant *body* only
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The three codes are distinct and ordered by severity. The covenant *body* only ever
|
|
5
|
+
* emits `0` (uphold) or `1` (break, non-blocking); translating a break into the blocking
|
|
6
|
+
* `2` is the wrapper's job, never the core's. The sole place the core itself reaches for
|
|
7
|
+
* `2` is the fail-closed parse path in `protocol.ts`.
|
|
8
8
|
*
|
|
9
|
-
* These live in their own module
|
|
10
|
-
* them: importing them from the barrel, which re-exports
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* These live in their own leaf module because `fail-policy.ts` and `protocol.ts` both need
|
|
10
|
+
* them: importing them from the barrel, which re-exports both, is an initialization
|
|
11
|
+
* cycle — the constants read as `undefined` depending on which module the runtime evaluates
|
|
12
|
+
* first. The barrel re-exports them, so every consumer outside core still reaches them at
|
|
13
|
+
* the same path.
|
|
14
14
|
*/
|
|
15
15
|
/** Promise upheld — no violation, the edit/push passes. */
|
|
16
16
|
export declare const EXIT_UPHOLD = 0;
|
package/dist/exit-codes.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* exit-codes — the covenant protocol's exit-code vocabulary
|
|
2
|
+
* exit-codes — the covenant protocol's exit-code vocabulary.
|
|
3
3
|
*
|
|
4
|
-
* The three codes are distinct and ordered by severity. The covenant *body* only
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The three codes are distinct and ordered by severity. The covenant *body* only ever
|
|
5
|
+
* emits `0` (uphold) or `1` (break, non-blocking); translating a break into the blocking
|
|
6
|
+
* `2` is the wrapper's job, never the core's. The sole place the core itself reaches for
|
|
7
|
+
* `2` is the fail-closed parse path in `protocol.ts`.
|
|
8
8
|
*
|
|
9
|
-
* These live in their own module
|
|
10
|
-
* them: importing them from the barrel, which re-exports
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* These live in their own leaf module because `fail-policy.ts` and `protocol.ts` both need
|
|
10
|
+
* them: importing them from the barrel, which re-exports both, is an initialization
|
|
11
|
+
* cycle — the constants read as `undefined` depending on which module the runtime evaluates
|
|
12
|
+
* first. The barrel re-exports them, so every consumer outside core still reaches them at
|
|
13
|
+
* the same path.
|
|
14
14
|
*/
|
|
15
15
|
/** Promise upheld — no violation, the edit/push passes. */
|
|
16
16
|
export const EXIT_UPHOLD = 0;
|
package/dist/fail-policy.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* fail-policy — the failure-kind → fail-mode policy table
|
|
2
|
+
* fail-policy — the failure-kind → fail-mode policy table.
|
|
3
3
|
*
|
|
4
4
|
* Pure and total: classifying a failure and mapping it to an exit code never
|
|
5
5
|
* performs I/O and never throws. The single source of truth for "which failures
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
/** How a failure resolves: 'open' passes the call through, 'closed' blocks it. */
|
|
9
9
|
export type FailMode = 'open' | 'closed';
|
|
10
10
|
/**
|
|
11
|
-
* The registered failure kinds
|
|
11
|
+
* The registered failure kinds. Gate-integrity failures
|
|
12
12
|
* (evidence-absence / input-parse / undecidable-structure) fail closed;
|
|
13
13
|
* observability failures fail open so measurement loss never holds work hostage.
|
|
14
14
|
*/
|
|
@@ -16,14 +16,14 @@ export type FailureKind = 'evidence-absence' | 'input-parse' | 'undecidable-stru
|
|
|
16
16
|
/**
|
|
17
17
|
* Resolve a failure kind to its {@link FailMode} via the policy table.
|
|
18
18
|
*
|
|
19
|
-
* fail-closed default
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* fail-closed default: any unregistered kind — including '' and prototype-pollution
|
|
20
|
+
* keys — resolves to 'closed'. "Cannot classify" means block. Pure and total: never
|
|
21
|
+
* throws, no I/O, no logging.
|
|
22
22
|
*/
|
|
23
23
|
export declare function resolveFailMode(kind: string): FailMode;
|
|
24
24
|
/**
|
|
25
|
-
* Map a {@link FailMode} to the covenant protocol's exit code
|
|
25
|
+
* Map a {@link FailMode} to the covenant protocol's exit code:
|
|
26
26
|
* 'open' → {@link EXIT_UPHOLD}, 'closed' → {@link EXIT_BREAK_BLOCKING}.
|
|
27
|
-
* Reuses
|
|
27
|
+
* Reuses the protocol's constants — no independent numeric literals here.
|
|
28
28
|
*/
|
|
29
29
|
export declare function failModeToExitCode(mode: FailMode): 0 | 2;
|
package/dist/fail-policy.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* fail-policy — the failure-kind → fail-mode policy table
|
|
2
|
+
* fail-policy — the failure-kind → fail-mode policy table.
|
|
3
3
|
*
|
|
4
4
|
* Pure and total: classifying a failure and mapping it to an exit code never
|
|
5
5
|
* performs I/O and never throws. The single source of truth for "which failures
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { EXIT_BREAK_BLOCKING, EXIT_UPHOLD } from './exit-codes.js';
|
|
9
9
|
/**
|
|
10
|
-
* Policy table
|
|
10
|
+
* Policy table. Null-prototype so lookups can never reach
|
|
11
11
|
* Object.prototype members ('__proto__', 'toString', …) — those must resolve
|
|
12
12
|
* to the fail-closed default, not to an inherited function.
|
|
13
13
|
*/
|
|
@@ -20,17 +20,17 @@ const FAIL_POLICY = Object.assign(Object.create(null), {
|
|
|
20
20
|
/**
|
|
21
21
|
* Resolve a failure kind to its {@link FailMode} via the policy table.
|
|
22
22
|
*
|
|
23
|
-
* fail-closed default
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* fail-closed default: any unregistered kind — including '' and prototype-pollution
|
|
24
|
+
* keys — resolves to 'closed'. "Cannot classify" means block. Pure and total: never
|
|
25
|
+
* throws, no I/O, no logging.
|
|
26
26
|
*/
|
|
27
27
|
export function resolveFailMode(kind) {
|
|
28
28
|
return FAIL_POLICY[kind] ?? 'closed';
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
* Map a {@link FailMode} to the covenant protocol's exit code
|
|
31
|
+
* Map a {@link FailMode} to the covenant protocol's exit code:
|
|
32
32
|
* 'open' → {@link EXIT_UPHOLD}, 'closed' → {@link EXIT_BREAK_BLOCKING}.
|
|
33
|
-
* Reuses
|
|
33
|
+
* Reuses the protocol's constants — no independent numeric literals here.
|
|
34
34
|
*/
|
|
35
35
|
export function failModeToExitCode(mode) {
|
|
36
36
|
return mode === 'open' ? EXIT_UPHOLD : EXIT_BREAK_BLOCKING;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,104 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @polydeukes/core — the thin, domain- and agent-agnostic core.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* appendRecordFailOpen — the fail-open wrapper promoted by CORE-05).
|
|
4
|
+
* Alpha. Carries the covenant protocol, the ROI telemetry collector, and the config
|
|
5
|
+
* schema. Pure types and functions, except telemetry's confined I/O functions
|
|
6
|
+
* (appendRecord / readRecords / appendRecordFailOpen).
|
|
8
7
|
* See https://github.com/huskyhoochu/polydeukes
|
|
9
8
|
*/
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export { type
|
|
13
|
-
export {
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
16
|
-
export {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* Agent-neutral, discriminated by `kind`: a deletion is first-class evidence rather
|
|
21
|
-
* than an unrepresentable case, and impossible states (a deletion with resulting
|
|
22
|
-
* content, a creation with a baseline) cannot be written down. Adapters fill this from
|
|
23
|
-
* their own sources (virtual apply, git blobs) — the core only transports it.
|
|
24
|
-
* `delete.pre` is the readable text baseline when one exists — absent for a binary
|
|
25
|
-
* blob, because a deletion needs no content to be judged.
|
|
26
|
-
*/
|
|
27
|
-
export type FileChange = {
|
|
28
|
-
kind: 'create';
|
|
29
|
-
path: string;
|
|
30
|
-
post: string;
|
|
31
|
-
} | {
|
|
32
|
-
kind: 'modify';
|
|
33
|
-
path: string;
|
|
34
|
-
pre: string;
|
|
35
|
-
post: string;
|
|
36
|
-
} | {
|
|
37
|
-
kind: 'delete';
|
|
38
|
-
path: string;
|
|
39
|
-
pre?: string;
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* `CovenantInput` — the agent-neutral input IR a covenant judges (PRD §4.2).
|
|
43
|
-
*
|
|
44
|
-
* Adapters up-translate their own agent payloads into this shape and pipe it as
|
|
45
|
-
* stdin-JSON. The vocabulary carries no agent/tool literals; concrete tool or
|
|
46
|
-
* subagent names are *values* an adapter fills in, never part of the core's type.
|
|
47
|
-
* Evidence has exactly one home — the call element it belongs to (CORE-06 §4.1):
|
|
48
|
-
* `fileChange` absent means "this call is unproven", and no sibling call's evidence
|
|
49
|
-
* can stand in for it.
|
|
50
|
-
*/
|
|
51
|
-
export type CovenantInput = {
|
|
52
|
-
toolCalls: {
|
|
53
|
-
name: string;
|
|
54
|
-
args?: Record<string, unknown>;
|
|
55
|
-
fileChange?: FileChange;
|
|
56
|
-
}[];
|
|
57
|
-
subagentSpawns: {
|
|
58
|
-
kind: string;
|
|
59
|
-
}[];
|
|
60
|
-
userMessages: {
|
|
61
|
-
text: string;
|
|
62
|
-
}[];
|
|
63
|
-
};
|
|
64
|
-
/**
|
|
65
|
-
* `CovenantVerdict` — the result a covenant body produces (PRD §4.3).
|
|
66
|
-
*
|
|
67
|
-
* Either the promise was upheld, or it was broken with a human-readable reason.
|
|
68
|
-
* Maps to an exit code via {@link verdictToExitCode}.
|
|
69
|
-
*/
|
|
70
|
-
export type CovenantVerdict = {
|
|
71
|
-
upheld: true;
|
|
72
|
-
} | {
|
|
73
|
-
upheld: false;
|
|
74
|
-
reason: string;
|
|
75
|
-
};
|
|
76
|
-
/**
|
|
77
|
-
* Deserialize stdin-JSON into a {@link CovenantInput} (the protocol's reverse direction).
|
|
78
|
-
*
|
|
79
|
-
* fail-closed (PRD §5.2): this never throws. Any failure — unparseable JSON, an empty
|
|
80
|
-
* payload, a parsed value that is not an object, or a missing required collection —
|
|
81
|
-
* resolves to a blocking `{ ok: false, exitCode: 2 }`. "Cannot judge" means block,
|
|
82
|
-
* so an unjudgeable input can never be mistaken for a valid one.
|
|
83
|
-
*/
|
|
84
|
-
export declare function parseInput(stdinJson: string): {
|
|
85
|
-
ok: true;
|
|
86
|
-
value: CovenantInput;
|
|
87
|
-
} | {
|
|
88
|
-
ok: false;
|
|
89
|
-
exitCode: 2;
|
|
90
|
-
};
|
|
91
|
-
/**
|
|
92
|
-
* Flatten every call's evidence into one array in call order (CORE-06 §4.1).
|
|
93
|
-
*
|
|
94
|
-
* The one traversal for consumers that need no attribution (discipline scope, delta
|
|
95
|
-
* judging): calls without evidence are skipped, never substituted for.
|
|
96
|
-
*/
|
|
97
|
-
export declare function allFileChanges(input: CovenantInput): FileChange[];
|
|
98
|
-
/**
|
|
99
|
-
* Map a {@link CovenantVerdict} to an exit code (the protocol's forward direction).
|
|
100
|
-
*
|
|
101
|
-
* Responsibility boundary (PRD §4.1): the body emits `0` when upheld and `1` when
|
|
102
|
-
* broken — never the blocking `2`. Translating `1` into `2` is the wrapper's policy.
|
|
103
|
-
*/
|
|
104
|
-
export declare function verdictToExitCode(verdict: CovenantVerdict): 0 | 1;
|
|
9
|
+
export { type AlgebraDeclaration, BINARY_COMBINATOR_NAMES, type BinaryStep, type ExtractBlock, type ExtractStep, RELATION_NAMES, type RelateEntry, type RelationDecl, type RelationName, type ScopeBlock, SUPPLY_POLICIES, type SupplyBlock, type UnaryStep, validateAlgebraDeclaration, type Witness, type WitnessBlock, type Witnesses, } from './algebra.ts';
|
|
10
|
+
export { AXIS_NAMES, type Axis, type DerivableDeclaration, deriveShape, MECHANISM_NAMES, MECHANISM_SHAPES, type MechanismName, type MechanismShape, } from './catalogue.ts';
|
|
11
|
+
export { type AlgebraDeclarationBody, ConfigValidationError, DEFAULT_TELEMETRY_LOG_PATH, type DisciplineDraft, type DisciplineEntry, defineConfig, type EnforceLevel, type LanguageProfile, type PolydeukesConfig, type ResolvedConfig, type ResolvedLanguageProfile, } from './config.ts';
|
|
12
|
+
export { EXIT_BREAK_BLOCKING, EXIT_BREAK_NON_BLOCKING, EXIT_UPHOLD, } from './exit-codes.ts';
|
|
13
|
+
export { type FailMode, type FailureKind, failModeToExitCode, resolveFailMode, } from './fail-policy.ts';
|
|
14
|
+
export { isPlainObject } from './is-plain-object.ts';
|
|
15
|
+
export { normalizeProtectedPaths } from './protected-paths.ts';
|
|
16
|
+
export { allFileChanges, type ChannelReader, type CovenantInput, type CovenantVerdict, type DispatchOutcome, type FileChange, parseInput, type SourceReader, verdictToExitCode, } from './protocol.ts';
|
|
17
|
+
export { aggregateGain, appendRecord, appendRecordFailOpen, formatRecordLine, type GainSummary, parseRecordLine, readRecords, runGain, SKIP_REASONS, type SkipReason, type TelemetryEvent, type TelemetryRecord, } from './telemetry.ts';
|
|
18
|
+
export { type CanonicalTranscript, noopTranscript, type TranscriptToolCall, type TranscriptUserMessage, transcriptFromInput, } from './transcript.ts';
|