@orkestrel/brief 0.0.7 → 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 CHANGED
@@ -1,17 +1,29 @@
1
1
  # @orkestrel/brief
2
2
 
3
- A synchronous, deterministic specification compiler. A rough request compiles into a
4
- `Brief` — a closed, JSON-serializable execution contract another agent can run with no
5
- interpretation left to do — and every downstream artifact (the prompt a model reads, a
6
- completion condition, a subagent dispatch) is projected from that one source of truth.
3
+ > The specification compiler: a synchronous, deterministic pipeline that resolves a rough
4
+ > request into a `Brief` — a closed, content-hashed execution contract another agent can run
5
+ > with no interpretation left to do — gated by a traceable reasoner and projected into every
6
+ > downstream artifact.
7
7
 
8
- A brief with blocking gaps never emits. The readiness gate is a `@orkestrel/reason`
9
- `LogicalDefinition`, so every verdict carries a traceable account of which check missed.
8
+ Compile a request with the `createBriefCompiler` factory, read the `Briefing` it returns, and
9
+ project the brief it carries into the prompt a model reads, a completion condition, or a
10
+ subagent dispatch. A brief with blocking gaps never emits: the readiness gate is a
11
+ `@orkestrel/reason` `LogicalDefinition`, so every verdict carries a traceable account of which
12
+ check missed. Part of the `@orkestrel` line.
13
+
14
+ ## Install
10
15
 
11
16
  ```sh
12
17
  npm install @orkestrel/brief
13
18
  ```
14
19
 
20
+ ## Requirements
21
+
22
+ - Node.js >= 22.12.0, matching the `engines` field in `package.json`
23
+ - ESM and CommonJS entry points, selected by the `exports` field in `package.json`
24
+
25
+ ## Usage
26
+
15
27
  ```ts
16
28
  import {
17
29
  createBriefCompiler,
@@ -36,6 +48,8 @@ if (briefing.brief !== undefined) {
36
48
  compiler.destroy()
37
49
  ```
38
50
 
51
+ ## Guide
52
+
39
53
  Full documentation: [`guides/brief.md`](guides/brief.md). The guides index lives at
40
54
  [`guides/README.md`](guides/README.md).
41
55
 
@@ -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
- /** Lists the `TaskOperation` values, frozen. */
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",
@@ -87,7 +93,7 @@ var GATE_ID = "gate";
87
93
  * Matches every line terminator a brief field refuses.
88
94
  *
89
95
  * @remarks
90
- * Every ECMAScript line terminator, not just `\n`: a renderer that splits on any of them
96
+ * Every ECMAScript line terminator, not only `\n`: a renderer that splits on any of them
91
97
  * would let the others forge a markdown row. CRLF leads the alternation so a Windows
92
98
  * exemplar splits as ONE break rather than two, which would insert a blank line the caller
93
99
  * never wrote. Kept unanchored and stateless — no `g` flag — so `test` never carries
@@ -95,7 +101,7 @@ var GATE_ID = "gate";
95
101
  */
96
102
  var LINE_BREAK_PATTERN = /\r\n|[\n\r\u2028\u2029]/;
97
103
  /**
98
- * Holds the positive form of {@link LINE_BREAK_PATTERN}, for the shape DSL.
104
+ * Holds the positive form of {@link LINE_BREAK_PATTERN}, for a `stringShape` `pattern`.
99
105
  *
100
106
  * @remarks
101
107
  * `stringShape`'s `pattern` must MATCH an accepted value, so the guard's refusal regex
@@ -121,6 +127,9 @@ var BLANK_PATTERN = /^ +$/;
121
127
  * Represents the one error class this package throws.
122
128
  *
123
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
+ *
124
133
  * Throws are reserved for caller misuse: `assertBrief`, `snapshotBrief`, and `pinBrief` on
125
134
  * off-contract data throw `INVALID`; any method after `destroy()` throws `DESTROYED`; and `BriefCompiler.gate` throws
126
135
  * `GATE_FAILED` when a borrowed reasoner returns a non-logical result. A stage that fails
@@ -169,14 +178,21 @@ function isBriefError(value) {
169
178
  }
170
179
  //#endregion
171
180
  //#region src/core/shapers.ts
172
- /** Describes a single-line string of any length, including empty. */
181
+ /** Describes a single-line string of any length, including empty — the shape mirror of `isText`. */
173
182
  var textShape = (0, _orkestrel_contract.stringShape)({ pattern: SINGLE_LINE_PATTERN });
174
183
  /** Describes a non-empty single-line string — the shape mirror of `isLine`. */
175
184
  var lineShape = (0, _orkestrel_contract.stringShape)({
176
185
  min: 1,
177
186
  pattern: SINGLE_LINE_PATTERN
178
187
  });
