@orkestrel/brief 0.0.6 → 0.0.8
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.md +26 -12
- package/dist/src/core/index.cjs +465 -279
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +907 -631
- package/dist/src/core/index.d.ts +907 -631
- package/dist/src/core/index.js +454 -268
- package/dist/src/core/index.js.map +1 -1
- package/package.json +18 -18
package/dist/src/core/index.cjs
CHANGED
|
@@ -4,7 +4,13 @@ let _orkestrel_interpret = require("@orkestrel/interpret");
|
|
|
4
4
|
let _orkestrel_reason = require("@orkestrel/reason");
|
|
5
5
|
let _orkestrel_emitter = require("@orkestrel/emitter");
|
|
6
6
|
//#region src/core/constants.ts
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Lists the `TaskOperation` values, frozen.
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* Compose the tuple rather than restating its members: `literalOf(TASK_OPERATIONS)` builds the
|
|
12
|
+
* guard and `parseEnum(value, TASK_OPERATIONS)` coerces a bare value against it.
|
|
13
|
+
*/
|
|
8
14
|
var TASK_OPERATIONS = Object.freeze([
|
|
9
15
|
"create",
|
|
10
16
|
"refactor",
|
|
@@ -19,7 +25,7 @@ var TASK_OPERATIONS = Object.freeze([
|
|
|
19
25
|
"document",
|
|
20
26
|
"plan"
|
|
21
27
|
]);
|
|
22
|
-
/**
|
|
28
|
+
/** Lists the `TaskDomain` values, frozen. */
|
|
23
29
|
var TASK_DOMAINS = Object.freeze([
|
|
24
30
|
"code",
|
|
25
31
|
"writing",
|
|
@@ -30,7 +36,7 @@ var TASK_DOMAINS = Object.freeze([
|
|
|
30
36
|
"ops",
|
|
31
37
|
"other"
|
|
32
38
|
]);
|
|
33
|
-
/**
|
|
39
|
+
/** Lists the `OutputFormat` values, frozen. */
|
|
34
40
|
var OUTPUT_FORMATS = Object.freeze([
|
|
35
41
|
"markdown",
|
|
36
42
|
"json",
|
|
@@ -38,14 +44,14 @@ var OUTPUT_FORMATS = Object.freeze([
|
|
|
38
44
|
"diff",
|
|
39
45
|
"prose"
|
|
40
46
|
]);
|
|
41
|
-
/**
|
|
47
|
+
/** Lists the `RiskSeverity` values, frozen. */
|
|
42
48
|
var RISK_SEVERITIES = Object.freeze([
|
|
43
49
|
"low",
|
|
44
50
|
"medium",
|
|
45
51
|
"high"
|
|
46
52
|
]);
|
|
47
53
|
/**
|
|
48
|
-
*
|
|
54
|
+
* Lists every published `Interpretation` member name, frozen.
|
|
49
55
|
*
|
|
50
56
|
* @remarks
|
|
51
57
|
* The capture list `BriefCompiler` hands `captureValue` at each interpret door — the borrowed
|
|
@@ -70,33 +76,32 @@ var INTERPRETATION_MEMBERS = Object.freeze([
|
|
|
70
76
|
"prompt",
|
|
71
77
|
"stages",
|
|
72
78
|
"failures",
|
|
73
|
-
"complete",
|
|
74
79
|
"confidence",
|
|
75
80
|
"digest"
|
|
76
81
|
]);
|
|
77
82
|
/**
|
|
78
|
-
* `16` — the default turn cap `briefToGoal` renders.
|
|
83
|
+
* Holds `16` — the default turn cap `briefToGoal` renders.
|
|
79
84
|
*
|
|
80
85
|
* @remarks
|
|
81
86
|
* Domain-qualified so the barrel stays collision-free as sibling modules add their own
|
|
82
87
|
* turn defaults.
|
|
83
88
|
*/
|
|
84
89
|
var DEFAULT_BRIEF_TURNS = 16;
|
|
85
|
-
/** `'gate'` — the id of the `
|
|
90
|
+
/** Holds `'gate'` — the id of the `buildGateDefinition()` logical definition. */
|
|
86
91
|
var GATE_ID = "gate";
|
|
87
92
|
/**
|
|
88
|
-
*
|
|
93
|
+
* Matches every line terminator a brief field refuses.
|
|
89
94
|
*
|
|
90
95
|
* @remarks
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
96
|
+
* Every ECMAScript line terminator, not only `\n`: a renderer that splits on any of them
|
|
97
|
+
* would let the others forge a markdown row. CRLF leads the alternation so a Windows
|
|
98
|
+
* exemplar splits as ONE break rather than two, which would insert a blank line the caller
|
|
99
|
+
* never wrote. Kept unanchored and stateless — no `g` flag — so `test` never carries
|
|
95
100
|
* `lastIndex` between calls.
|
|
96
101
|
*/
|
|
97
102
|
var LINE_BREAK_PATTERN = /\r\n|[\n\r\u2028\u2029]/;
|
|
98
103
|
/**
|
|
99
|
-
*
|
|
104
|
+
* Holds the positive form of {@link LINE_BREAK_PATTERN}, for a `stringShape` `pattern`.
|
|
100
105
|
*
|
|
101
106
|
* @remarks
|
|
102
107
|
* `stringShape`'s `pattern` must MATCH an accepted value, so the guard's refusal regex
|
|
@@ -105,7 +110,7 @@ var LINE_BREAK_PATTERN = /\r\n|[\n\r\u2028\u2029]/;
|
|
|
105
110
|
*/
|
|
106
111
|
var SINGLE_LINE_PATTERN = /^[^\n\r\u2028\u2029]*$/;
|
|
107
112
|
/**
|
|
108
|
-
*
|
|
113
|
+
* Matches a string of one or more spaces and nothing else.
|
|
109
114
|
*
|
|
110
115
|
* @remarks
|
|
111
116
|
* The one exemplar side `exampleToLines` must NOT pad. CommonMark strips a fully-blank code
|
|
@@ -119,9 +124,12 @@ var BLANK_PATTERN = /^ +$/;
|
|
|
119
124
|
//#endregion
|
|
120
125
|
//#region src/core/errors.ts
|
|
121
126
|
/**
|
|
122
|
-
*
|
|
127
|
+
* Represents the one error class this package throws.
|
|
123
128
|
*
|
|
124
129
|
* @remarks
|
|
130
|
+
* Extends `Error` with a readonly `code` on the `BriefErrorCode` vocabulary and an optional
|
|
131
|
+
* readonly `context` record carrying whatever the raising site can supply.
|
|
132
|
+
*
|
|
125
133
|
* Throws are reserved for caller misuse: `assertBrief`, `snapshotBrief`, and `pinBrief` on
|
|
126
134
|
* off-contract data throw `INVALID`; any method after `destroy()` throws `DESTROYED`; and `BriefCompiler.gate` throws
|
|
127
135
|
* `GATE_FAILED` when a borrowed reasoner returns a non-logical result. A stage that fails
|
|
@@ -149,10 +157,10 @@ var BriefError = class extends Error {
|
|
|
149
157
|
}
|
|
150
158
|
};
|
|
151
159
|
/**
|
|
152
|
-
*
|
|
160
|
+
* Narrows a caught value to a {@link BriefError}.
|
|
153
161
|
*
|
|
154
162
|
* @param value - The caught value to inspect.
|
|
155
|
-
* @returns
|
|
163
|
+
* @returns True if `value` is a `BriefError`; false otherwise.
|
|
156
164
|
*
|
|
157
165
|
* @example
|
|
158
166
|
* ```ts
|
|
@@ -170,82 +178,100 @@ function isBriefError(value) {
|
|
|
170
178
|
}
|
|
171
179
|
//#endregion
|
|
172
180
|
//#region src/core/shapers.ts
|
|
173
|
-
/**
|
|
181
|
+
/** Describes a single-line string of any length, including empty — the shape mirror of `isText`. */
|
|
174
182
|
var textShape = (0, _orkestrel_contract.stringShape)({ pattern: SINGLE_LINE_PATTERN });
|
|
175
|
-
/**
|
|
183
|
+
/** Describes a non-empty single-line string — the shape mirror of `isLine`. */
|
|
176
184
|
var lineShape = (0, _orkestrel_contract.stringShape)({
|
|
177
185
|
min: 1,
|
|
178
186
|
pattern: SINGLE_LINE_PATTERN
|
|
179
187
|
});
|
|
180
|
-
/**
|
|
188
|
+
/**
|
|
189
|
+
* Describes the `Task` shape — closed operation and domain vocabularies plus a non-empty
|
|
190
|
+
* statement.
|
|
191
|
+
*
|
|
192
|
+
* @remarks
|
|
193
|
+
* `literalShape(TASK_OPERATIONS)` and `literalShape(TASK_DOMAINS)` compile the same tuples the
|
|
194
|
+
* guards read, and `statement` carries `min: 1`.
|
|
195
|
+
*/
|
|
181
196
|
var taskShape = (0, _orkestrel_contract.objectShape)({
|
|
182
197
|
operation: (0, _orkestrel_contract.literalShape)(TASK_OPERATIONS),
|
|
183
198
|
domain: (0, _orkestrel_contract.literalShape)(TASK_DOMAINS),
|
|
184
199
|
statement: lineShape
|
|
185
200
|
}, { description: "What the brief asks for, in one imperative sentence." });
|
|
186
|
-
/**
|
|
201
|
+
/** Describes the `Reference` shape — a path and the note that justifies listing it. */
|
|
187
202
|
var referenceShape = (0, _orkestrel_contract.objectShape)({
|
|
188
203
|
path: lineShape,
|
|
189
204
|
note: lineShape
|
|
190
205
|
}, { description: "One referenced path and why it is listed." });
|
|
191
|
-
/**
|
|
206
|
+
/**
|
|
207
|
+
* Describes the `Manifest` shape — disjoint reference partitions.
|
|
208
|
+
*
|
|
209
|
+
* @remarks
|
|
210
|
+
* Each partition is an `arrayShape(referenceShape)`; disjointness is `validateBrief`'s pass
|
|
211
|
+
* rather than the shape's.
|
|
212
|
+
*/
|
|
192
213
|
var manifestShape = (0, _orkestrel_contract.objectShape)({
|
|
193
214
|
read: (0, _orkestrel_contract.arrayShape)(referenceShape),
|
|
194
215
|
edit: (0, _orkestrel_contract.arrayShape)(referenceShape),
|
|
195
216
|
locked: (0, _orkestrel_contract.arrayShape)(referenceShape),
|
|
196
217
|
forbidden: (0, _orkestrel_contract.arrayShape)(referenceShape)
|
|
197
|
-
}, { description: "The
|
|
198
|
-
/**
|
|
218
|
+
}, { description: "The disjoint file partitions of a brief." });
|
|
219
|
+
/**
|
|
220
|
+
* Describes the `Outcome` shape — a one-based rank, the result text, and whether it gates done.
|
|
221
|
+
*
|
|
222
|
+
* @remarks
|
|
223
|
+
* `rank` is an `integerShape({ min: 1 })`, so a zero or fractional rank is off-contract.
|
|
224
|
+
*/
|
|
199
225
|
var outcomeShape = (0, _orkestrel_contract.objectShape)({
|
|
200
226
|
rank: (0, _orkestrel_contract.integerShape)({ min: 1 }),
|
|
201
227
|
text: lineShape,
|
|
202
228
|
required: (0, _orkestrel_contract.booleanShape)()
|
|
203
229
|
}, { description: "One ranked outcome — a result, never a step." });
|
|
204
|
-
/**
|
|
230
|
+
/** Describes the `Given` shape — one categorized context fact. */
|
|
205
231
|
var givenShape = (0, _orkestrel_contract.objectShape)({
|
|
206
232
|
category: lineShape,
|
|
207
233
|
name: lineShape,
|
|
208
234
|
value: textShape
|
|
209
235
|
}, { description: "One context fact handed to the executor." });
|
|
210
|
-
/**
|
|
236
|
+
/** Describes the `Example` shape — one input to output exemplar. */
|
|
211
237
|
var exampleShape = (0, _orkestrel_contract.objectShape)({
|
|
212
238
|
input: (0, _orkestrel_contract.stringShape)({ min: 1 }),
|
|
213
239
|
output: (0, _orkestrel_contract.stringShape)({ min: 1 }),
|
|
214
240
|
note: (0, _orkestrel_contract.optionalShape)(lineShape)
|
|
215
241
|
}, { description: "One input to output exemplar." });
|
|
216
|
-
/**
|
|
242
|
+
/** Describes the `Citation` shape — a name, a locator, and why the source is cited. */
|
|
217
243
|
var citationShape = (0, _orkestrel_contract.objectShape)({
|
|
218
244
|
name: lineShape,
|
|
219
245
|
url: lineShape,
|
|
220
246
|
note: lineShape
|
|
221
247
|
}, { description: "One external source; list order is the trust order." });
|
|
222
|
-
/**
|
|
248
|
+
/** Describes the `Gap` shape — an unknown, whether it blocks, and the candidates that would close it. */
|
|
223
249
|
var gapShape = (0, _orkestrel_contract.objectShape)({
|
|
224
250
|
field: lineShape,
|
|
225
251
|
question: lineShape,
|
|
226
252
|
blocking: (0, _orkestrel_contract.booleanShape)(),
|
|
227
253
|
candidates: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)(lineShape))
|
|
228
254
|
}, { description: "One unresolved decision; blocking means the gate fails closed." });
|
|
229
|
-
/**
|
|
255
|
+
/** Describes the `Risk` shape — a closed severity, the risk, and its mitigation. */
|
|
230
256
|
var riskShape = (0, _orkestrel_contract.objectShape)({
|
|
231
257
|
severity: (0, _orkestrel_contract.literalShape)(RISK_SEVERITIES),
|
|
232
258
|
text: lineShape,
|
|
233
259
|
mitigation: lineShape
|
|
234
260
|
}, { description: "One pre-empted risk and the mitigation that answers it." });
|
|
235
|
-
/**
|
|
261
|
+
/** Describes the `Output` shape — a closed format plus its optional refinements. */
|
|
236
262
|
var outputShape = (0, _orkestrel_contract.objectShape)({
|
|
237
263
|
format: (0, _orkestrel_contract.literalShape)(OUTPUT_FORMATS),
|
|
238
264
|
sections: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)(lineShape)),
|
|
239
265
|
include: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)(lineShape)),
|
|
240
266
|
exclude: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)(lineShape))
|
|
241
267
|
}, { description: "The closed shape of the deliverable." });
|
|
242
|
-
/**
|
|
268
|
+
/** Describes the `Proof` shape — the claim and the command that settles it. */
|
|
243
269
|
var proofShape = (0, _orkestrel_contract.objectShape)({
|
|
244
270
|
text: lineShape,
|
|
245
271
|
command: lineShape
|
|
246
272
|
}, { description: "One mechanical, transcript-provable check." });
|
|
247
273
|
/**
|
|
248
|
-
*
|
|
274
|
+
* Describes the whole `Brief` shape, section shapes composed.
|
|
249
275
|
*
|
|
250
276
|
* @remarks
|
|
251
277
|
* `trace` and `hash` are optional because `pinBrief` fills them; an unpinned draft is
|
|
@@ -272,40 +298,86 @@ var briefShape = (0, _orkestrel_contract.objectShape)({
|
|
|
272
298
|
//#endregion
|
|
273
299
|
//#region src/core/validators.ts
|
|
274
300
|
/**
|
|
275
|
-
*
|
|
301
|
+
* Checks whether the value is a string holding no line terminator, empty included.
|
|
276
302
|
*
|
|
277
303
|
* @remarks
|
|
278
304
|
* `briefToMarkdown` renders each brief field as ONE markdown row, so a field carrying a
|
|
279
305
|
* line break would forge a heading or an extra manifest row — which is how a rendered
|
|
280
306
|
* prompt and `briefToDispatch`'s path sets could disagree about the same brief.
|
|
307
|
+
*
|
|
308
|
+
* @param value - The value to inspect.
|
|
309
|
+
* @returns True if `value` is a string holding no line terminator, empty included; false
|
|
310
|
+
* otherwise.
|
|
281
311
|
*/
|
|
282
312
|
var isText = (value) => (0, _orkestrel_contract.isString)(value) && !LINE_BREAK_PATTERN.test(value);
|
|
283
|
-
/**
|
|
313
|
+
/**
|
|
314
|
+
* Checks whether the value is a non-empty string holding no line terminator.
|
|
315
|
+
*
|
|
316
|
+
* @remarks
|
|
317
|
+
* The shape of nearly every brief field: a path, a note, a statement, a rule, and a command
|
|
318
|
+
* all narrow through it.
|
|
319
|
+
*
|
|
320
|
+
* @param value - The value to inspect.
|
|
321
|
+
* @returns True if `value` is a non-empty string holding no line terminator; false otherwise.
|
|
322
|
+
*/
|
|
284
323
|
var isLine = (0, _orkestrel_contract.andOf)(_orkestrel_contract.isNonEmptyString, isText);
|
|
285
|
-
/**
|
|
324
|
+
/**
|
|
325
|
+
* Checks whether the value is one of the `TaskOperation` literals.
|
|
326
|
+
*
|
|
327
|
+
* @param value - The value to inspect.
|
|
328
|
+
* @returns True if `value` is one of the `TaskOperation` literals; false otherwise.
|
|
329
|
+
*/
|
|
286
330
|
var isTaskOperation = (0, _orkestrel_contract.literalOf)(TASK_OPERATIONS);
|
|
287
|
-
/**
|
|
331
|
+
/**
|
|
332
|
+
* Checks whether the value is one of the `TaskDomain` literals.
|
|
333
|
+
*
|
|
334
|
+
* @param value - The value to inspect.
|
|
335
|
+
* @returns True if `value` is one of the `TaskDomain` literals; false otherwise.
|
|
336
|
+
*/
|
|
288
337
|
var isTaskDomain = (0, _orkestrel_contract.literalOf)(TASK_DOMAINS);
|
|
289
|
-
/**
|
|
338
|
+
/**
|
|
339
|
+
* Checks whether the value is one of the `OutputFormat` literals.
|
|
340
|
+
*
|
|
341
|
+
* @param value - The value to inspect.
|
|
342
|
+
* @returns True if `value` is one of the `OutputFormat` literals; false otherwise.
|
|
343
|
+
*/
|
|
290
344
|
var isOutputFormat = (0, _orkestrel_contract.literalOf)(OUTPUT_FORMATS);
|
|
291
|
-
/**
|
|
345
|
+
/**
|
|
346
|
+
* Checks whether the value is one of the `RiskSeverity` literals.
|
|
347
|
+
*
|
|
348
|
+
* @param value - The value to inspect.
|
|
349
|
+
* @returns True if `value` is one of the `RiskSeverity` literals; false otherwise.
|
|
350
|
+
*/
|
|
292
351
|
var isRiskSeverity = (0, _orkestrel_contract.literalOf)(RISK_SEVERITIES);
|
|
293
|
-
/**
|
|
352
|
+
/**
|
|
353
|
+
* Checks whether the value is a well-formed `Task` — both vocabularies closed, statement one line.
|
|
354
|
+
*
|
|
355
|
+
* @param value - The value to inspect.
|
|
356
|
+
* @returns True if `value` is a well-formed `Task`; false otherwise.
|
|
357
|
+
*/
|
|
294
358
|
var isTask = (0, _orkestrel_contract.recordOf)({
|
|
295
359
|
operation: isTaskOperation,
|
|
296
360
|
domain: isTaskDomain,
|
|
297
361
|
statement: isLine
|
|
298
362
|
});
|
|
299
|
-
/**
|
|
363
|
+
/**
|
|
364
|
+
* Checks whether the value is a well-formed `Reference` — both members required, both single-line.
|
|
365
|
+
*
|
|
366
|
+
* @param value - The value to inspect.
|
|
367
|
+
* @returns True if `value` is a well-formed `Reference`; false otherwise.
|
|
368
|
+
*/
|
|
300
369
|
var isReference = (0, _orkestrel_contract.recordOf)({
|
|
301
370
|
path: isLine,
|
|
302
371
|
note: isLine
|
|
303
372
|
});
|
|
304
373
|
/**
|
|
305
|
-
*
|
|
374
|
+
* Checks whether the value is a well-formed `Manifest`.
|
|
306
375
|
*
|
|
307
376
|
* @remarks
|
|
308
377
|
* Partition presence only — disjointness is `validateBrief`'s semantic pass.
|
|
378
|
+
*
|
|
379
|
+
* @param value - The value to inspect.
|
|
380
|
+
* @returns True if `value` is a well-formed `Manifest`; false otherwise.
|
|
309
381
|
*/
|
|
310
382
|
var isManifest = (0, _orkestrel_contract.recordOf)({
|
|
311
383
|
read: (0, _orkestrel_contract.arrayOf)(isReference),
|
|
@@ -313,50 +385,83 @@ var isManifest = (0, _orkestrel_contract.recordOf)({
|
|
|
313
385
|
locked: (0, _orkestrel_contract.arrayOf)(isReference),
|
|
314
386
|
forbidden: (0, _orkestrel_contract.arrayOf)(isReference)
|
|
315
387
|
});
|
|
316
|
-
/**
|
|
388
|
+
/**
|
|
389
|
+
* Checks whether the value is a well-formed `Outcome` — `rank` a positive integer.
|
|
390
|
+
*
|
|
391
|
+
* @param value - The value to inspect.
|
|
392
|
+
* @returns True if `value` is a well-formed `Outcome`; false otherwise.
|
|
393
|
+
*/
|
|
317
394
|
var isOutcome = (0, _orkestrel_contract.recordOf)({
|
|
318
395
|
rank: (0, _orkestrel_contract.andOf)(_orkestrel_contract.isInteger, (0, _orkestrel_contract.boundsOf)(1)),
|
|
319
396
|
text: isLine,
|
|
320
397
|
required: _orkestrel_contract.isBoolean
|
|
321
398
|
});
|
|
322
|
-
/**
|
|
399
|
+
/**
|
|
400
|
+
* Checks whether the value is a well-formed `Given` — its `value` may be empty but stays one line.
|
|
401
|
+
*
|
|
402
|
+
* @param value - The value to inspect.
|
|
403
|
+
* @returns True if `value` is a well-formed `Given`; false otherwise.
|
|
404
|
+
*/
|
|
323
405
|
var isGiven = (0, _orkestrel_contract.recordOf)({
|
|
324
406
|
category: isLine,
|
|
325
407
|
name: isLine,
|
|
326
408
|
value: isText
|
|
327
409
|
});
|
|
328
410
|
/**
|
|
329
|
-
*
|
|
411
|
+
* Checks whether the value is a well-formed `Example`.
|
|
330
412
|
*
|
|
331
413
|
* @remarks
|
|
332
414
|
* An exemplar's two sides are the ONLY members a brief lets span lines, because they
|
|
333
415
|
* carry code. `briefToMarkdown` fences them rather than rendering them as a row.
|
|
416
|
+
*
|
|
417
|
+
* @param value - The value to inspect.
|
|
418
|
+
* @returns True if `value` is a well-formed `Example`; false otherwise.
|
|
334
419
|
*/
|
|
335
420
|
var isExample = (0, _orkestrel_contract.recordOf)({
|
|
336
421
|
input: _orkestrel_contract.isNonEmptyString,
|
|
337
422
|
output: _orkestrel_contract.isNonEmptyString,
|
|
338
423
|
note: isLine
|
|
339
424
|
}, ["note"]);
|
|
340
|
-
/**
|
|
425
|
+
/**
|
|
426
|
+
* Checks whether the value is a well-formed `Citation` — every member single-line.
|
|
427
|
+
*
|
|
428
|
+
* @param value - The value to inspect.
|
|
429
|
+
* @returns True if `value` is a well-formed `Citation`; false otherwise.
|
|
430
|
+
*/
|
|
341
431
|
var isCitation = (0, _orkestrel_contract.recordOf)({
|
|
342
432
|
name: isLine,
|
|
343
433
|
url: isLine,
|
|
344
434
|
note: isLine
|
|
345
435
|
});
|
|
346
|
-
/**
|
|
436
|
+
/**
|
|
437
|
+
* Checks whether the value is a well-formed `Gap`.
|
|
438
|
+
*
|
|
439
|
+
* @param value - The value to inspect.
|
|
440
|
+
* @returns True if `value` is a well-formed `Gap`; false otherwise.
|
|
441
|
+
*/
|
|
347
442
|
var isGap = (0, _orkestrel_contract.recordOf)({
|
|
348
443
|
field: isLine,
|
|
349
444
|
question: isLine,
|
|
350
445
|
blocking: _orkestrel_contract.isBoolean,
|
|
351
446
|
candidates: (0, _orkestrel_contract.arrayOf)(isLine)
|
|
352
447
|
}, ["candidates"]);
|
|
353
|
-
/**
|
|
448
|
+
/**
|
|
449
|
+
* Checks whether the value is a well-formed `Risk` — `severity` on the closed vocabulary.
|
|
450
|
+
*
|
|
451
|
+
* @param value - The value to inspect.
|
|
452
|
+
* @returns True if `value` is a well-formed `Risk`; false otherwise.
|
|
453
|
+
*/
|
|
354
454
|
var isRisk = (0, _orkestrel_contract.recordOf)({
|
|
355
455
|
severity: isRiskSeverity,
|
|
356
456
|
text: isLine,
|
|
357
457
|
mitigation: isLine
|
|
358
458
|
});
|
|
359
|
-
/**
|
|
459
|
+
/**
|
|
460
|
+
* Checks whether the value is a well-formed `Output` — `format` on the closed vocabulary.
|
|
461
|
+
*
|
|
462
|
+
* @param value - The value to inspect.
|
|
463
|
+
* @returns True if `value` is a well-formed `Output`; false otherwise.
|
|
464
|
+
*/
|
|
360
465
|
var isOutput = (0, _orkestrel_contract.recordOf)({
|
|
361
466
|
format: isOutputFormat,
|
|
362
467
|
sections: (0, _orkestrel_contract.arrayOf)(isLine),
|
|
@@ -367,17 +472,25 @@ var isOutput = (0, _orkestrel_contract.recordOf)({
|
|
|
367
472
|
"include",
|
|
368
473
|
"exclude"
|
|
369
474
|
]);
|
|
370
|
-
/**
|
|
475
|
+
/**
|
|
476
|
+
* Checks whether the value is a well-formed `Proof`.
|
|
477
|
+
*
|
|
478
|
+
* @param value - The value to inspect.
|
|
479
|
+
* @returns True if `value` is a well-formed `Proof`; false otherwise.
|
|
480
|
+
*/
|
|
371
481
|
var isProof = (0, _orkestrel_contract.recordOf)({
|
|
372
482
|
text: isLine,
|
|
373
483
|
command: isLine
|
|
374
484
|
});
|
|
375
485
|
/**
|
|
376
|
-
*
|
|
486
|
+
* Checks whether the value satisfies the whole exact-record `Brief` contract.
|
|
377
487
|
*
|
|
378
488
|
* @remarks
|
|
379
489
|
* Every section must be present; an extra key fails. `trace` and `hash` are the only
|
|
380
490
|
* optional members, because `pinBrief` rather than the author fills them.
|
|
491
|
+
*
|
|
492
|
+
* @param value - The value to inspect.
|
|
493
|
+
* @returns True if `value` satisfies the whole exact-record `Brief` contract; false otherwise.
|
|
381
494
|
*/
|
|
382
495
|
var isBrief = (0, _orkestrel_contract.recordOf)({
|
|
383
496
|
task: isTask,
|
|
@@ -478,7 +591,7 @@ function captureValue(source, members) {
|
|
|
478
591
|
return target;
|
|
479
592
|
}
|
|
480
593
|
/**
|
|
481
|
-
*
|
|
594
|
+
* Returns a deeply owned, deeply frozen copy of a brief, refusing anything off-contract.
|
|
482
595
|
*
|
|
483
596
|
* @remarks
|
|
484
597
|
* The one reading boundary this package has, used by the pin, the registry, and every
|
|
@@ -502,10 +615,10 @@ function captureValue(source, members) {
|
|
|
502
615
|
*
|
|
503
616
|
* @example
|
|
504
617
|
* ```ts
|
|
505
|
-
* import {
|
|
618
|
+
* import { buildBrief, buildOutcome, buildTask, snapshotBrief } from '@orkestrel/brief'
|
|
506
619
|
*
|
|
507
|
-
* const outcomes = [
|
|
508
|
-
* const owned = snapshotBrief(
|
|
620
|
+
* const outcomes = [buildOutcome(1, 'shipped')]
|
|
621
|
+
* const owned = snapshotBrief(buildBrief(buildTask('plan', 'ops', 'Plan the release.'), { outcomes }))
|
|
509
622
|
* owned.outcomes === outcomes // false — the alias is broken
|
|
510
623
|
* Object.isFrozen(owned.outcomes) // true
|
|
511
624
|
* ```
|
|
@@ -518,7 +631,7 @@ function snapshotBrief(source) {
|
|
|
518
631
|
//#endregion
|
|
519
632
|
//#region src/core/helpers.ts
|
|
520
633
|
/**
|
|
521
|
-
*
|
|
634
|
+
* Assembles a `Task` from an operation, a domain, and a statement.
|
|
522
635
|
*
|
|
523
636
|
* @param operation - What the brief asks for, from the closed operation vocabulary.
|
|
524
637
|
* @param domain - The subject matter, from the closed domain vocabulary.
|
|
@@ -527,12 +640,12 @@ function snapshotBrief(source) {
|
|
|
527
640
|
*
|
|
528
641
|
* @example
|
|
529
642
|
* ```ts
|
|
530
|
-
* import {
|
|
643
|
+
* import { buildTask } from '@orkestrel/brief'
|
|
531
644
|
*
|
|
532
|
-
*
|
|
645
|
+
* buildTask('refactor', 'code', 'Refactor useForm to native browser form APIs.')
|
|
533
646
|
* ```
|
|
534
647
|
*/
|
|
535
|
-
function
|
|
648
|
+
function buildTask(operation, domain, statement) {
|
|
536
649
|
return {
|
|
537
650
|
operation,
|
|
538
651
|
domain,
|
|
@@ -540,7 +653,11 @@ function task(operation, domain, statement) {
|
|
|
540
653
|
};
|
|
541
654
|
}
|
|
542
655
|
/**
|
|
543
|
-
*
|
|
656
|
+
* Assembles a `Reference` from a path and the note that justifies listing it.
|
|
657
|
+
*
|
|
658
|
+
* @remarks
|
|
659
|
+
* The one builder for an authority entry and a manifest entry alike: the container the record
|
|
660
|
+
* lands in is what says whether the path is ranked or permitted.
|
|
544
661
|
*
|
|
545
662
|
* @param path - The referenced path or glob.
|
|
546
663
|
* @param note - Why the path is listed.
|
|
@@ -548,31 +665,31 @@ function task(operation, domain, statement) {
|
|
|
548
665
|
*
|
|
549
666
|
* @example
|
|
550
667
|
* ```ts
|
|
551
|
-
* import {
|
|
668
|
+
* import { buildReference } from '@orkestrel/brief'
|
|
552
669
|
*
|
|
553
|
-
*
|
|
670
|
+
* buildReference('AGENTS.md', 'project law') // { path: 'AGENTS.md', note: 'project law' }
|
|
554
671
|
* ```
|
|
555
672
|
*/
|
|
556
|
-
function
|
|
673
|
+
function buildReference(path, note) {
|
|
557
674
|
return {
|
|
558
675
|
path,
|
|
559
676
|
note
|
|
560
677
|
};
|
|
561
678
|
}
|
|
562
679
|
/**
|
|
563
|
-
*
|
|
680
|
+
* Assembles a `Manifest`, defaulting every absent partition to an empty list.
|
|
564
681
|
*
|
|
565
682
|
* @param partitions - The partitions to fill; a partial literal is enough.
|
|
566
|
-
* @returns A fresh `Manifest` with
|
|
683
|
+
* @returns A fresh `Manifest` with every partition present.
|
|
567
684
|
*
|
|
568
685
|
* @example
|
|
569
686
|
* ```ts
|
|
570
|
-
* import {
|
|
687
|
+
* import { buildManifest, buildReference } from '@orkestrel/brief'
|
|
571
688
|
*
|
|
572
|
-
*
|
|
689
|
+
* buildManifest({ edit: [buildReference('src/core/helpers.ts', 'implementation')] })
|
|
573
690
|
* ```
|
|
574
691
|
*/
|
|
575
|
-
function
|
|
692
|
+
function buildManifest(partitions) {
|
|
576
693
|
return {
|
|
577
694
|
read: partitions?.read ?? [],
|
|
578
695
|
edit: partitions?.edit ?? [],
|
|
@@ -581,22 +698,23 @@ function manifest(partitions) {
|
|
|
581
698
|
};
|
|
582
699
|
}
|
|
583
700
|
/**
|
|
584
|
-
*
|
|
701
|
+
* Assembles an `Outcome` from a rank and its result text.
|
|
585
702
|
*
|
|
586
703
|
* @param rank - The one-based rank; lower ranks matter more.
|
|
587
704
|
* @param text - The result, never a step.
|
|
588
|
-
* @param required -
|
|
705
|
+
* @param required - If `true`, the outcome gates "done"; if `false`, it is desirable but not
|
|
706
|
+
* blocking. Default: `true`.
|
|
589
707
|
* @returns A fresh `Outcome`.
|
|
590
708
|
*
|
|
591
709
|
* @example
|
|
592
710
|
* ```ts
|
|
593
|
-
* import {
|
|
711
|
+
* import { buildOutcome } from '@orkestrel/brief'
|
|
594
712
|
*
|
|
595
|
-
*
|
|
596
|
-
*
|
|
713
|
+
* buildOutcome(1, 'useForm uses native FormData with no behavior change') // required: true
|
|
714
|
+
* buildOutcome(2, 'the diff stays under 200 lines', false)
|
|
597
715
|
* ```
|
|
598
716
|
*/
|
|
599
|
-
function
|
|
717
|
+
function buildOutcome(rank, text, required = true) {
|
|
600
718
|
return {
|
|
601
719
|
rank,
|
|
602
720
|
text,
|
|
@@ -604,7 +722,7 @@ function outcome(rank, text, required = true) {
|
|
|
604
722
|
};
|
|
605
723
|
}
|
|
606
724
|
/**
|
|
607
|
-
*
|
|
725
|
+
* Assembles a `Given` from a category, a name, and a value.
|
|
608
726
|
*
|
|
609
727
|
* @param category - The kind of fact — a convention, a version, a constraint.
|
|
610
728
|
* @param name - The fact's name.
|
|
@@ -613,12 +731,12 @@ function outcome(rank, text, required = true) {
|
|
|
613
731
|
*
|
|
614
732
|
* @example
|
|
615
733
|
* ```ts
|
|
616
|
-
* import {
|
|
734
|
+
* import { buildGiven } from '@orkestrel/brief'
|
|
617
735
|
*
|
|
618
|
-
*
|
|
736
|
+
* buildGiven('convention', 'indentation', 'tabs')
|
|
619
737
|
* ```
|
|
620
738
|
*/
|
|
621
|
-
function
|
|
739
|
+
function buildGiven(category, name, value) {
|
|
622
740
|
return {
|
|
623
741
|
category,
|
|
624
742
|
name,
|
|
@@ -626,32 +744,32 @@ function given(category, name, value) {
|
|
|
626
744
|
};
|
|
627
745
|
}
|
|
628
746
|
/**
|
|
629
|
-
*
|
|
747
|
+
* Assembles an `Example` from an exemplar input and its expected output.
|
|
630
748
|
*
|
|
631
749
|
* @param input - The exemplar input.
|
|
632
|
-
* @param
|
|
750
|
+
* @param output - The expected output for that input.
|
|
633
751
|
* @param note - Optional detail; the key is OMITTED when absent.
|
|
634
752
|
* @returns A fresh `Example`.
|
|
635
753
|
*
|
|
636
754
|
* @example
|
|
637
755
|
* ```ts
|
|
638
|
-
* import {
|
|
756
|
+
* import { buildExample } from '@orkestrel/brief'
|
|
639
757
|
*
|
|
640
|
-
*
|
|
758
|
+
* buildExample('<input required>', 'validity read from el.validity')
|
|
641
759
|
* ```
|
|
642
760
|
*/
|
|
643
|
-
function
|
|
761
|
+
function buildExample(input, output, note) {
|
|
644
762
|
return note === void 0 ? {
|
|
645
763
|
input,
|
|
646
|
-
output
|
|
764
|
+
output
|
|
647
765
|
} : {
|
|
648
766
|
input,
|
|
649
|
-
output
|
|
767
|
+
output,
|
|
650
768
|
note
|
|
651
769
|
};
|
|
652
770
|
}
|
|
653
771
|
/**
|
|
654
|
-
*
|
|
772
|
+
* Assembles a `Citation` from a name, a URL, and the note that justifies citing it.
|
|
655
773
|
*
|
|
656
774
|
* @param name - The source's display name.
|
|
657
775
|
* @param url - Where the source lives.
|
|
@@ -660,16 +778,16 @@ function example(input, result, note) {
|
|
|
660
778
|
*
|
|
661
779
|
* @example
|
|
662
780
|
* ```ts
|
|
663
|
-
* import {
|
|
781
|
+
* import { buildCitation } from '@orkestrel/brief'
|
|
664
782
|
*
|
|
665
|
-
*
|
|
783
|
+
* buildCitation(
|
|
666
784
|
* 'MDN Constraint Validation',
|
|
667
785
|
* 'https://developer.mozilla.org/',
|
|
668
786
|
* 'the native validity behavior being adopted',
|
|
669
787
|
* )
|
|
670
788
|
* ```
|
|
671
789
|
*/
|
|
672
|
-
function
|
|
790
|
+
function buildCitation(name, url, note) {
|
|
673
791
|
return {
|
|
674
792
|
name,
|
|
675
793
|
url,
|
|
@@ -677,23 +795,23 @@ function citation(name, url, note) {
|
|
|
677
795
|
};
|
|
678
796
|
}
|
|
679
797
|
/**
|
|
680
|
-
*
|
|
798
|
+
* Assembles a `Gap` from the section it belongs to and the question that would close it.
|
|
681
799
|
*
|
|
682
800
|
* @param field - The brief section the unknown belongs to.
|
|
683
801
|
* @param question - The question that would close it.
|
|
684
|
-
* @param overrides - Optional `blocking`
|
|
685
|
-
*
|
|
802
|
+
* @param overrides - Optional `blocking` and `candidates`; an absent `candidates` key is
|
|
803
|
+
* OMITTED entirely. Default: `blocking: false`.
|
|
686
804
|
* @returns A fresh `Gap`.
|
|
687
805
|
*
|
|
688
806
|
* @example
|
|
689
807
|
* ```ts
|
|
690
|
-
* import {
|
|
808
|
+
* import { buildGap } from '@orkestrel/brief'
|
|
691
809
|
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
810
|
+
* buildGap('rules', 'Does validation message wording need to change?') // blocking: false
|
|
811
|
+
* buildGap('output', 'Diff or full files?', { blocking: true, candidates: ['diff', 'code'] })
|
|
694
812
|
* ```
|
|
695
813
|
*/
|
|
696
|
-
function
|
|
814
|
+
function buildGap(field, question, overrides) {
|
|
697
815
|
const blocking = overrides?.blocking ?? false;
|
|
698
816
|
return overrides?.candidates === void 0 ? {
|
|
699
817
|
field,
|
|
@@ -707,7 +825,7 @@ function gap(field, question, overrides) {
|
|
|
707
825
|
};
|
|
708
826
|
}
|
|
709
827
|
/**
|
|
710
|
-
*
|
|
828
|
+
* Assembles a `Risk` from a severity, what could go wrong, and the mitigation that answers it.
|
|
711
829
|
*
|
|
712
830
|
* @param severity - The closed severity.
|
|
713
831
|
* @param text - What could go wrong.
|
|
@@ -716,12 +834,12 @@ function gap(field, question, overrides) {
|
|
|
716
834
|
*
|
|
717
835
|
* @example
|
|
718
836
|
* ```ts
|
|
719
|
-
* import {
|
|
837
|
+
* import { buildRisk } from '@orkestrel/brief'
|
|
720
838
|
*
|
|
721
|
-
*
|
|
839
|
+
* buildRisk('medium', 'native validation differs subtly', 'assert message and state in tests')
|
|
722
840
|
* ```
|
|
723
841
|
*/
|
|
724
|
-
function
|
|
842
|
+
function buildRisk(severity, text, mitigation) {
|
|
725
843
|
return {
|
|
726
844
|
severity,
|
|
727
845
|
text,
|
|
@@ -729,7 +847,7 @@ function risk(severity, text, mitigation) {
|
|
|
729
847
|
};
|
|
730
848
|
}
|
|
731
849
|
/**
|
|
732
|
-
*
|
|
850
|
+
* Assembles an `Output` from a format plus its optional refinements.
|
|
733
851
|
*
|
|
734
852
|
* @param format - The closed deliverable format.
|
|
735
853
|
* @param overrides - Optional `sections` / `include` / `exclude`; absent keys are OMITTED.
|
|
@@ -737,13 +855,13 @@ function risk(severity, text, mitigation) {
|
|
|
737
855
|
*
|
|
738
856
|
* @example
|
|
739
857
|
* ```ts
|
|
740
|
-
* import {
|
|
858
|
+
* import { buildOutput } from '@orkestrel/brief'
|
|
741
859
|
*
|
|
742
|
-
*
|
|
743
|
-
*
|
|
860
|
+
* buildOutput('markdown') // { format: 'markdown' }
|
|
861
|
+
* buildOutput('diff', { include: ['updated useForm.ts'] })
|
|
744
862
|
* ```
|
|
745
863
|
*/
|
|
746
|
-
function
|
|
864
|
+
function buildOutput(format, overrides) {
|
|
747
865
|
return {
|
|
748
866
|
format,
|
|
749
867
|
...overrides?.sections === void 0 ? {} : { sections: overrides.sections },
|
|
@@ -752,7 +870,7 @@ function output(format, overrides) {
|
|
|
752
870
|
};
|
|
753
871
|
}
|
|
754
872
|
/**
|
|
755
|
-
*
|
|
873
|
+
* Assembles a `Proof` from what the check settles and the command that settles it.
|
|
756
874
|
*
|
|
757
875
|
* @param text - What the check settles.
|
|
758
876
|
* @param command - The command whose exit signal settles it.
|
|
@@ -760,41 +878,41 @@ function output(format, overrides) {
|
|
|
760
878
|
*
|
|
761
879
|
* @example
|
|
762
880
|
* ```ts
|
|
763
|
-
* import {
|
|
881
|
+
* import { buildProof } from '@orkestrel/brief'
|
|
764
882
|
*
|
|
765
|
-
*
|
|
883
|
+
* buildProof('type-check and lint pass', 'npm run check')
|
|
766
884
|
* ```
|
|
767
885
|
*/
|
|
768
|
-
function
|
|
886
|
+
function buildProof(text, command) {
|
|
769
887
|
return {
|
|
770
888
|
text,
|
|
771
889
|
command
|
|
772
890
|
};
|
|
773
891
|
}
|
|
774
892
|
/**
|
|
775
|
-
*
|
|
893
|
+
* Assembles a `Brief` from a `Task` plus section overrides.
|
|
776
894
|
*
|
|
777
895
|
* @param subject - The task the brief is about.
|
|
778
|
-
* @param overrides - Any sections to fill;
|
|
779
|
-
* `
|
|
780
|
-
* `
|
|
896
|
+
* @param overrides - Any sections to fill; `trace` / `hash` stay OMITTED so `pinBrief` can
|
|
897
|
+
* fill them. Default: `[]` for every absent collection and `buildOutput('markdown')` for
|
|
898
|
+
* `output`.
|
|
781
899
|
* @returns A fresh, unpinned `Brief`.
|
|
782
900
|
*
|
|
783
901
|
* @example
|
|
784
902
|
* ```ts
|
|
785
|
-
* import {
|
|
903
|
+
* import { buildBrief, buildOutcome, buildProof, buildTask } from '@orkestrel/brief'
|
|
786
904
|
*
|
|
787
|
-
*
|
|
788
|
-
* outcomes: [
|
|
789
|
-
* proofs: [
|
|
905
|
+
* buildBrief(buildTask('audit', 'code', 'Audit the barrel for undocumented exports.'), {
|
|
906
|
+
* outcomes: [buildOutcome(1, 'every export appears in the guide')],
|
|
907
|
+
* proofs: [buildProof('parity passes', 'npm run test:guides')],
|
|
790
908
|
* })
|
|
791
909
|
* ```
|
|
792
910
|
*/
|
|
793
|
-
function
|
|
911
|
+
function buildBrief(subject, overrides) {
|
|
794
912
|
return {
|
|
795
913
|
task: subject,
|
|
796
914
|
authority: overrides?.authority ?? [],
|
|
797
|
-
manifest: overrides?.manifest ??
|
|
915
|
+
manifest: overrides?.manifest ?? buildManifest(),
|
|
798
916
|
outcomes: overrides?.outcomes ?? [],
|
|
799
917
|
rules: overrides?.rules ?? [],
|
|
800
918
|
invariants: overrides?.invariants ?? [],
|
|
@@ -804,17 +922,17 @@ function brief(subject, overrides) {
|
|
|
804
922
|
citations: overrides?.citations ?? [],
|
|
805
923
|
gaps: overrides?.gaps ?? [],
|
|
806
924
|
risks: overrides?.risks ?? [],
|
|
807
|
-
output: overrides?.output ??
|
|
925
|
+
output: overrides?.output ?? buildOutput("markdown"),
|
|
808
926
|
proofs: overrides?.proofs ?? []
|
|
809
927
|
};
|
|
810
928
|
}
|
|
811
929
|
/**
|
|
812
|
-
*
|
|
930
|
+
* Assembles the fail-closed readiness gate as a reasons `LogicalDefinition`.
|
|
813
931
|
*
|
|
814
932
|
* @remarks
|
|
815
|
-
*
|
|
816
|
-
*
|
|
817
|
-
*
|
|
933
|
+
* Each readiness rule derives one named fact from `briefToSubject`'s measures, and a final
|
|
934
|
+
* `ready` rule conjoins them all. Forward chaining reports the LAST rule's conclusion, so
|
|
935
|
+
* `LogicalResult.conclusion` is exactly `ready`.
|
|
818
936
|
*
|
|
819
937
|
* The gate takes NO parameters, and that is deliberate rather than unfinished. The
|
|
820
938
|
* reasoner overlays every derived fact into one flat namespace, so a caller rule named
|
|
@@ -828,50 +946,50 @@ function brief(subject, overrides) {
|
|
|
828
946
|
*
|
|
829
947
|
* @example
|
|
830
948
|
* ```ts
|
|
831
|
-
* import { briefToSubject,
|
|
949
|
+
* import { briefToSubject, buildGateDefinition } from '@orkestrel/brief'
|
|
832
950
|
* import { createLogicalReasoner, createReason } from '@orkestrel/reason'
|
|
833
951
|
*
|
|
834
952
|
* const reason = createReason({ reasoners: [createLogicalReasoner()] })
|
|
835
|
-
* const verdict = reason.reason(briefToSubject(pinned),
|
|
953
|
+
* const verdict = reason.reason(briefToSubject(pinned), buildGateDefinition())
|
|
836
954
|
* reason.destroy()
|
|
837
955
|
* ```
|
|
838
956
|
*/
|
|
839
|
-
function
|
|
957
|
+
function buildGateDefinition() {
|
|
840
958
|
const readiness = [
|
|
841
|
-
(0, _orkestrel_reason.
|
|
842
|
-
(0, _orkestrel_reason.
|
|
843
|
-
(0, _orkestrel_reason.
|
|
844
|
-
(0, _orkestrel_reason.
|
|
845
|
-
(0, _orkestrel_reason.
|
|
846
|
-
(0, _orkestrel_reason.
|
|
959
|
+
(0, _orkestrel_reason.createRule)("specified", [(0, _orkestrel_reason.createAtom)("blocking", "equals", 0)], (0, _orkestrel_reason.createAtom)("specified", "equals", true)),
|
|
960
|
+
(0, _orkestrel_reason.createRule)("aimed", [(0, _orkestrel_reason.createCompound)("and", [(0, _orkestrel_reason.createAtom)("outcomes", "above", 0), (0, _orkestrel_reason.createAtom)("required", "above", 0)])], (0, _orkestrel_reason.createAtom)("aimed", "equals", true)),
|
|
961
|
+
(0, _orkestrel_reason.createRule)("proven", [(0, _orkestrel_reason.createAtom)("proofs", "above", 0)], (0, _orkestrel_reason.createAtom)("proven", "equals", true)),
|
|
962
|
+
(0, _orkestrel_reason.createRule)("disjoint", [(0, _orkestrel_reason.createAtom)("overlaps", "equals", 0)], (0, _orkestrel_reason.createAtom)("disjoint", "equals", true)),
|
|
963
|
+
(0, _orkestrel_reason.createRule)("granted", [(0, _orkestrel_reason.createAtom)("ungranted", "equals", 0)], (0, _orkestrel_reason.createAtom)("granted", "equals", true)),
|
|
964
|
+
(0, _orkestrel_reason.createRule)("single", [(0, _orkestrel_reason.createAtom)("sentences", "equals", 1)], (0, _orkestrel_reason.createAtom)("single", "equals", true))
|
|
847
965
|
];
|
|
848
|
-
return (0, _orkestrel_reason.
|
|
966
|
+
return (0, _orkestrel_reason.createLogicalDefinition)(GATE_ID, "Brief readiness", [...readiness, (0, _orkestrel_reason.createRule)("ready", [(0, _orkestrel_reason.createCompound)("and", readiness.map((entry) => (0, _orkestrel_reason.createAtom)(entry.id, "equals", true)))], (0, _orkestrel_reason.createAtom)("ready", "equals", true))]);
|
|
849
967
|
}
|
|
850
968
|
/**
|
|
851
|
-
*
|
|
969
|
+
* Lists the readiness rules a brief fails, computed directly from its own measures.
|
|
852
970
|
*
|
|
853
971
|
* @remarks
|
|
854
|
-
* The gate's decision, in code. `
|
|
972
|
+
* The gate's decision, in code. `buildGateDefinition()` states the same rules as data for a
|
|
855
973
|
* reasoner to narrate, and a narration is not a decision: `BriefCompilerOptions.reason` lets a
|
|
856
974
|
* caller supply the engine, and an engine that answers "met" to everything would otherwise
|
|
857
975
|
* emit a brief with no proofs. `compile` refuses on THIS and keeps the verdict for its
|
|
858
976
|
* trace, so a supplied engine can add detail and never remove a refusal.
|
|
859
977
|
*
|
|
860
|
-
* The
|
|
861
|
-
* is what stops
|
|
978
|
+
* The data and the code must agree. `tests/src/core/helpers.test.ts` drives both over one
|
|
979
|
+
* value set, which is what stops them from drifting apart.
|
|
862
980
|
*
|
|
863
981
|
* @param source - The brief to measure.
|
|
864
982
|
* @returns The unmet rule ids, in gate order; empty when the brief is ready.
|
|
865
983
|
*
|
|
866
984
|
* @example
|
|
867
985
|
* ```ts
|
|
868
|
-
* import {
|
|
986
|
+
* import { buildBrief, buildOutcome, buildProof, buildTask, findUnmetRules } from '@orkestrel/brief'
|
|
869
987
|
*
|
|
870
|
-
* findUnmetRules(
|
|
988
|
+
* findUnmetRules(buildBrief(buildTask('plan', 'ops', 'Plan the release.'))) // ['aimed', 'proven']
|
|
871
989
|
* findUnmetRules(
|
|
872
|
-
*
|
|
873
|
-
* outcomes: [
|
|
874
|
-
* proofs: [
|
|
990
|
+
* buildBrief(buildTask('plan', 'ops', 'Plan the release.'), {
|
|
991
|
+
* outcomes: [buildOutcome(1, 'shipped')],
|
|
992
|
+
* proofs: [buildProof('x', 'npm test')],
|
|
875
993
|
* }),
|
|
876
994
|
* ) // []
|
|
877
995
|
* ```
|
|
@@ -887,7 +1005,7 @@ function findUnmetRules(source) {
|
|
|
887
1005
|
return unready;
|
|
888
1006
|
}
|
|
889
1007
|
/**
|
|
890
|
-
*
|
|
1008
|
+
* Counts the sentences a statement holds.
|
|
891
1009
|
*
|
|
892
1010
|
* @remarks
|
|
893
1011
|
* A terminator run (`.`, `!`, `?`) followed by whitespace or the end of the text closes one
|
|
@@ -925,17 +1043,21 @@ function countSentences(statement) {
|
|
|
925
1043
|
return /[.!?]$/u.test(text) ? matches.length : matches.length + 1;
|
|
926
1044
|
}
|
|
927
1045
|
/**
|
|
928
|
-
*
|
|
1046
|
+
* Lists the gaps that block emission.
|
|
1047
|
+
*
|
|
1048
|
+
* @remarks
|
|
1049
|
+
* A non-empty result means the gate must fail closed: a blocking gap has no safe default, so
|
|
1050
|
+
* the compile yields a visible incomplete `Briefing` carrying the questions instead of a brief.
|
|
929
1051
|
*
|
|
930
1052
|
* @param source - The brief to inspect.
|
|
931
1053
|
* @returns Every gap carrying `blocking: true`, in declaration order.
|
|
932
1054
|
*
|
|
933
1055
|
* @example
|
|
934
1056
|
* ```ts
|
|
935
|
-
* import {
|
|
1057
|
+
* import { buildBrief, buildGap, buildTask, findBlockingGaps } from '@orkestrel/brief'
|
|
936
1058
|
*
|
|
937
|
-
* const draft =
|
|
938
|
-
* gaps: [
|
|
1059
|
+
* const draft = buildBrief(buildTask('plan', 'ops', 'Plan the release.'), {
|
|
1060
|
+
* gaps: [buildGap('output', 'Diff or files?', { blocking: true })],
|
|
939
1061
|
* })
|
|
940
1062
|
* findBlockingGaps(draft).length // 1
|
|
941
1063
|
* ```
|
|
@@ -944,18 +1066,18 @@ function findBlockingGaps(source) {
|
|
|
944
1066
|
return source.gaps.filter((entry) => entry.blocking);
|
|
945
1067
|
}
|
|
946
1068
|
/**
|
|
947
|
-
*
|
|
1069
|
+
* Lists the authority paths the manifest never grants access to.
|
|
948
1070
|
*
|
|
949
1071
|
* @remarks
|
|
950
1072
|
* An authority the executor cannot open is an instruction it cannot follow, so every ranked
|
|
951
|
-
* path must appear in `read`, `edit`, or `locked`. Those
|
|
1073
|
+
* path must appear in `read`, `edit`, or `locked`. Those are the grants: `locked` is a
|
|
952
1074
|
* grant, because read-only is exactly what obeying a file requires.
|
|
953
1075
|
*
|
|
954
|
-
* This subsumes the narrower question of an authority sitting in `forbidden`. The
|
|
955
|
-
*
|
|
956
|
-
*
|
|
957
|
-
*
|
|
958
|
-
*
|
|
1076
|
+
* This subsumes the narrower question of an authority sitting in `forbidden`. The partitions
|
|
1077
|
+
* are disjoint — `findManifestOverlaps` and the `disjoint` rule enforce it — so a forbidden
|
|
1078
|
+
* path is in none of the grants and is reported here. An authority named in NO partition at
|
|
1079
|
+
* all is reported for the same reason, and that is the case a forbidden-only check misses
|
|
1080
|
+
* entirely: the brief never says the executor may open what it must obey.
|
|
959
1081
|
*
|
|
960
1082
|
* Paths are compared as EXACT strings, matching `findManifestOverlaps`. A glob is never
|
|
961
1083
|
* expanded, so `read: 'guides/**'` does not grant `authority: 'guides/brief.md'`. State a
|
|
@@ -966,11 +1088,17 @@ function findBlockingGaps(source) {
|
|
|
966
1088
|
*
|
|
967
1089
|
* @example
|
|
968
1090
|
* ```ts
|
|
969
|
-
* import {
|
|
970
|
-
*
|
|
971
|
-
*
|
|
972
|
-
*
|
|
973
|
-
*
|
|
1091
|
+
* import {
|
|
1092
|
+
* buildBrief,
|
|
1093
|
+
* buildManifest,
|
|
1094
|
+
* buildReference,
|
|
1095
|
+
* buildTask,
|
|
1096
|
+
* findUngrantedAuthority,
|
|
1097
|
+
* } from '@orkestrel/brief'
|
|
1098
|
+
*
|
|
1099
|
+
* const draft = buildBrief(buildTask('debug', 'code', 'Fix the leak.'), {
|
|
1100
|
+
* authority: [buildReference('AGENTS.md', 'project law')],
|
|
1101
|
+
* manifest: buildManifest(),
|
|
974
1102
|
* })
|
|
975
1103
|
* findUngrantedAuthority(draft) // ['AGENTS.md'] — ranked, but no partition opens it
|
|
976
1104
|
* ```
|
|
@@ -986,10 +1114,10 @@ function findUngrantedAuthority(source) {
|
|
|
986
1114
|
return ungranted;
|
|
987
1115
|
}
|
|
988
1116
|
/**
|
|
989
|
-
*
|
|
1117
|
+
* Lists the paths appearing in more than one manifest partition.
|
|
990
1118
|
*
|
|
991
1119
|
* @remarks
|
|
992
|
-
* Duplicates WITHIN one partition are not an overlap; the
|
|
1120
|
+
* Duplicates WITHIN one partition are not an overlap; the partitions must be
|
|
993
1121
|
* mutually disjoint, which is what `validateBrief` errors on.
|
|
994
1122
|
*
|
|
995
1123
|
* Paths are compared as EXACT strings. A glob is never expanded, so `edit: 'app/file.ts'`
|
|
@@ -1001,12 +1129,18 @@ function findUngrantedAuthority(source) {
|
|
|
1001
1129
|
*
|
|
1002
1130
|
* @example
|
|
1003
1131
|
* ```ts
|
|
1004
|
-
* import {
|
|
1005
|
-
*
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1008
|
-
*
|
|
1009
|
-
*
|
|
1132
|
+
* import {
|
|
1133
|
+
* buildBrief,
|
|
1134
|
+
* buildManifest,
|
|
1135
|
+
* buildReference,
|
|
1136
|
+
* buildTask,
|
|
1137
|
+
* findManifestOverlaps,
|
|
1138
|
+
* } from '@orkestrel/brief'
|
|
1139
|
+
*
|
|
1140
|
+
* const draft = buildBrief(buildTask('debug', 'code', 'Fix the leak.'), {
|
|
1141
|
+
* manifest: buildManifest({
|
|
1142
|
+
* edit: [buildReference('src/core/BriefCompiler.ts', 'the leaking pipeline')],
|
|
1143
|
+
* locked: [buildReference('src/core/BriefCompiler.ts', 'the published contract')],
|
|
1010
1144
|
* }),
|
|
1011
1145
|
* })
|
|
1012
1146
|
* findManifestOverlaps(draft) // ['src/core/BriefCompiler.ts']
|
|
@@ -1026,7 +1160,7 @@ function findManifestOverlaps(source) {
|
|
|
1026
1160
|
return overlaps;
|
|
1027
1161
|
}
|
|
1028
1162
|
/**
|
|
1029
|
-
*
|
|
1163
|
+
* Lists the open gaps with no assumption to stand on.
|
|
1030
1164
|
*
|
|
1031
1165
|
* @remarks
|
|
1032
1166
|
* The discipline is exactly one recorded assumption per open gap, so the open gaps past
|
|
@@ -1038,10 +1172,10 @@ function findManifestOverlaps(source) {
|
|
|
1038
1172
|
*
|
|
1039
1173
|
* @example
|
|
1040
1174
|
* ```ts
|
|
1041
|
-
* import {
|
|
1175
|
+
* import { buildBrief, buildGap, buildTask, findUnpairedGaps } from '@orkestrel/brief'
|
|
1042
1176
|
*
|
|
1043
|
-
* const draft =
|
|
1044
|
-
* gaps: [
|
|
1177
|
+
* const draft = buildBrief(buildTask('plan', 'ops', 'Plan the release.'), {
|
|
1178
|
+
* gaps: [buildGap('rules', 'Keep the wording?'), buildGap('output', 'Diff or files?')],
|
|
1045
1179
|
* assumptions: ['Wording is preserved.'],
|
|
1046
1180
|
* })
|
|
1047
1181
|
* findUnpairedGaps(draft).length // 1
|
|
@@ -1051,16 +1185,16 @@ function findUnpairedGaps(source) {
|
|
|
1051
1185
|
return source.gaps.filter((entry) => !entry.blocking).slice(source.assumptions.length);
|
|
1052
1186
|
}
|
|
1053
1187
|
/**
|
|
1054
|
-
*
|
|
1188
|
+
* Projects a brief into the reasons `Subject` of readiness measures the gate reads.
|
|
1055
1189
|
*
|
|
1056
1190
|
* @param source - The brief to measure.
|
|
1057
|
-
* @returns A flat record of counts plus the task's
|
|
1191
|
+
* @returns A flat record of counts plus the task's vocabulary values.
|
|
1058
1192
|
*
|
|
1059
1193
|
* @example
|
|
1060
1194
|
* ```ts
|
|
1061
|
-
* import {
|
|
1195
|
+
* import { briefToSubject, buildBrief, buildProof, buildTask } from '@orkestrel/brief'
|
|
1062
1196
|
*
|
|
1063
|
-
* briefToSubject(
|
|
1197
|
+
* briefToSubject(buildBrief(buildTask('test', 'code', 'Cover the gate.'), { proofs: [buildProof('x', 'y')] }))
|
|
1064
1198
|
* // { operation: 'test', domain: 'code', sentences: 1, proofs: 1, … }
|
|
1065
1199
|
* ```
|
|
1066
1200
|
*/
|
|
@@ -1087,7 +1221,7 @@ function briefToSubject(source) {
|
|
|
1087
1221
|
};
|
|
1088
1222
|
}
|
|
1089
1223
|
/**
|
|
1090
|
-
*
|
|
1224
|
+
* Runs the semantic pass over an already-shape-valid brief.
|
|
1091
1225
|
*
|
|
1092
1226
|
* @remarks
|
|
1093
1227
|
* ERRORS are the structural violations no assumption can paper over: a manifest
|
|
@@ -1101,11 +1235,11 @@ function briefToSubject(source) {
|
|
|
1101
1235
|
*
|
|
1102
1236
|
* @example
|
|
1103
1237
|
* ```ts
|
|
1104
|
-
* import {
|
|
1238
|
+
* import { buildBrief, buildProof, buildTask, validateBrief } from '@orkestrel/brief'
|
|
1105
1239
|
*
|
|
1106
|
-
* validateBrief(
|
|
1240
|
+
* validateBrief(buildBrief(buildTask('plan', 'ops', 'Plan the release.'))) // valid: false — no proofs
|
|
1107
1241
|
* validateBrief(
|
|
1108
|
-
*
|
|
1242
|
+
* buildBrief(buildTask('plan', 'ops', 'Plan the release.'), { proofs: [buildProof('ok', 'npm test')] }),
|
|
1109
1243
|
* ) // valid: true
|
|
1110
1244
|
* ```
|
|
1111
1245
|
*/
|
|
@@ -1133,7 +1267,7 @@ function validateBrief(source) {
|
|
|
1133
1267
|
};
|
|
1134
1268
|
}
|
|
1135
1269
|
/**
|
|
1136
|
-
*
|
|
1270
|
+
* Computes the canonical structural digest of a brief's content.
|
|
1137
1271
|
*
|
|
1138
1272
|
* @remarks
|
|
1139
1273
|
* `trace` and `hash` are stripped before digesting, so the value is the identity of what
|
|
@@ -1145,9 +1279,9 @@ function validateBrief(source) {
|
|
|
1145
1279
|
*
|
|
1146
1280
|
* @example
|
|
1147
1281
|
* ```ts
|
|
1148
|
-
* import {
|
|
1282
|
+
* import { briefToHash, buildBrief, buildTask, pinBrief } from '@orkestrel/brief'
|
|
1149
1283
|
*
|
|
1150
|
-
* const draft =
|
|
1284
|
+
* const draft = buildBrief(buildTask('plan', 'ops', 'Plan the release.'))
|
|
1151
1285
|
* briefToHash(draft) === briefToHash(pinBrief(draft)) // true — pinning does not move it
|
|
1152
1286
|
* ```
|
|
1153
1287
|
*/
|
|
@@ -1155,7 +1289,7 @@ function briefToHash(source) {
|
|
|
1155
1289
|
return (0, _orkestrel_interpret.digestValue)(briefToContent(source));
|
|
1156
1290
|
}
|
|
1157
1291
|
/**
|
|
1158
|
-
*
|
|
1292
|
+
* Renders the canonical text of exactly what a brief's hash describes.
|
|
1159
1293
|
*
|
|
1160
1294
|
* @remarks
|
|
1161
1295
|
* `trace` and `hash` are stripped, then interprets `canonicalize` renders the rest in a
|
|
@@ -1167,9 +1301,9 @@ function briefToHash(source) {
|
|
|
1167
1301
|
*
|
|
1168
1302
|
* @example
|
|
1169
1303
|
* ```ts
|
|
1170
|
-
* import {
|
|
1304
|
+
* import { briefToContent, buildBrief, buildTask, pinBrief } from '@orkestrel/brief'
|
|
1171
1305
|
*
|
|
1172
|
-
* const draft =
|
|
1306
|
+
* const draft = buildBrief(buildTask('plan', 'ops', 'Plan the release.'))
|
|
1173
1307
|
* briefToContent(draft) === briefToContent(pinBrief(draft)) // true — pinning adds no content
|
|
1174
1308
|
* ```
|
|
1175
1309
|
*/
|
|
@@ -1178,7 +1312,7 @@ function briefToContent(source) {
|
|
|
1178
1312
|
return (0, _orkestrel_interpret.canonicalize)(content);
|
|
1179
1313
|
}
|
|
1180
1314
|
/**
|
|
1181
|
-
*
|
|
1315
|
+
* Freezes a value and everything reachable from it.
|
|
1182
1316
|
*
|
|
1183
1317
|
* @remarks
|
|
1184
1318
|
* `Object.freeze` is SHALLOW, so freezing a record leaves every nested array and object
|
|
@@ -1192,7 +1326,7 @@ function briefToContent(source) {
|
|
|
1192
1326
|
* Reaches PLAIN objects and arrays, which is the whole of a `Brief` — it is JSON-serializable
|
|
1193
1327
|
* by contract. A `Map`, `Set`, or typed array is frozen as an object and its CONTENTS are left
|
|
1194
1328
|
* writable, and `Object.isFrozen` reports `true` for it either way. Nothing this package
|
|
1195
|
-
* produces contains one; a caller freezing their own value
|
|
1329
|
+
* produces contains one; the limit lands on a caller freezing their own value.
|
|
1196
1330
|
*
|
|
1197
1331
|
* @param value - The value to freeze in place; returned for convenience.
|
|
1198
1332
|
* @returns The same value, now deeply frozen.
|
|
@@ -1209,7 +1343,7 @@ function freezeDeep(value) {
|
|
|
1209
1343
|
return freezeBranch(value, /* @__PURE__ */ new WeakSet());
|
|
1210
1344
|
}
|
|
1211
1345
|
/**
|
|
1212
|
-
*
|
|
1346
|
+
* Freezes one branch of a value graph, skipping what the visited set already holds.
|
|
1213
1347
|
*
|
|
1214
1348
|
* @param value - The branch to freeze.
|
|
1215
1349
|
* @param seen - The objects already frozen on this walk; what makes a cycle terminate.
|
|
@@ -1231,7 +1365,7 @@ function freezeBranch(value, seen) {
|
|
|
1231
1365
|
return value;
|
|
1232
1366
|
}
|
|
1233
1367
|
/**
|
|
1234
|
-
*
|
|
1368
|
+
* Renders a value thrown by a stage into a message.
|
|
1235
1369
|
*
|
|
1236
1370
|
* @remarks
|
|
1237
1371
|
* TOTAL: it never throws, for any input. That is load-bearing rather than tidy, because this
|
|
@@ -1240,7 +1374,7 @@ function freezeBranch(value, seen) {
|
|
|
1240
1374
|
* falsifies the package's central promise that a failing stage yields an incomplete
|
|
1241
1375
|
* `Briefing` rather than an exception.
|
|
1242
1376
|
*
|
|
1243
|
-
*
|
|
1377
|
+
* Real inputs used to throw: an `Error` subclass whose `message` getter throws, a value
|
|
1244
1378
|
* whose string conversion throws, and a null-prototype object, which has no inherited
|
|
1245
1379
|
* conversion for String() to reach. Each is wrapped, and an unreadable value degrades to its
|
|
1246
1380
|
* type rather than propagating.
|
|
@@ -1264,10 +1398,10 @@ function errorToMessage(error) {
|
|
|
1264
1398
|
return `an unreadable ${typeof error} was thrown`;
|
|
1265
1399
|
}
|
|
1266
1400
|
/**
|
|
1267
|
-
*
|
|
1401
|
+
* Narrows unknown data to a `Brief`, throwing when it is off-contract.
|
|
1268
1402
|
*
|
|
1269
1403
|
* @remarks
|
|
1270
|
-
* The throwing half of the intake pair: this returns its argument by IDENTITY
|
|
1404
|
+
* The throwing half of the intake pair: this returns its argument by IDENTITY after the
|
|
1271
1405
|
* guard passes, while `parseBrief` returns `undefined` for bad input. It constructs
|
|
1272
1406
|
* nothing, so it is an assertion rather than a factory. Reserve it for programmer-error
|
|
1273
1407
|
* contexts where invalidity is a bug.
|
|
@@ -1281,24 +1415,24 @@ function errorToMessage(error) {
|
|
|
1281
1415
|
* `briefToTrace` read the value they are handed instead, so a caller reaching one of those
|
|
1282
1416
|
* directly owns that reading. Pass `assertBrief` a value you already own.
|
|
1283
1417
|
*
|
|
1284
|
-
* @param
|
|
1418
|
+
* @param value - The candidate brief value.
|
|
1285
1419
|
* @returns The same value, now known to satisfy {@link Brief}.
|
|
1286
|
-
* @throws {@link BriefError} `INVALID` when `
|
|
1420
|
+
* @throws {@link BriefError} `INVALID` when `value` fails `isBrief`.
|
|
1287
1421
|
*
|
|
1288
1422
|
* @example
|
|
1289
1423
|
* ```ts
|
|
1290
|
-
* import { assertBrief,
|
|
1424
|
+
* import { assertBrief, buildBrief, buildProof, buildTask } from '@orkestrel/brief'
|
|
1291
1425
|
*
|
|
1292
|
-
* assertBrief(
|
|
1426
|
+
* assertBrief(buildBrief(buildTask('plan', 'ops', 'Plan the release.'), { proofs: [buildProof('x', 'y')] }))
|
|
1293
1427
|
* assertBrief({ task: { operation: 'plan', domain: 'ops', statement: 'x.' } }) // throws INVALID
|
|
1294
1428
|
* ```
|
|
1295
1429
|
*/
|
|
1296
|
-
function assertBrief(
|
|
1297
|
-
if (!isBrief(
|
|
1298
|
-
return
|
|
1430
|
+
function assertBrief(value) {
|
|
1431
|
+
if (!isBrief(value)) throw new BriefError("INVALID", "Brief failed the exact-record contract", { field: "brief" });
|
|
1432
|
+
return value;
|
|
1299
1433
|
}
|
|
1300
1434
|
/**
|
|
1301
|
-
*
|
|
1435
|
+
* Returns a fresh brief with `trace` and `hash` derived from its own content.
|
|
1302
1436
|
*
|
|
1303
1437
|
* @remarks
|
|
1304
1438
|
* Deterministic: no clock, no randomness, no run-specific data. Any existing `trace` /
|
|
@@ -1314,9 +1448,9 @@ function assertBrief(data) {
|
|
|
1314
1448
|
*
|
|
1315
1449
|
* @example
|
|
1316
1450
|
* ```ts
|
|
1317
|
-
* import {
|
|
1451
|
+
* import { buildBrief, buildTask, pinBrief } from '@orkestrel/brief'
|
|
1318
1452
|
*
|
|
1319
|
-
* const pinned = pinBrief(
|
|
1453
|
+
* const pinned = pinBrief(buildBrief(buildTask('document', 'writing', 'Write the brief guide.')))
|
|
1320
1454
|
* pinned.hash // an 8-hex-digit structural digest
|
|
1321
1455
|
* pinned.trace // 'document/writing · outcomes:0 · gaps:0/0 · proofs:0'
|
|
1322
1456
|
* ```
|
|
@@ -1331,7 +1465,7 @@ function pinBrief(source) {
|
|
|
1331
1465
|
});
|
|
1332
1466
|
}
|
|
1333
1467
|
/**
|
|
1334
|
-
*
|
|
1468
|
+
* Renders the one-line census `pinBrief` stamps onto a brief.
|
|
1335
1469
|
*
|
|
1336
1470
|
* @remarks
|
|
1337
1471
|
* Extracted so it has ONE implementation. `pinBrief` derives it and `BriefManager` re-derives
|
|
@@ -1344,9 +1478,9 @@ function pinBrief(source) {
|
|
|
1344
1478
|
*
|
|
1345
1479
|
* @example
|
|
1346
1480
|
* ```ts
|
|
1347
|
-
* import {
|
|
1481
|
+
* import { briefToTrace, buildBrief, buildTask } from '@orkestrel/brief'
|
|
1348
1482
|
*
|
|
1349
|
-
* briefToTrace(
|
|
1483
|
+
* briefToTrace(buildBrief(buildTask('document', 'writing', 'Write the guide.')))
|
|
1350
1484
|
* // 'document/writing · outcomes:0 · gaps:0/0 · proofs:0'
|
|
1351
1485
|
* ```
|
|
1352
1486
|
*/
|
|
@@ -1359,7 +1493,7 @@ function briefToTrace(source) {
|
|
|
1359
1493
|
].join(" · ");
|
|
1360
1494
|
}
|
|
1361
1495
|
/**
|
|
1362
|
-
*
|
|
1496
|
+
* Renders one exemplar as markdown lines.
|
|
1363
1497
|
*
|
|
1364
1498
|
* @remarks
|
|
1365
1499
|
* An `Example`'s two sides are the only brief members permitted to span lines, so a
|
|
@@ -1371,9 +1505,9 @@ function briefToTrace(source) {
|
|
|
1371
1505
|
*
|
|
1372
1506
|
* @example
|
|
1373
1507
|
* ```ts
|
|
1374
|
-
* import {
|
|
1508
|
+
* import { buildExample, exampleToLines } from '@orkestrel/brief'
|
|
1375
1509
|
*
|
|
1376
|
-
* exampleToLines(
|
|
1510
|
+
* exampleToLines(buildExample('<input required>', 'el.validity')) // ['- ` <input required> ` → ` el.validity `']
|
|
1377
1511
|
* ```
|
|
1378
1512
|
*/
|
|
1379
1513
|
function exampleToLines(entry) {
|
|
@@ -1404,20 +1538,21 @@ function exampleToLines(entry) {
|
|
|
1404
1538
|
];
|
|
1405
1539
|
}
|
|
1406
1540
|
/**
|
|
1407
|
-
*
|
|
1541
|
+
* Projects a brief into the copy-ready agent prompt.
|
|
1408
1542
|
*
|
|
1409
1543
|
* @remarks
|
|
1410
|
-
*
|
|
1411
|
-
*
|
|
1544
|
+
* Sections render in authority order, so the executor meets what wins a conflict before what
|
|
1545
|
+
* it may touch. Paths are referenced, never inlined — the executor retrieves them. An empty
|
|
1546
|
+
* section is omitted entirely, so the rendering carries no filler an executor must read past.
|
|
1412
1547
|
*
|
|
1413
|
-
* @param
|
|
1548
|
+
* @param input - The brief to render.
|
|
1414
1549
|
* @returns The markdown prompt.
|
|
1415
1550
|
*
|
|
1416
1551
|
* @example
|
|
1417
1552
|
* ```ts
|
|
1418
|
-
* import {
|
|
1553
|
+
* import { briefToMarkdown, buildBrief, buildTask } from '@orkestrel/brief'
|
|
1419
1554
|
*
|
|
1420
|
-
* briefToMarkdown(
|
|
1555
|
+
* briefToMarkdown(buildBrief(buildTask('review', 'code', 'Review the gate rules.')))
|
|
1421
1556
|
* // '# Brief: Review the gate rules.\n\nreview · code\n\n## Output\n\n- format: markdown\n'
|
|
1422
1557
|
* ```
|
|
1423
1558
|
*/
|
|
@@ -1511,21 +1646,21 @@ function briefToMarkdown(input) {
|
|
|
1511
1646
|
return lines.join("\n");
|
|
1512
1647
|
}
|
|
1513
1648
|
/**
|
|
1514
|
-
*
|
|
1649
|
+
* Projects a brief into a `/goal` completion condition.
|
|
1515
1650
|
*
|
|
1516
1651
|
* @remarks
|
|
1517
1652
|
* The proofs' commands VERBATIM plus a turn cap — the goal never adds a condition the
|
|
1518
1653
|
* brief does not carry.
|
|
1519
1654
|
*
|
|
1520
|
-
* @param
|
|
1521
|
-
* @param turns - The turn cap
|
|
1655
|
+
* @param input - The brief to render.
|
|
1656
|
+
* @param turns - The turn cap. Default: `DEFAULT_BRIEF_TURNS`.
|
|
1522
1657
|
* @returns The one-line completion condition.
|
|
1523
1658
|
*
|
|
1524
1659
|
* @example
|
|
1525
1660
|
* ```ts
|
|
1526
|
-
* import {
|
|
1661
|
+
* import { briefToGoal, buildBrief, buildProof, buildTask } from '@orkestrel/brief'
|
|
1527
1662
|
*
|
|
1528
|
-
* briefToGoal(
|
|
1663
|
+
* briefToGoal(buildBrief(buildTask('test', 'code', 'Cover the gate.'), { proofs: [buildProof('x', 'npm test')] }))
|
|
1529
1664
|
* // 'Done when every proof passes: npm test exits 0. Cap: 16 turns.'
|
|
1530
1665
|
* ```
|
|
1531
1666
|
*/
|
|
@@ -1534,27 +1669,34 @@ function briefToGoal(input, turns = 16) {
|
|
|
1534
1669
|
return `Done when every proof passes: ${source.proofs.length === 0 ? "no proofs recorded" : source.proofs.map((entry) => `${entry.command} exits 0`).join("; ")}. Cap: ${String(turns)} turns.`;
|
|
1535
1670
|
}
|
|
1536
1671
|
/**
|
|
1537
|
-
*
|
|
1672
|
+
* Projects a brief into a subagent `Dispatch`.
|
|
1538
1673
|
*
|
|
1539
1674
|
* @remarks
|
|
1540
|
-
* `edit` is exactly `manifest.edit
|
|
1541
|
-
* can run concurrently under the same brief without conflict.
|
|
1675
|
+
* `edit` is exactly `manifest.edit` — the owned set — so two dispatches whose `edit` sets do
|
|
1676
|
+
* not intersect can run concurrently under the same brief without conflict. `locked` and
|
|
1677
|
+
* `forbidden` cross unchanged as the do-not-touch sets.
|
|
1542
1678
|
*
|
|
1543
|
-
* `authority` is exactly `brief.authority` in rank order, and it is a
|
|
1544
|
-
*
|
|
1679
|
+
* `authority` is exactly `brief.authority` in rank order, and it is a separate axis from the
|
|
1680
|
+
* permission sets rather than a further partition — a ranked path normally also appears in
|
|
1545
1681
|
* `read` or `locked`, because the executor has to open what it obeys. It is projected as
|
|
1546
1682
|
* paths so a machine consumer never has to parse `prompt`, which is written for a model.
|
|
1547
1683
|
*
|
|
1548
|
-
* @param
|
|
1549
|
-
* @returns The dispatch — the rendered prompt, the ranked authority, and the
|
|
1684
|
+
* @param input - The brief to project.
|
|
1685
|
+
* @returns The dispatch — the rendered prompt, the ranked authority, and the path sets.
|
|
1550
1686
|
*
|
|
1551
1687
|
* @example
|
|
1552
1688
|
* ```ts
|
|
1553
|
-
* import {
|
|
1554
|
-
*
|
|
1555
|
-
*
|
|
1556
|
-
*
|
|
1557
|
-
*
|
|
1689
|
+
* import {
|
|
1690
|
+
* briefToDispatch,
|
|
1691
|
+
* buildBrief,
|
|
1692
|
+
* buildManifest,
|
|
1693
|
+
* buildReference,
|
|
1694
|
+
* buildTask,
|
|
1695
|
+
* } from '@orkestrel/brief'
|
|
1696
|
+
*
|
|
1697
|
+
* const draft = buildBrief(buildTask('migrate', 'code', 'Migrate the stores.'), {
|
|
1698
|
+
* authority: [buildReference('AGENTS.md', 'project law')],
|
|
1699
|
+
* manifest: buildManifest({ edit: [buildReference('src/core/stores/**', 'the legacy stores')] }),
|
|
1558
1700
|
* })
|
|
1559
1701
|
* briefToDispatch(draft).edit // ['src/core/stores/**']
|
|
1560
1702
|
* briefToDispatch(draft).authority // ['AGENTS.md']
|
|
@@ -1572,36 +1714,39 @@ function briefToDispatch(input) {
|
|
|
1572
1714
|
};
|
|
1573
1715
|
}
|
|
1574
1716
|
/**
|
|
1575
|
-
*
|
|
1717
|
+
* Derives one imperative statement from free text.
|
|
1576
1718
|
*
|
|
1577
1719
|
* @remarks
|
|
1578
1720
|
* Whitespace collapses, the first character uppercases, and a terminator is appended
|
|
1579
1721
|
* when the text carries none. Nothing else is invented.
|
|
1580
1722
|
*
|
|
1581
1723
|
* @param text - The raw request text.
|
|
1582
|
-
* @returns The statement, or `
|
|
1724
|
+
* @returns The statement, or `undefined` for empty or whitespace-only text.
|
|
1583
1725
|
*
|
|
1584
1726
|
* @example
|
|
1585
1727
|
* ```ts
|
|
1586
1728
|
* import { deriveStatement } from '@orkestrel/brief'
|
|
1587
1729
|
*
|
|
1588
1730
|
* deriveStatement(' clean up useForm ') // 'Clean up useForm.'
|
|
1589
|
-
* deriveStatement('') //
|
|
1731
|
+
* deriveStatement('') // undefined
|
|
1590
1732
|
* ```
|
|
1591
1733
|
*/
|
|
1592
1734
|
function deriveStatement(text) {
|
|
1593
1735
|
const collapsed = (0, _orkestrel_interpret.collapseWhitespace)(text);
|
|
1594
|
-
if (collapsed.length === 0) return
|
|
1736
|
+
if (collapsed.length === 0) return void 0;
|
|
1595
1737
|
const capitalized = collapsed.charAt(0).toUpperCase() + collapsed.slice(1);
|
|
1596
1738
|
return /[.!?]$/u.test(capitalized) ? capitalized : `${capitalized}.`;
|
|
1597
1739
|
}
|
|
1598
1740
|
/**
|
|
1599
|
-
*
|
|
1741
|
+
* Derives a `Task` from an interprets `Intent` through the caller's vocabularies.
|
|
1600
1742
|
*
|
|
1601
1743
|
* @remarks
|
|
1602
1744
|
* The vocabularies are the CALLER's policy: this maps and never guesses. An action or
|
|
1603
1745
|
* domain the caller did not map — or mapped to an off-vocabulary value — yields
|
|
1604
|
-
* `undefined` rather than an invented task. Inherited keys never resolve.
|
|
1746
|
+
* `undefined` rather than an invented task. Inherited keys never resolve. `Intent.action`
|
|
1747
|
+
* and `Intent.domain` are optional, because `classifyIntent` leaves an unmatched axis
|
|
1748
|
+
* absent, and an absent axis is unmapped by definition: it yields `undefined` before
|
|
1749
|
+
* either vocabulary is read.
|
|
1605
1750
|
*
|
|
1606
1751
|
* @param intent - The classified intent from an interpret pipeline.
|
|
1607
1752
|
* @param text - The text the statement derives from.
|
|
@@ -1620,16 +1765,17 @@ function deriveStatement(text) {
|
|
|
1620
1765
|
* ```
|
|
1621
1766
|
*/
|
|
1622
1767
|
function deriveTask(intent, text, actions, domains) {
|
|
1768
|
+
if (intent.action === void 0 || intent.domain === void 0) return void 0;
|
|
1623
1769
|
const operationDescriptor = Object.getOwnPropertyDescriptor(actions, intent.action);
|
|
1624
1770
|
const domainDescriptor = Object.getOwnPropertyDescriptor(domains, intent.domain);
|
|
1625
1771
|
const operation = operationDescriptor === void 0 ? void 0 : "value" in operationDescriptor ? operationDescriptor.value : operationDescriptor.get === void 0 ? void 0 : Reflect.apply(operationDescriptor.get, actions, []);
|
|
1626
1772
|
const domain = domainDescriptor === void 0 ? void 0 : "value" in domainDescriptor ? domainDescriptor.value : domainDescriptor.get === void 0 ? void 0 : Reflect.apply(domainDescriptor.get, domains, []);
|
|
1627
1773
|
if (!isTaskOperation(operation) || !isTaskDomain(domain)) return void 0;
|
|
1628
1774
|
const statement = deriveStatement(text);
|
|
1629
|
-
return statement
|
|
1775
|
+
return statement === void 0 ? void 0 : buildTask(operation, domain, statement);
|
|
1630
1776
|
}
|
|
1631
1777
|
/**
|
|
1632
|
-
*
|
|
1778
|
+
* Derives `Given[]` from an interprets `Entity[]`.
|
|
1633
1779
|
*
|
|
1634
1780
|
* @remarks
|
|
1635
1781
|
* Every extracted entity becomes one `extracted` fact. A nameless entity is dropped; an
|
|
@@ -1648,10 +1794,10 @@ function deriveTask(intent, text, actions, domains) {
|
|
|
1648
1794
|
* ```
|
|
1649
1795
|
*/
|
|
1650
1796
|
function deriveGivens(entities) {
|
|
1651
|
-
return entities.filter((entity) => entity.name.length > 0).map((entity) =>
|
|
1797
|
+
return entities.filter((entity) => entity.name.length > 0).map((entity) => buildGiven("extracted", entity.name, typeof entity.value === "string" ? entity.value : typeof entity.value === "object" && entity.value !== null ? (0, _orkestrel_interpret.canonicalize)(entity.value) : String(entity.value)));
|
|
1652
1798
|
}
|
|
1653
1799
|
/**
|
|
1654
|
-
*
|
|
1800
|
+
* Derives `Gap[]` from an interprets `Ambiguity[]`.
|
|
1655
1801
|
*
|
|
1656
1802
|
* @remarks
|
|
1657
1803
|
* A REQUIRED ambiguity becomes a BLOCKING gap — the gate must fail closed on it. The
|
|
@@ -1672,7 +1818,7 @@ function deriveGivens(entities) {
|
|
|
1672
1818
|
function deriveGaps(ambiguities) {
|
|
1673
1819
|
return ambiguities.map((ambiguity) => {
|
|
1674
1820
|
const candidates = ambiguity.candidates.filter((candidate) => candidate.length > 0);
|
|
1675
|
-
return
|
|
1821
|
+
return buildGap((0, _orkestrel_reason.formatField)(ambiguity.field), ambiguity.question, {
|
|
1676
1822
|
blocking: ambiguity.required,
|
|
1677
1823
|
...candidates.length === 0 ? {} : { candidates }
|
|
1678
1824
|
});
|
|
@@ -1681,7 +1827,7 @@ function deriveGaps(ambiguities) {
|
|
|
1681
1827
|
//#endregion
|
|
1682
1828
|
//#region src/core/parsers.ts
|
|
1683
1829
|
/**
|
|
1684
|
-
*
|
|
1830
|
+
* Parses a JSON string into a `Brief`.
|
|
1685
1831
|
*
|
|
1686
1832
|
* @remarks
|
|
1687
1833
|
* The parse-then-trust boundary for a stored brief, a tool argument, or an agent's
|
|
@@ -1715,7 +1861,7 @@ function parseBrief(value) {
|
|
|
1715
1861
|
//#endregion
|
|
1716
1862
|
//#region src/core/BriefManager.ts
|
|
1717
1863
|
/**
|
|
1718
|
-
*
|
|
1864
|
+
* Implements the self-owning, versioned and content-hashed brief registry.
|
|
1719
1865
|
*
|
|
1720
1866
|
* @remarks
|
|
1721
1867
|
* Record ids are MINTED from each brief's own content hash unless the caller names one,
|
|
@@ -1725,10 +1871,10 @@ function parseBrief(value) {
|
|
|
1725
1871
|
*
|
|
1726
1872
|
* @example
|
|
1727
1873
|
* ```ts
|
|
1728
|
-
* import { BriefManager,
|
|
1874
|
+
* import { BriefManager, buildBrief, buildTask } from '@orkestrel/brief'
|
|
1729
1875
|
*
|
|
1730
1876
|
* const briefs = new BriefManager()
|
|
1731
|
-
* const record = briefs.add(
|
|
1877
|
+
* const record = briefs.add(buildBrief(buildTask('document', 'writing', 'Write the brief guide.')))
|
|
1732
1878
|
* record.id === record.hash // true
|
|
1733
1879
|
* briefs.destroy()
|
|
1734
1880
|
* ```
|
|
@@ -1755,7 +1901,7 @@ var BriefManager = class {
|
|
|
1755
1901
|
get emitter() {
|
|
1756
1902
|
return this.#emitter;
|
|
1757
1903
|
}
|
|
1758
|
-
get
|
|
1904
|
+
get count() {
|
|
1759
1905
|
return this.#records.size;
|
|
1760
1906
|
}
|
|
1761
1907
|
has(id) {
|
|
@@ -1770,9 +1916,9 @@ var BriefManager = class {
|
|
|
1770
1916
|
this.#refuseDestroyed();
|
|
1771
1917
|
return [...this.#records.values()];
|
|
1772
1918
|
}
|
|
1773
|
-
add(
|
|
1919
|
+
add(brief, options) {
|
|
1774
1920
|
this.#refuseDestroyed();
|
|
1775
|
-
const record = this.#stage(
|
|
1921
|
+
const record = this.#stage(brief, this.#records, options);
|
|
1776
1922
|
this.#commit(record);
|
|
1777
1923
|
return record;
|
|
1778
1924
|
}
|
|
@@ -1839,7 +1985,7 @@ var BriefManager = class {
|
|
|
1839
1985
|
//#endregion
|
|
1840
1986
|
//#region src/core/BriefCompiler.ts
|
|
1841
1987
|
/**
|
|
1842
|
-
*
|
|
1988
|
+
* Implements the compilation orchestrator — the `[interpret, draft, gate, pin]` pipeline.
|
|
1843
1989
|
*
|
|
1844
1990
|
* @remarks
|
|
1845
1991
|
* `compile` is genuinely SYNCHRONOUS and never throws for a brief it cannot emit: a
|
|
@@ -1849,13 +1995,13 @@ var BriefManager = class {
|
|
|
1849
1995
|
*
|
|
1850
1996
|
* @example
|
|
1851
1997
|
* ```ts
|
|
1852
|
-
* import { BriefCompiler,
|
|
1998
|
+
* import { BriefCompiler, buildProof, buildTask } from '@orkestrel/brief'
|
|
1853
1999
|
*
|
|
1854
2000
|
* const compiler = new BriefCompiler()
|
|
1855
2001
|
* const briefing = compiler.compile({
|
|
1856
|
-
* task:
|
|
2002
|
+
* task: buildTask('audit', 'code', 'Audit the barrel for undocumented exports.'),
|
|
1857
2003
|
* outcomes: [{ rank: 1, text: 'every export appears in the guide', required: true }],
|
|
1858
|
-
* proofs: [
|
|
2004
|
+
* proofs: [buildProof('parity passes', 'npm run test:guides')],
|
|
1859
2005
|
* })
|
|
1860
2006
|
* briefing.brief !== undefined // true — the presence of the brief IS the completeness test
|
|
1861
2007
|
* compiler.destroy()
|
|
@@ -2006,9 +2152,9 @@ var BriefCompiler = class {
|
|
|
2006
2152
|
this.#emitter.emit("compile", briefing);
|
|
2007
2153
|
return briefing;
|
|
2008
2154
|
}
|
|
2009
|
-
gate(
|
|
2155
|
+
gate(brief) {
|
|
2010
2156
|
this.#refuseDestroyed();
|
|
2011
|
-
const ruled = (0, _orkestrel_contract.attempt)(() => this.#own(this.#reason.reason(briefToSubject(
|
|
2157
|
+
const ruled = (0, _orkestrel_contract.attempt)(() => this.#own(this.#reason.reason(briefToSubject(brief), buildGateDefinition()), [
|
|
2012
2158
|
"reasoning",
|
|
2013
2159
|
"conclusion",
|
|
2014
2160
|
"rules",
|
|
@@ -2097,7 +2243,7 @@ var BriefCompiler = class {
|
|
|
2097
2243
|
message: `Gate refused: ${unready.join(", ")}`
|
|
2098
2244
|
};
|
|
2099
2245
|
if (verdict === void 0) return void 0;
|
|
2100
|
-
const refused = verdict.rules.filter((entry) => !entry.
|
|
2246
|
+
const refused = verdict.rules.filter((entry) => !entry.applied).map((entry) => entry.id).join(", ");
|
|
2101
2247
|
if (refused.length === 0) return {
|
|
2102
2248
|
stage: "gate",
|
|
2103
2249
|
code: "BLOCKED",
|
|
@@ -2112,7 +2258,7 @@ var BriefCompiler = class {
|
|
|
2112
2258
|
#unresolved(interpretation, failures) {
|
|
2113
2259
|
if (interpretation !== void 0) return [];
|
|
2114
2260
|
if (!failures.some((entry) => entry.stage === "interpret")) return [];
|
|
2115
|
-
return [
|
|
2261
|
+
return [buildGap("gaps", "The interpret stage failed, so the request is unread and its unknowns are unknown", { blocking: true })];
|
|
2116
2262
|
}
|
|
2117
2263
|
#draft(input, interpretation, unresolved) {
|
|
2118
2264
|
const derived = interpretation === void 0 ? void 0 : deriveTask(interpretation.intent, interpretation.text, this.#actions, this.#domains);
|
|
@@ -2121,9 +2267,9 @@ var BriefCompiler = class {
|
|
|
2121
2267
|
stage: "draft",
|
|
2122
2268
|
field: "task"
|
|
2123
2269
|
});
|
|
2124
|
-
return snapshotBrief(
|
|
2270
|
+
return snapshotBrief(buildBrief(subject, {
|
|
2125
2271
|
authority: input.authority ?? [],
|
|
2126
|
-
manifest: input.manifest ??
|
|
2272
|
+
manifest: input.manifest ?? buildManifest(),
|
|
2127
2273
|
outcomes: input.outcomes ?? [],
|
|
2128
2274
|
rules: input.rules ?? [],
|
|
2129
2275
|
invariants: input.invariants ?? [],
|
|
@@ -2137,7 +2283,7 @@ var BriefCompiler = class {
|
|
|
2137
2283
|
...input.gaps ?? []
|
|
2138
2284
|
],
|
|
2139
2285
|
risks: input.risks ?? [],
|
|
2140
|
-
output: input.output ??
|
|
2286
|
+
output: input.output ?? buildOutput("markdown"),
|
|
2141
2287
|
proofs: input.proofs ?? []
|
|
2142
2288
|
}));
|
|
2143
2289
|
}
|
|
@@ -2165,7 +2311,7 @@ var BriefCompiler = class {
|
|
|
2165
2311
|
//#endregion
|
|
2166
2312
|
//#region src/core/factories.ts
|
|
2167
2313
|
/**
|
|
2168
|
-
*
|
|
2314
|
+
* Creates a compilation orchestrator.
|
|
2169
2315
|
*
|
|
2170
2316
|
* @remarks
|
|
2171
2317
|
* With no engines supplied the compiler wires its own: a default `createInterpret()`
|
|
@@ -2173,9 +2319,49 @@ var BriefCompiler = class {
|
|
|
2173
2319
|
* `createReason` carrying one `LogicalReasoner` for the gate. Pass your own to share
|
|
2174
2320
|
* instances or observe their emitters — the compiler destroys ONLY what it created.
|
|
2175
2321
|
*
|
|
2176
|
-
* @param options - Engines to borrow, the
|
|
2322
|
+
* @param options - Engines to borrow, the `actions` and `domains` intent vocabularies, and
|
|
2323
|
+
* emitter hooks.
|
|
2177
2324
|
* @returns A working {@link BriefCompilerInterface}.
|
|
2178
2325
|
*
|
|
2326
|
+
* @example Compile and project a brief
|
|
2327
|
+
* ```ts
|
|
2328
|
+
* import {
|
|
2329
|
+
* briefToGoal,
|
|
2330
|
+
* briefToMarkdown,
|
|
2331
|
+
* buildOutcome,
|
|
2332
|
+
* buildProof,
|
|
2333
|
+
* buildTask,
|
|
2334
|
+
* createBriefCompiler,
|
|
2335
|
+
* } from '@orkestrel/brief'
|
|
2336
|
+
*
|
|
2337
|
+
* const compiler = createBriefCompiler()
|
|
2338
|
+
*
|
|
2339
|
+
* const briefing = compiler.compile({
|
|
2340
|
+
* task: buildTask('refactor', 'code', 'Refactor useForm to native browser form APIs.'),
|
|
2341
|
+
* authority: [{ path: 'AGENTS.md', note: 'project law; wins every conflict' }],
|
|
2342
|
+
* manifest: {
|
|
2343
|
+
* read: [
|
|
2344
|
+
* { path: 'AGENTS.md', note: 'project law; wins every conflict' },
|
|
2345
|
+
* { path: 'guides/browser.md', note: 'the composable contract' },
|
|
2346
|
+
* ],
|
|
2347
|
+
* edit: [{ path: 'src/browser/composables/useForm.ts', note: 'the composable being refactored' }],
|
|
2348
|
+
* locked: [{ path: 'src/browser/types.ts', note: 'the published contract' }],
|
|
2349
|
+
* forbidden: [{ path: 'app/**', note: 'out of scope' }],
|
|
2350
|
+
* },
|
|
2351
|
+
* outcomes: [buildOutcome(1, 'useForm uses native FormData with no behavior change')],
|
|
2352
|
+
* proofs: [buildProof('type-check and lint pass', 'npm run check')],
|
|
2353
|
+
* })
|
|
2354
|
+
*
|
|
2355
|
+
* briefing.brief !== undefined // true — the brief is present exactly when the gate passed
|
|
2356
|
+
* if (briefing.brief !== undefined) {
|
|
2357
|
+
* briefToMarkdown(briefing.brief) // the copy-ready agent prompt
|
|
2358
|
+
* briefToGoal(briefing.brief) // the /goal completion condition
|
|
2359
|
+
* }
|
|
2360
|
+
*
|
|
2361
|
+
* compiler.emitter.on('block', (questions) => questions.length)
|
|
2362
|
+
* compiler.destroy()
|
|
2363
|
+
* ```
|
|
2364
|
+
*
|
|
2179
2365
|
* @example
|
|
2180
2366
|
* ```ts
|
|
2181
2367
|
* import { createBriefCompiler } from '@orkestrel/brief'
|
|
@@ -2188,7 +2374,7 @@ function createBriefCompiler(options) {
|
|
|
2188
2374
|
return new BriefCompiler(options);
|
|
2189
2375
|
}
|
|
2190
2376
|
/**
|
|
2191
|
-
*
|
|
2377
|
+
* Creates a brief registry.
|
|
2192
2378
|
*
|
|
2193
2379
|
* @param options - An optional seed collection plus emitter hooks.
|
|
2194
2380
|
* @returns A working {@link BriefManagerInterface}.
|
|
@@ -2198,7 +2384,7 @@ function createBriefCompiler(options) {
|
|
|
2198
2384
|
* import { createBriefManager } from '@orkestrel/brief'
|
|
2199
2385
|
*
|
|
2200
2386
|
* const briefs = createBriefManager()
|
|
2201
|
-
* briefs.
|
|
2387
|
+
* briefs.count // 0
|
|
2202
2388
|
* briefs.destroy()
|
|
2203
2389
|
* ```
|
|
2204
2390
|
*/
|
|
@@ -2206,7 +2392,7 @@ function createBriefManager(options) {
|
|
|
2206
2392
|
return new BriefManager(options);
|
|
2207
2393
|
}
|
|
2208
2394
|
/**
|
|
2209
|
-
*
|
|
2395
|
+
* Compiles `briefShape` into a guard, parser, JSON Schema, and seeded generator bundle.
|
|
2210
2396
|
*
|
|
2211
2397
|
* @remarks
|
|
2212
2398
|
* The schema is what a tool boundary needs — hand it to `schemaToParameters` — and
|
|
@@ -2244,7 +2430,6 @@ exports.SINGLE_LINE_PATTERN = SINGLE_LINE_PATTERN;
|
|
|
2244
2430
|
exports.TASK_DOMAINS = TASK_DOMAINS;
|
|
2245
2431
|
exports.TASK_OPERATIONS = TASK_OPERATIONS;
|
|
2246
2432
|
exports.assertBrief = assertBrief;
|
|
2247
|
-
exports.brief = brief;
|
|
2248
2433
|
exports.briefShape = briefShape;
|
|
2249
2434
|
exports.briefToContent = briefToContent;
|
|
2250
2435
|
exports.briefToDispatch = briefToDispatch;
|
|
@@ -2253,8 +2438,20 @@ exports.briefToHash = briefToHash;
|
|
|
2253
2438
|
exports.briefToMarkdown = briefToMarkdown;
|
|
2254
2439
|
exports.briefToSubject = briefToSubject;
|
|
2255
2440
|
exports.briefToTrace = briefToTrace;
|
|
2441
|
+
exports.buildBrief = buildBrief;
|
|
2442
|
+
exports.buildCitation = buildCitation;
|
|
2443
|
+
exports.buildExample = buildExample;
|
|
2444
|
+
exports.buildGap = buildGap;
|
|
2445
|
+
exports.buildGateDefinition = buildGateDefinition;
|
|
2446
|
+
exports.buildGiven = buildGiven;
|
|
2447
|
+
exports.buildManifest = buildManifest;
|
|
2448
|
+
exports.buildOutcome = buildOutcome;
|
|
2449
|
+
exports.buildOutput = buildOutput;
|
|
2450
|
+
exports.buildProof = buildProof;
|
|
2451
|
+
exports.buildReference = buildReference;
|
|
2452
|
+
exports.buildRisk = buildRisk;
|
|
2453
|
+
exports.buildTask = buildTask;
|
|
2256
2454
|
exports.captureValue = captureValue;
|
|
2257
|
-
exports.citation = citation;
|
|
2258
2455
|
exports.citationShape = citationShape;
|
|
2259
2456
|
exports.countSentences = countSentences;
|
|
2260
2457
|
exports.createBriefCompiler = createBriefCompiler;
|
|
@@ -2265,7 +2462,6 @@ exports.deriveGivens = deriveGivens;
|
|
|
2265
2462
|
exports.deriveStatement = deriveStatement;
|
|
2266
2463
|
exports.deriveTask = deriveTask;
|
|
2267
2464
|
exports.errorToMessage = errorToMessage;
|
|
2268
|
-
exports.example = example;
|
|
2269
2465
|
exports.exampleShape = exampleShape;
|
|
2270
2466
|
exports.exampleToLines = exampleToLines;
|
|
2271
2467
|
exports.findBlockingGaps = findBlockingGaps;
|
|
@@ -2275,10 +2471,7 @@ exports.findUnmetRules = findUnmetRules;
|
|
|
2275
2471
|
exports.findUnpairedGaps = findUnpairedGaps;
|
|
2276
2472
|
exports.freezeBranch = freezeBranch;
|
|
2277
2473
|
exports.freezeDeep = freezeDeep;
|
|
2278
|
-
exports.gap = gap;
|
|
2279
2474
|
exports.gapShape = gapShape;
|
|
2280
|
-
exports.gateDefinition = gateDefinition;
|
|
2281
|
-
exports.given = given;
|
|
2282
2475
|
exports.givenShape = givenShape;
|
|
2283
2476
|
exports.isBrief = isBrief;
|
|
2284
2477
|
exports.isBriefError = isBriefError;
|
|
@@ -2300,22 +2493,15 @@ exports.isTaskDomain = isTaskDomain;
|
|
|
2300
2493
|
exports.isTaskOperation = isTaskOperation;
|
|
2301
2494
|
exports.isText = isText;
|
|
2302
2495
|
exports.lineShape = lineShape;
|
|
2303
|
-
exports.manifest = manifest;
|
|
2304
2496
|
exports.manifestShape = manifestShape;
|
|
2305
|
-
exports.outcome = outcome;
|
|
2306
2497
|
exports.outcomeShape = outcomeShape;
|
|
2307
|
-
exports.output = output;
|
|
2308
2498
|
exports.outputShape = outputShape;
|
|
2309
2499
|
exports.parseBrief = parseBrief;
|
|
2310
2500
|
exports.pinBrief = pinBrief;
|
|
2311
|
-
exports.proof = proof;
|
|
2312
2501
|
exports.proofShape = proofShape;
|
|
2313
|
-
exports.reference = reference;
|
|
2314
2502
|
exports.referenceShape = referenceShape;
|
|
2315
|
-
exports.risk = risk;
|
|
2316
2503
|
exports.riskShape = riskShape;
|
|
2317
2504
|
exports.snapshotBrief = snapshotBrief;
|
|
2318
|
-
exports.task = task;
|
|
2319
2505
|
exports.taskShape = taskShape;
|
|
2320
2506
|
exports.textShape = textShape;
|
|
2321
2507
|
exports.validateBrief = validateBrief;
|