179
- /** Describes the `Task` shape — closed operation and domain vocabularies plus a non-empty statement. */
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
+ */
180
196
  var taskShape = (0, _orkestrel_contract.objectShape)({
181
197
  operation: (0, _orkestrel_contract.literalShape)(TASK_OPERATIONS),
182
198
  domain: (0, _orkestrel_contract.literalShape)(TASK_DOMAINS),
@@ -187,14 +203,25 @@ var referenceShape = (0, _orkestrel_contract.objectShape)({
187
203
  path: lineShape,
188
204
  note: lineShape
189
205
  }, { description: "One referenced path and why it is listed." });
190
- /** Describes the `Manifest` shape — disjoint reference partitions. */
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
+ */
191
213
  var manifestShape = (0, _orkestrel_contract.objectShape)({
192
214
  read: (0, _orkestrel_contract.arrayShape)(referenceShape),
193
215
  edit: (0, _orkestrel_contract.arrayShape)(referenceShape),
194
216
  locked: (0, _orkestrel_contract.arrayShape)(referenceShape),
195
217
  forbidden: (0, _orkestrel_contract.arrayShape)(referenceShape)
196
218
  }, { description: "The disjoint file partitions of a brief." });
197
- /** Describes the `Outcome` shape — a one-based rank, the result text, and whether it gates done. */
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
+ */
198
225
  var outcomeShape = (0, _orkestrel_contract.objectShape)({
199
226
  rank: (0, _orkestrel_contract.integerShape)({ min: 1 }),
200
227
  text: lineShape,
@@ -286,6 +313,10 @@ var isText = (value) => (0, _orkestrel_contract.isString)(value) && !LINE_BREAK_
286
313
  /**
287
314
  * Checks whether the value is a non-empty string holding no line terminator.
288
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
+ *
289
320
  * @param value - The value to inspect.
290
321
  * @returns True if `value` is a non-empty string holding no line terminator; false otherwise.
291
322
  */
@@ -624,6 +655,10 @@ function buildTask(operation, domain, statement) {
624
655
  /**
625
656
  * Assembles a `Reference` from a path and the note that justifies listing it.
626
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.
661
+ *
627
662
  * @param path - The referenced path or glob.
628
663
  * @param note - Why the path is listed.
629
664
  * @returns A fresh `Reference`.
@@ -1010,6 +1045,10 @@ function countSentences(statement) {
1010
1045
  /**
1011
1046
  * Lists the gaps that block emission.
1012
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.
1051
+ *
1013
1052
  * @param source - The brief to inspect.
1014
1053
  * @returns Every gap carrying `blocking: true`, in declaration order.
1015
1054
  *
@@ -1038,7 +1077,7 @@ function findBlockingGaps(source) {
1038
1077
  * are disjoint — `findManifestOverlaps` and the `disjoint` rule enforce it — so a forbidden
1039
1078
  * path is in none of the grants and is reported here. An authority named in NO partition at
1040
1079
  * all is reported for the same reason, and that is the case a forbidden-only check misses
1041
- * entirely: the brief simply never says the executor may open what it must obey.
1080
+ * entirely: the brief never says the executor may open what it must obey.
1042
1081
  *
1043
1082
  * Paths are compared as EXACT strings, matching `findManifestOverlaps`. A glob is never
1044
1083
  * expanded, so `read: 'guides/**'` does not grant `authority: 'guides/brief.md'`. State a
@@ -1502,8 +1541,9 @@ function exampleToLines(entry) {
1502
1541
  * Projects a brief into the copy-ready agent prompt.
1503
1542
  *
1504
1543
  * @remarks
1505
- * Paths are REFERENCED, never inlined the executor retrieves them. An empty section is
1506
- * omitted entirely, so the rendering carries no filler an executor must read past.
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.
1507
1547
  *
1508
1548
  * @param input - The brief to render.
1509
1549
  * @returns The markdown prompt.
@@ -1632,10 +1672,11 @@ function briefToGoal(input, turns = 16) {
1632
1672
  * Projects a brief into a subagent `Dispatch`.
1633
1673
  *
1634
1674
  * @remarks
1635
- * `edit` is exactly `manifest.edit`, so two dispatches whose `edit` sets do not intersect
1636
- * 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.
1637
1678
  *
1638
- * `authority` is exactly `brief.authority` in rank order, and it is a SEPARATE axis from the
1679
+ * `authority` is exactly `brief.authority` in rank order, and it is a separate axis from the
1639
1680
  * permission sets rather than a further partition — a ranked path normally also appears in
1640
1681
  * `read` or `locked`, because the executor has to open what it obeys. It is projected as
1641
1682
  * paths so a machine consumer never has to parse `prompt`, which is written for a model.
@@ -2282,6 +2323,45 @@ var BriefCompiler = class {
2282
2323
  * emitter hooks.
2283
2324
  * @returns A working {@link BriefCompilerInterface}.
2284
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
+ *
2285
2365
  * @example
2286
2366
  * ```ts
2287
2367
  * import { createBriefCompiler } from '@orkestrel/brief'