@orkestrel/brief 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1911 @@
1
+ import { Ambiguity } from '@orkestrel/interpret';
2
+ import { ArrayShape } from '@orkestrel/contract';
3
+ import { BooleanShape } from '@orkestrel/contract';
4
+ import { ContractInterface } from '@orkestrel/contract';
5
+ import { EmitterErrorHandler } from '@orkestrel/emitter';
6
+ import { EmitterHooks } from '@orkestrel/emitter';
7
+ import { EmitterInterface } from '@orkestrel/emitter';
8
+ import { Entity } from '@orkestrel/interpret';
9
+ import { Guard } from '@orkestrel/contract';
10
+ import { Intent } from '@orkestrel/interpret';
11
+ import { Interpretation } from '@orkestrel/interpret';
12
+ import { InterpretInterface } from '@orkestrel/interpret';
13
+ import { LiteralShape } from '@orkestrel/contract';
14
+ import { LogicalDefinition } from '@orkestrel/reason';
15
+ import { LogicalResult } from '@orkestrel/reason';
16
+ import { ManagerAddOptions } from '@orkestrel/interpret';
17
+ import { NumberShape } from '@orkestrel/contract';
18
+ import { ObjectShape } from '@orkestrel/contract';
19
+ import { OptionalShape } from '@orkestrel/contract';
20
+ import { ReasonInterface } from '@orkestrel/reason';
21
+ import { ReasonValidationResult } from '@orkestrel/reason';
22
+ import { RuleResult } from '@orkestrel/reason';
23
+ import { StringShape } from '@orkestrel/contract';
24
+ import { Subject } from '@orkestrel/reason';
25
+
26
+ /**
27
+ * Narrow unknown data to a `Brief`, throwing when it is off-contract.
28
+ *
29
+ * @remarks
30
+ * The throwing half of the intake pair: this returns its argument by IDENTITY once the
31
+ * guard passes, while `parseBrief` returns `undefined` for bad input. It constructs
32
+ * nothing, so it is an assertion rather than a factory. Reserve it for programmer-error
33
+ * contexts where invalidity is a bug.
34
+ *
35
+ * @param data - The candidate brief data.
36
+ * @returns The same value, now known to satisfy {@link Brief}.
37
+ * @throws {@link BriefError} `INVALID` when `data` fails `isBrief`.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * import { assertBrief, brief, proof, task } from '@orkestrel/brief'
42
+ *
43
+ * assertBrief(brief(task('plan', 'ops', 'Plan the release.'), { proofs: [proof('x', 'y')] }))
44
+ * assertBrief({ task: { operation: 'plan', domain: 'ops', statement: 'x.' } }) // throws INVALID
45
+ * ```
46
+ */
47
+ export declare function assertBrief(data: unknown): Brief;
48
+
49
+ /**
50
+ * A string of one or more spaces and nothing else.
51
+ *
52
+ * @remarks
53
+ * The one exemplar side `exampleToLines` must NOT pad. CommonMark strips a fully-blank code
54
+ * span to nothing rather than one space from each end, so padding inflates an all-space value
55
+ * while every other value needs the pad to keep its own boundary spaces.
56
+ *
57
+ * `+` rather than `*`, because the EMPTY string is not that case: it has no spaces to
58
+ * preserve, and withholding the pad emitted an empty backtick run that does not close.
59
+ */
60
+ export declare const BLANK_PATTERN: RegExp;
61
+
62
+ /**
63
+ * The closed execution contract — a rough request with every implicit decision resolved.
64
+ *
65
+ * @remarks
66
+ * `trace` and `hash` are DERIVED by `pinBrief`. The `brief` builder cannot set them, so
67
+ * nothing this package produces authors them.
68
+ *
69
+ * They are still SHAPE-checked rather than verified on the way in: `isBrief` and `parseBrief`
70
+ * accept any single-line string, because that is what lets a pinned brief round-trip through
71
+ * JSON and back. So an inbound brief's `hash` is not proof of its content — re-derive it with
72
+ * `briefToHash` if the source is untrusted. `BriefManager` does exactly that, and refuses a
73
+ * record whose stored hash contradicts its content.
74
+ */
75
+ export declare interface Brief {
76
+ readonly task: Task;
77
+ readonly authority: readonly Reference[];
78
+ readonly manifest: Manifest;
79
+ readonly outcomes: readonly Outcome[];
80
+ readonly rules: readonly string[];
81
+ readonly invariants: readonly string[];
82
+ readonly givens: readonly Given[];
83
+ readonly examples: readonly Example[];
84
+ readonly assumptions: readonly string[];
85
+ readonly citations: readonly Citation[];
86
+ readonly gaps: readonly Gap[];
87
+ readonly risks: readonly Risk[];
88
+ readonly output: Output;
89
+ readonly proofs: readonly Proof[];
90
+ readonly trace?: string;
91
+ readonly hash?: string;
92
+ }
93
+
94
+ /**
95
+ * Build a `Brief` from a `Task` plus section overrides.
96
+ *
97
+ * @param subject - The task the brief is about.
98
+ * @param overrides - Any sections to fill; every absent collection defaults to `[]`,
99
+ * `output` defaults to `output('markdown')`, and `trace` / `hash` stay OMITTED so
100
+ * `pinBrief` can fill them.
101
+ * @returns A fresh, unpinned `Brief`.
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * import { brief, outcome, proof, task } from '@orkestrel/brief'
106
+ *
107
+ * brief(task('audit', 'code', 'Audit the barrel for undocumented exports.'), {
108
+ * outcomes: [outcome(1, 'every export appears in the guide')],
109
+ * proofs: [proof('parity passes', 'npm run test:guides')],
110
+ * })
111
+ * ```
112
+ */
113
+ export declare function brief(subject: Task, overrides?: Partial<Omit<Brief, 'task' | 'trace' | 'hash'>>): Brief;
114
+
115
+ /**
116
+ * The compilation orchestrator — the four-stage `[interpret, draft, gate, pin]` pipeline.
117
+ *
118
+ * @remarks
119
+ * `compile` is genuinely SYNCHRONOUS and never throws for a brief it cannot emit: a
120
+ * blocking gap, a refused gate, and a thrown stage all yield a visible INCOMPLETE
121
+ * `Briefing`. It owns the engines it created and BORROWS the ones passed in, so
122
+ * `destroy()` releases only what it made.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * import { BriefCompiler, proof, task } from '@orkestrel/brief'
127
+ *
128
+ * const compiler = new BriefCompiler()
129
+ * const briefing = compiler.compile({
130
+ * task: task('audit', 'code', 'Audit the barrel for undocumented exports.'),
131
+ * outcomes: [{ rank: 1, text: 'every export appears in the guide', required: true }],
132
+ * proofs: [proof('parity passes', 'npm run test:guides')],
133
+ * })
134
+ * briefing.brief !== undefined // true — the presence of the brief IS the completeness test
135
+ * compiler.destroy()
136
+ * ```
137
+ */
138
+ export declare class BriefCompiler implements BriefCompilerInterface {
139
+ #private;
140
+ constructor(options?: BriefCompilerOptions);
141
+ get emitter(): EmitterInterface<BriefCompilerEventMap>;
142
+ get interpret(): InterpretInterface;
143
+ get reason(): ReasonInterface;
144
+ compile(input: BriefInput): Briefing;
145
+ gate(source: Brief): LogicalResult;
146
+ destroy(): void;
147
+ }
148
+
149
+ /** The `BriefCompiler`'s push observation surface. */
150
+ export declare type BriefCompilerEventMap = {
151
+ compile: readonly [briefing: Briefing];
152
+ block: readonly [questions: readonly Gap[]];
153
+ error: readonly [error: unknown];
154
+ destroy: readonly [];
155
+ };
156
+
157
+ /** The compilation orchestrator contract. */
158
+ export declare interface BriefCompilerInterface {
159
+ readonly emitter: EmitterInterface<BriefCompilerEventMap>;
160
+ readonly interpret: InterpretInterface;
161
+ readonly reason: ReasonInterface;
162
+ compile(input: BriefInput): Briefing;
163
+ gate(brief: Brief): LogicalResult;
164
+ destroy(): void;
165
+ }
166
+
167
+ /**
168
+ * Input to `createBriefCompiler`.
169
+ *
170
+ * @remarks
171
+ * `interpret` and `reason` are BORROWED when supplied — the compiler destroys only
172
+ * engines it created itself. `actions` and `domains` map an interpret `Intent`'s free
173
+ * strings onto the closed task vocabularies; an unmapped value drafts no task. The gate
174
+ * is FIXED: readiness is this package's contract, not a caller setting.
175
+ *
176
+ * A borrowed engine is the caller's own code, not an attacker, and this package does not
177
+ * treat it as one. The line is OWNERSHIP, and it produces four obligations worth stating
178
+ * because none of them is enforced in code:
179
+ *
180
+ * - Every value a borrowed engine returns is OWNED AT ARRIVAL — copied where the value
181
+ * permits it, sealed in place where it does not — and then read exactly once. What the
182
+ * engine does with its own object afterwards cannot reach a `Briefing`.
183
+ * - Only the VERDICT is shape-checked, because `ReasonInterface.reason` publishes a union
184
+ * this package must narrow. The `Interpretation` is owned and not validated: it is a
185
+ * 14-member foreign interface with no published guard, and only four of its members are
186
+ * read, inside contained code. That asymmetry is deliberate — guarding it would trade a
187
+ * contained failure for a wrong refusal against a valid engine.
188
+ * - Neither engine is narrowed past its published contract. `Entity.value` is `unknown` and
189
+ * `LogicalResult` is an interface a class instance satisfies, so a value JSON cannot
190
+ * express is on-contract and is sealed rather than refused.
191
+ * - `actions` and `domains` are read LIVE on every `compile`, not snapshotted at
192
+ * construction. Mutating them between calls changes what the next call derives.
193
+ *
194
+ * Whether the engine is correct, and whether it answers the same way twice, remain the
195
+ * caller's own problem.
196
+ */
197
+ export declare interface BriefCompilerOptions {
198
+ readonly interpret?: InterpretInterface;
199
+ readonly reason?: ReasonInterface;
200
+ readonly actions?: Readonly<Record<string, TaskOperation>>;
201
+ readonly domains?: Readonly<Record<string, TaskDomain>>;
202
+ readonly on?: EmitterHooks<BriefCompilerEventMap>;
203
+ readonly error?: EmitterErrorHandler;
204
+ }
205
+
206
+ /**
207
+ * The one error class this package throws.
208
+ *
209
+ * @remarks
210
+ * Throws are reserved for caller misuse: `assertBrief`, `snapshotBrief`, and `pinBrief` on
211
+ * off-contract data throw `INVALID`; any method after `destroy()` throws `DESTROYED`; and `BriefCompiler.gate` throws
212
+ * `GATE_FAILED` when a borrowed reasoner returns a non-logical result. A stage that fails
213
+ * inside `compile` is CONTAINED as a `BriefStageFailure` on the `Briefing` instead.
214
+ *
215
+ * @example
216
+ * ```ts
217
+ * import { BriefError } from '@orkestrel/brief'
218
+ *
219
+ * const error = new BriefError('INVALID', 'Brief failed the exact-record contract', {
220
+ * field: 'proofs',
221
+ * })
222
+ * error.code // 'INVALID'
223
+ * error.context // { field: 'proofs' }
224
+ * ```
225
+ */
226
+ export declare class BriefError extends Error {
227
+ readonly code: BriefErrorCode;
228
+ readonly context?: Readonly<Record<string, unknown>>;
229
+ constructor(code: BriefErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
230
+ }
231
+
232
+ /**
233
+ * The machine-readable reasons a {@link BriefError} carries.
234
+ *
235
+ * @remarks
236
+ * Inside `compile` every stage failure is CONTAINED: the four `*_FAILED` codes and
237
+ * `BLOCKED` mark it on the {@link Briefing} rather than throwing.
238
+ *
239
+ * `INTERPRET_FAILED` needs a FOREIGN `InterpretInterface`. `@orkestrel/interpret` contains
240
+ * its own stage failures and returns a degraded `Interpretation` rather than throwing, so
241
+ * the default engine never raises it; `BriefCompilerOptions.interpret` is the seam a caller
242
+ * reaches it through. Three codes also reach a
243
+ * throw, from methods outside that containment — `INVALID` from `assertBrief`,
244
+ * `snapshotBrief`, and `pinBrief`; `DESTROYED` from any method after
245
+ * `destroy()`; and `GATE_FAILED` from `BriefCompiler.gate` when a borrowed reasoner returns a
246
+ * non-logical result OR throws its own error, which is translated rather than leaked so that
247
+ * every throw out of this module stays a `BriefError` an `isBriefError` catch can narrow.
248
+ */
249
+ export declare type BriefErrorCode = 'INTERPRET_FAILED' | 'DRAFT_FAILED' | 'GATE_FAILED' | 'PIN_FAILED' | 'BLOCKED' | 'INVALID' | 'DESTROYED';
250
+
251
+ /**
252
+ * The full, replayable outcome of one `compile()` call.
253
+ *
254
+ * @remarks
255
+ * `brief` is present exactly when the compile completed, so it is ALSO the completeness
256
+ * test — there is no `complete` flag, because a second stored fact is free to disagree
257
+ * with the first. `questions` carries what the caller must answer when it is absent.
258
+ *
259
+ * Nor is the caller's `text` echoed back. It is the caller's own value on the direct
260
+ * path, and `interpretation.text` on the interpret path, so storing it here would be one
261
+ * fact in two places. `interpretation` and `verdict` are the originating packages' own
262
+ * types — import them from `@orkestrel/interpret` and `@orkestrel/reason`.
263
+ *
264
+ * `digest` identifies this OUTCOME — the brief, the questions, and the failures together — so
265
+ * two identical compiles share it and a refused compile has one just as a complete one does.
266
+ * It is not `Brief.hash`, which identifies the brief's content alone and exists only on an
267
+ * emitted brief. Key a cache of compile results by `digest`; key a registry of briefs by
268
+ * `hash`.
269
+ */
270
+ export declare interface Briefing {
271
+ readonly interpretation?: Interpretation;
272
+ readonly brief?: Brief;
273
+ readonly questions: readonly Gap[];
274
+ readonly verdict?: LogicalResult;
275
+ readonly stages: readonly BriefStageRecord[];
276
+ readonly failures: readonly BriefStageFailure[];
277
+ readonly digest: string;
278
+ }
279
+
280
+ /**
281
+ * One `compile()` input.
282
+ *
283
+ * @remarks
284
+ * THREE classes of input, not two. `text` selects the interpret stage. `interpretation`
285
+ * supplies that stage's result directly: with no `text` it drives `deriveTask`,
286
+ * `deriveGivens`, and `deriveGaps` without running the language pipeline at all, and with
287
+ * `text` present it is also the FALLBACK the draft uses when the interpret engine throws.
288
+ * Every remaining key is a caller-authored section merged OVER whatever the draft derived.
289
+ *
290
+ * Supplying both `text` and `interpretation` is therefore meaningful: the engine's result
291
+ * wins when it succeeds, and the supplied one carries the compile when it does not.
292
+ */
293
+ export declare interface BriefInput {
294
+ readonly text?: string;
295
+ readonly interpretation?: Interpretation;
296
+ readonly task?: Task;
297
+ readonly authority?: readonly Reference[];
298
+ readonly manifest?: Manifest;
299
+ readonly outcomes?: readonly Outcome[];
300
+ readonly rules?: readonly string[];
301
+ readonly invariants?: readonly string[];
302
+ readonly givens?: readonly Given[];
303
+ readonly examples?: readonly Example[];
304
+ readonly assumptions?: readonly string[];
305
+ readonly citations?: readonly Citation[];
306
+ readonly gaps?: readonly Gap[];
307
+ readonly risks?: readonly Risk[];
308
+ readonly output?: Output;
309
+ readonly proofs?: readonly Proof[];
310
+ }
311
+
312
+ /**
313
+ * The self-owning, versioned and content-hashed brief registry.
314
+ *
315
+ * @remarks
316
+ * Record ids are MINTED from each brief's own content hash unless the caller names one,
317
+ * so registering unchanged content twice is a version no-op and two callers who compiled
318
+ * the same request land on the same id with no coordination. A call after `destroy()`
319
+ * throws `BriefError('DESTROYED', …)`.
320
+ *
321
+ * @example
322
+ * ```ts
323
+ * import { BriefManager, brief, task } from '@orkestrel/brief'
324
+ *
325
+ * const briefs = new BriefManager()
326
+ * const record = briefs.add(brief(task('document', 'writing', 'Write the brief guide.')))
327
+ * record.id === record.hash // true
328
+ * briefs.destroy()
329
+ * ```
330
+ */
331
+ export declare class BriefManager implements BriefManagerInterface {
332
+ #private;
333
+ constructor(options?: BriefManagerOptions);
334
+ get emitter(): EmitterInterface<BriefManagerEventMap>;
335
+ get size(): number;
336
+ has(id: string): boolean;
337
+ brief(id: string): BriefRecord | undefined;
338
+ briefs(): readonly BriefRecord[];
339
+ add(source: Brief, options?: ManagerAddOptions): BriefRecord;
340
+ remove(ids: readonly string[]): boolean;
341
+ remove(id: string): boolean;
342
+ remove(): void;
343
+ destroy(): void;
344
+ }
345
+
346
+ /** The `BriefManager`'s push observation surface. */
347
+ export declare type BriefManagerEventMap = {
348
+ add: readonly [id: string];
349
+ remove: readonly [id: string];
350
+ destroy: readonly [];
351
+ };
352
+
353
+ /**
354
+ * The brief registry contract.
355
+ *
356
+ * @remarks
357
+ * The array overload of `remove` is declared FIRST so an id list resolves to the batch
358
+ * form. `add` takes the fleet's own `ManagerAddOptions` from `@orkestrel/interpret`;
359
+ * omit its `id` and the record is keyed by the brief's own content hash, so re-adding
360
+ * unchanged content is a version no-op.
361
+ */
362
+ export declare interface BriefManagerInterface {
363
+ readonly emitter: EmitterInterface<BriefManagerEventMap>;
364
+ readonly size: number;
365
+ has(id: string): boolean;
366
+ brief(id: string): BriefRecord | undefined;
367
+ briefs(): readonly BriefRecord[];
368
+ add(brief: Brief, options?: ManagerAddOptions): BriefRecord;
369
+ remove(ids: readonly string[]): boolean;
370
+ remove(id: string): boolean;
371
+ remove(): void;
372
+ destroy(): void;
373
+ }
374
+
375
+ /** Input to `createBriefManager`. */
376
+ export declare interface BriefManagerOptions {
377
+ readonly briefs?: readonly Brief[];
378
+ readonly on?: EmitterHooks<BriefManagerEventMap>;
379
+ readonly error?: EmitterErrorHandler;
380
+ }
381
+
382
+ /** A versioned, content-hashed `Brief` inside a {@link BriefManagerInterface}. */
383
+ export declare interface BriefRecord {
384
+ readonly id: string;
385
+ readonly brief: Brief;
386
+ readonly version: number;
387
+ readonly hash: string;
388
+ }
389
+
390
+ /**
391
+ * The whole `Brief` shape, section shapes composed.
392
+ *
393
+ * @remarks
394
+ * `trace` and `hash` are optional because `pinBrief` fills them; an unpinned draft is
395
+ * on-contract without them.
396
+ */
397
+ export declare const briefShape: ObjectShape<{
398
+ task: ObjectShape<{
399
+ operation: LiteralShape<readonly TaskOperation[]>;
400
+ domain: LiteralShape<readonly TaskDomain[]>;
401
+ statement: StringShape;
402
+ }, false>;
403
+ authority: ArrayShape<ObjectShape<{
404
+ path: StringShape;
405
+ note: StringShape;
406
+ }, false>>;
407
+ manifest: ObjectShape<{
408
+ read: ArrayShape<ObjectShape<{
409
+ path: StringShape;
410
+ note: StringShape;
411
+ }, false>>;
412
+ edit: ArrayShape<ObjectShape<{
413
+ path: StringShape;
414
+ note: StringShape;
415
+ }, false>>;
416
+ locked: ArrayShape<ObjectShape<{
417
+ path: StringShape;
418
+ note: StringShape;
419
+ }, false>>;
420
+ forbidden: ArrayShape<ObjectShape<{
421
+ path: StringShape;
422
+ note: StringShape;
423
+ }, false>>;
424
+ }, false>;
425
+ outcomes: ArrayShape<ObjectShape<{
426
+ rank: NumberShape;
427
+ text: StringShape;
428
+ required: BooleanShape;
429
+ }, false>>;
430
+ rules: ArrayShape<StringShape>;
431
+ invariants: ArrayShape<StringShape>;
432
+ givens: ArrayShape<ObjectShape<{
433
+ category: StringShape;
434
+ name: StringShape;
435
+ value: StringShape;
436
+ }, false>>;
437
+ examples: ArrayShape<ObjectShape<{
438
+ input: StringShape;
439
+ output: StringShape;
440
+ note: OptionalShape<StringShape>;
441
+ }, false>>;
442
+ assumptions: ArrayShape<StringShape>;
443
+ citations: ArrayShape<ObjectShape<{
444
+ name: StringShape;
445
+ url: StringShape;
446
+ note: StringShape;
447
+ }, false>>;
448
+ gaps: ArrayShape<ObjectShape<{
449
+ field: StringShape;
450
+ question: StringShape;
451
+ blocking: BooleanShape;
452
+ candidates: OptionalShape<ArrayShape<StringShape>>;
453
+ }, false>>;
454
+ risks: ArrayShape<ObjectShape<{
455
+ severity: LiteralShape<readonly RiskSeverity[]>;
456
+ text: StringShape;
457
+ mitigation: StringShape;
458
+ }, false>>;
459
+ output: ObjectShape<{
460
+ format: LiteralShape<readonly OutputFormat[]>;
461
+ sections: OptionalShape<ArrayShape<StringShape>>;
462
+ include: OptionalShape<ArrayShape<StringShape>>;
463
+ exclude: OptionalShape<ArrayShape<StringShape>>;
464
+ }, false>;
465
+ proofs: ArrayShape<ObjectShape<{
466
+ text: StringShape;
467
+ command: StringShape;
468
+ }, false>>;
469
+ trace: OptionalShape<StringShape>;
470
+ hash: OptionalShape<StringShape>;
471
+ }, false>;
472
+
473
+ /** The four fixed compilation phases, in pipeline order. */
474
+ export declare type BriefStage = 'interpret' | 'draft' | 'gate' | 'pin';
475
+
476
+ /** A visible marker for a phase that failed. */
477
+ export declare interface BriefStageFailure {
478
+ readonly stage: BriefStage;
479
+ readonly code: BriefErrorCode;
480
+ readonly message: string;
481
+ }
482
+
483
+ /**
484
+ * One pipeline phase, discriminated by `stage`.
485
+ *
486
+ * @remarks
487
+ * Narrowing on `stage` types both payloads exactly, so a consumer reads a replay without
488
+ * a type assertion. A phase failed exactly when `error` is present.
489
+ */
490
+ export declare type BriefStageRecord = InterpretStageRecord | DraftStageRecord | GateStageRecord | PinStageRecord;
491
+
492
+ /**
493
+ * The canonical text of exactly what a brief's hash describes.
494
+ *
495
+ * @remarks
496
+ * `trace` and `hash` are stripped, then interprets `canonicalize` renders the rest in a
497
+ * key-order-stable form. Two briefs with the same hash are the same brief only when this
498
+ * text matches — the digest is eight hex digits, so hash equality alone is not identity.
499
+ *
500
+ * @param source - The brief to render.
501
+ * @returns The canonical content text.
502
+ *
503
+ * @example
504
+ * ```ts
505
+ * import { brief, briefToContent, pinBrief, task } from '@orkestrel/brief'
506
+ *
507
+ * const draft = brief(task('plan', 'ops', 'Plan the release.'))
508
+ * briefToContent(draft) === briefToContent(pinBrief(draft)) // true — pinning adds no content
509
+ * ```
510
+ */
511
+ export declare function briefToContent(source: Brief): string;
512
+
513
+ /**
514
+ * Project a brief into a subagent `Dispatch`.
515
+ *
516
+ * @remarks
517
+ * `edit` is exactly `manifest.edit`, so two dispatches whose `edit` sets do not intersect
518
+ * can run concurrently under the same brief without conflict.
519
+ *
520
+ * `authority` is exactly `brief.authority` in rank order, and it is a SEPARATE axis from the
521
+ * four permission sets rather than a fifth partition — a ranked path normally also appears in
522
+ * `read` or `locked`, because the executor has to open what it obeys. It is projected as
523
+ * paths so a machine consumer never has to parse `prompt`, which is written for a model.
524
+ *
525
+ * @param source - The brief to project.
526
+ * @returns The dispatch — the rendered prompt, the ranked authority, and the four path sets.
527
+ *
528
+ * @example
529
+ * ```ts
530
+ * import { brief, briefToDispatch, manifest, reference, task } from '@orkestrel/brief'
531
+ *
532
+ * const draft = brief(task('migrate', 'code', 'Migrate the stores.'), {
533
+ * authority: [reference('AGENTS.md', 'project law')],
534
+ * manifest: manifest({ edit: [reference('src/core/stores/**', 'the legacy stores')] }),
535
+ * })
536
+ * briefToDispatch(draft).edit // ['src/core/stores/**']
537
+ * briefToDispatch(draft).authority // ['AGENTS.md']
538
+ * ```
539
+ */
540
+ export declare function briefToDispatch(input: Brief): Dispatch;
541
+
542
+ /**
543
+ * Project a brief into a `/goal` completion condition.
544
+ *
545
+ * @remarks
546
+ * The proofs' commands VERBATIM plus a turn cap — the goal never adds a condition the
547
+ * brief does not carry.
548
+ *
549
+ * @param source - The brief to render.
550
+ * @param turns - The turn cap; defaults to `DEFAULT_BRIEF_TURNS`.
551
+ * @returns The one-line completion condition.
552
+ *
553
+ * @example
554
+ * ```ts
555
+ * import { brief, briefToGoal, proof, task } from '@orkestrel/brief'
556
+ *
557
+ * briefToGoal(brief(task('test', 'code', 'Cover the gate.'), { proofs: [proof('x', 'npm test')] }))
558
+ * // 'Done when every proof passes: npm test exits 0. Cap: 16 turns.'
559
+ * ```
560
+ */
561
+ export declare function briefToGoal(input: Brief, turns?: number): string;
562
+
563
+ /**
564
+ * The canonical structural digest of a brief's content.
565
+ *
566
+ * @remarks
567
+ * `trace` and `hash` are stripped before digesting, so the value is the identity of what
568
+ * the brief SAYS rather than of a particular pinning. Deterministic across runs — the
569
+ * same interprets `digestValue` the fleet uses everywhere else.
570
+ *
571
+ * @param source - The brief to digest.
572
+ * @returns An eight-hex-digit digest.
573
+ *
574
+ * @example
575
+ * ```ts
576
+ * import { brief, briefToHash, pinBrief, task } from '@orkestrel/brief'
577
+ *
578
+ * const draft = brief(task('plan', 'ops', 'Plan the release.'))
579
+ * briefToHash(draft) === briefToHash(pinBrief(draft)) // true — pinning does not move it
580
+ * ```
581
+ */
582
+ export declare function briefToHash(source: Brief): string;
583
+
584
+ /**
585
+ * Project a brief into the copy-ready agent prompt.
586
+ *
587
+ * @remarks
588
+ * Paths are REFERENCED, never inlined — the executor retrieves them. An empty section is
589
+ * omitted entirely, so the rendering carries no filler an executor must read past.
590
+ *
591
+ * @param source - The brief to render.
592
+ * @returns The markdown prompt.
593
+ *
594
+ * @example
595
+ * ```ts
596
+ * import { brief, briefToMarkdown, task } from '@orkestrel/brief'
597
+ *
598
+ * briefToMarkdown(brief(task('review', 'code', 'Review the gate rules.')))
599
+ * // '# Brief: Review the gate rules.\n\nreview · code\n\n## Output\n\n- format: markdown\n'
600
+ * ```
601
+ */
602
+ export declare function briefToMarkdown(input: Brief): string;
603
+
604
+ /**
605
+ * Project a brief into the reasons `Subject` of readiness measures the gate reads.
606
+ *
607
+ * @param source - The brief to measure.
608
+ * @returns A flat record of counts plus the task's two vocabulary values.
609
+ *
610
+ * @example
611
+ * ```ts
612
+ * import { brief, briefToSubject, proof, task } from '@orkestrel/brief'
613
+ *
614
+ * briefToSubject(brief(task('test', 'code', 'Cover the gate.'), { proofs: [proof('x', 'y')] }))
615
+ * // { operation: 'test', domain: 'code', sentences: 1, proofs: 1, … }
616
+ * ```
617
+ */
618
+ export declare function briefToSubject(source: Brief): Subject;
619
+
620
+ /**
621
+ * The one-line census `pinBrief` stamps onto a brief.
622
+ *
623
+ * @remarks
624
+ * Extracted so it has ONE implementation. `pinBrief` derives it and `BriefManager` re-derives
625
+ * it to reconcile an inbound brief's own `trace` against its content — an inbound `trace` is
626
+ * shape-checked rather than verified, and it is the line `briefToMarkdown` prints at the top
627
+ * of the executor's prompt, so a stale one misdescribes the brief where it is most read.
628
+ *
629
+ * @param source - The brief to describe.
630
+ * @returns The census line: operation/domain, outcomes, blocking-over-total gaps, proofs.
631
+ *
632
+ * @example
633
+ * ```ts
634
+ * import { brief, briefToTrace, task } from '@orkestrel/brief'
635
+ *
636
+ * briefToTrace(brief(task('document', 'writing', 'Write the guide.')))
637
+ * // 'document/writing · outcomes:0 · gaps:0/0 · proofs:0'
638
+ * ```
639
+ */
640
+ export declare function briefToTrace(source: Brief): string;
641
+
642
+ /**
643
+ * One external source — what it is called, where it lives, and why it is cited.
644
+ *
645
+ * @remarks
646
+ * List ORDER is the trust order; there is no per-entry weight.
647
+ *
648
+ * The off-repository twin of {@link Reference}: `note` says why the entry is listed, in both.
649
+ * It carries `name` where a reference does not because a path is already readable and a URL
650
+ * is not, so the name is the citation's readable half rather than a classifier in disguise.
651
+ *
652
+ * It carries no classifier for a different reason than a reference does. A reference has none
653
+ * because its container already fixes what it is; a citation has none because the closed
654
+ * vocabulary this once held — `docs`, `spec`, `api`, `standard` — was a document taxonomy no
655
+ * mechanism read, and its members were not disjoint, so a page documenting an API or a
656
+ * specification that is also a standard forced an arbitrary choice.
657
+ *
658
+ * `url` is validated as a single line and NOT as a URL, so `url: 'docs'` is on-contract. That
659
+ * is a limit rather than an oversight: constraining it needs a `pattern` on `citationShape`,
660
+ * and the shape DSL's seeded generator builds a random alphanumeric string and throws when it
661
+ * fails the pattern — so any pattern requiring a scheme's colon makes `createBriefContract()`
662
+ * ungeneratable for the whole brief. Constraining only the guard would leave the guard and the
663
+ * compiled shape disagreeing, which is the parity this package holds in lockstep. Four working
664
+ * mechanisms beat one stricter member.
665
+ *
666
+ * The cost lands on one migration: `citation` takes `(name, url, note)` where it once took
667
+ * `(name, role, url)` — three strings either way, so a stale call still compiles and still
668
+ * passes the guard, and only renders wrong. Nothing is published, so a version bump carries it.
669
+ */
670
+ export declare interface Citation {
671
+ readonly name: string;
672
+ readonly url: string;
673
+ readonly note: string;
674
+ }
675
+
676
+ /**
677
+ * Build a `Citation`.
678
+ *
679
+ * @param name - The source's display name.
680
+ * @param url - Where the source lives.
681
+ * @param note - Why the source is cited.
682
+ * @returns A fresh `Citation`.
683
+ *
684
+ * @example
685
+ * ```ts
686
+ * import { citation } from '@orkestrel/brief'
687
+ *
688
+ * citation(
689
+ * 'MDN Constraint Validation',
690
+ * 'https://developer.mozilla.org/',
691
+ * 'the native validity behavior being adopted',
692
+ * )
693
+ * ```
694
+ */
695
+ export declare function citation(name: string, url: string, note: string): Citation;
696
+
697
+ /** The `Citation` shape — a name, a locator, and why the source is cited. */
698
+ export declare const citationShape: ObjectShape<{
699
+ name: StringShape;
700
+ url: StringShape;
701
+ note: StringShape;
702
+ }, false>;
703
+
704
+ /**
705
+ * Count the sentences a statement holds.
706
+ *
707
+ * @remarks
708
+ * A terminator run (`.`, `!`, `?`) followed by whitespace or the end of the text closes one
709
+ * sentence, and a trailing run with no terminator closes one more.
710
+ *
711
+ * LIMIT, stated because it decides a gate: an embedded abbreviation reads as a boundary, so
712
+ * `'Ask Dr. Smith'` and `'Compare React vs. Vue'` count TWO and the `single` rule refuses
713
+ * them. Rewrite the statement without the abbreviation — a brief's statement is one
714
+ * imperative sentence naming the object of the work, and it rarely needs one.
715
+ *
716
+ * This is inherent rather than unfinished. Separating `'Dr.'` from a real boundary needs a
717
+ * lexicon or a heuristic over capitalisation and word length, and a heuristic gets a
718
+ * different set of statements wrong — quietly, in the direction of letting a genuinely
719
+ * compound statement through, which is the failure this rule exists to prevent. `validateBrief`
720
+ * therefore reports the count and lets the author judge, rather than guessing at intent.
721
+ *
722
+ * @param statement - The statement to measure.
723
+ * @returns The sentence count; `0` for empty or whitespace-only text.
724
+ *
725
+ * @example
726
+ * ```ts
727
+ * import { countSentences } from '@orkestrel/brief'
728
+ *
729
+ * countSentences('Refactor useForm to native APIs.') // 1
730
+ * countSentences('Refactor useForm. Then update the tests') // 2 — the tail counts
731
+ * countSentences('Ask Dr. Smith') // 2 — an abbreviation reads as a boundary
732
+ * countSentences('') // 0
733
+ * ```
734
+ */
735
+ export declare function countSentences(statement: string): number;
736
+
737
+ /**
738
+ * Create a compilation orchestrator.
739
+ *
740
+ * @remarks
741
+ * With no engines supplied the compiler wires its own: a default `createInterpret()`
742
+ * (empty vocabularies, so `options.actions` / `options.domains` drive `deriveTask`) and a
743
+ * `createReason` carrying one `LogicalReasoner` for the gate. Pass your own to share
744
+ * instances or observe their emitters — the compiler destroys ONLY what it created.
745
+ *
746
+ * @param options - Engines to borrow, the two intent vocabularies, and emitter hooks.
747
+ * @returns A working {@link BriefCompilerInterface}.
748
+ *
749
+ * @example
750
+ * ```ts
751
+ * import { createBriefCompiler } from '@orkestrel/brief'
752
+ *
753
+ * const compiler = createBriefCompiler({ actions: { refactor: 'refactor' }, domains: { code: 'code' } })
754
+ * compiler.destroy()
755
+ * ```
756
+ */
757
+ export declare function createBriefCompiler(options?: BriefCompilerOptions): BriefCompilerInterface;
758
+
759
+ /**
760
+ * Compile `briefShape` into a guard, parser, JSON Schema, and seeded generator bundle.
761
+ *
762
+ * @remarks
763
+ * The schema is what a tool boundary needs — hand it to `schemaToParameters` — and
764
+ * `generate(seededRandom(n))` yields a reproducible on-contract brief for tests. This
765
+ * bundle and the hand-composed `isBrief` are two independent mechanisms over one
766
+ * vocabulary; `tests/src/core/shapers.test.ts` is what holds them in lockstep.
767
+ *
768
+ * @returns A `ContractInterface` over `Brief`.
769
+ *
770
+ * @example
771
+ * ```ts
772
+ * import { createBriefContract } from '@orkestrel/brief'
773
+ * import { schemaToParameters, seededRandom } from '@orkestrel/contract'
774
+ *
775
+ * const contract = createBriefContract()
776
+ * schemaToParameters(contract.schema) // the open tool-parameters record, no `as` anywhere
777
+ * contract.generate(seededRandom(42)) // a reproducible on-contract brief
778
+ * ```
779
+ */
780
+ export declare function createBriefContract(): ContractInterface<Brief>;
781
+
782
+ /**
783
+ * Create a brief registry.
784
+ *
785
+ * @param options - An optional seed collection plus emitter hooks.
786
+ * @returns A working {@link BriefManagerInterface}.
787
+ *
788
+ * @example
789
+ * ```ts
790
+ * import { createBriefManager } from '@orkestrel/brief'
791
+ *
792
+ * const briefs = createBriefManager()
793
+ * briefs.size // 0
794
+ * briefs.destroy()
795
+ * ```
796
+ */
797
+ export declare function createBriefManager(options?: BriefManagerOptions): BriefManagerInterface;
798
+
799
+ /**
800
+ * `16` — the default turn cap `briefToGoal` renders.
801
+ *
802
+ * @remarks
803
+ * Domain-qualified so the barrel stays collision-free as sibling modules add their own
804
+ * turn defaults.
805
+ */
806
+ export declare const DEFAULT_BRIEF_TURNS = 16;
807
+
808
+ /**
809
+ * Derive `Gap[]` from an interprets `Ambiguity[]`.
810
+ *
811
+ * @remarks
812
+ * A REQUIRED ambiguity becomes a BLOCKING gap — the gate must fail closed on it. The
813
+ * rest stay open, to be answered with a recorded assumption. An array field path flattens
814
+ * through reasons `formatField`.
815
+ *
816
+ * @param ambiguities - The ambiguities an interpret pipeline surfaced.
817
+ * @returns One `Gap` per ambiguity, in surfacing order.
818
+ *
819
+ * @example
820
+ * ```ts
821
+ * import { deriveGaps } from '@orkestrel/brief'
822
+ *
823
+ * deriveGaps([{ field: 'output', question: 'Diff or files?', candidates: [], required: true }])
824
+ * // [{ field: 'output', question: 'Diff or files?', blocking: true }]
825
+ * ```
826
+ */
827
+ export declare function deriveGaps(ambiguities: readonly Ambiguity[]): readonly Gap[];
828
+
829
+ /**
830
+ * Derive `Given[]` from an interprets `Entity[]`.
831
+ *
832
+ * @remarks
833
+ * Every extracted entity becomes one `extracted` fact. A nameless entity is dropped; an
834
+ * object value renders through interprets `canonicalize`, so the text is key-order stable.
835
+ *
836
+ * @param entities - The entities an interpret pipeline extracted.
837
+ * @returns One `Given` per named entity, in extraction order.
838
+ *
839
+ * @example
840
+ * ```ts
841
+ * import { deriveGivens } from '@orkestrel/brief'
842
+ *
843
+ * deriveGivens([
844
+ * { name: 'value', value: 3, provenance: { category: 'extracted' }, confidence: 1 },
845
+ * ]) // [{ category: 'extracted', name: 'value', value: '3' }]
846
+ * ```
847
+ */
848
+ export declare function deriveGivens(entities: readonly Entity[]): readonly Given[];
849
+
850
+ /**
851
+ * Derive one imperative statement from free text.
852
+ *
853
+ * @remarks
854
+ * Whitespace collapses, the first character uppercases, and a terminator is appended
855
+ * when the text carries none. Nothing else is invented.
856
+ *
857
+ * @param text - The raw request text.
858
+ * @returns The statement, or `''` for empty or whitespace-only text.
859
+ *
860
+ * @example
861
+ * ```ts
862
+ * import { deriveStatement } from '@orkestrel/brief'
863
+ *
864
+ * deriveStatement(' clean up useForm ') // 'Clean up useForm.'
865
+ * deriveStatement('') // ''
866
+ * ```
867
+ */
868
+ export declare function deriveStatement(text: string): string;
869
+
870
+ /**
871
+ * Derive a `Task` from an interprets `Intent` through the caller's vocabularies.
872
+ *
873
+ * @remarks
874
+ * The vocabularies are the CALLER's policy: this maps and never guesses. An action or
875
+ * domain the caller did not map — or mapped to an off-vocabulary value — yields
876
+ * `undefined` rather than an invented task. Inherited keys never resolve.
877
+ *
878
+ * @param intent - The classified intent from an interpret pipeline.
879
+ * @param text - The text the statement derives from.
880
+ * @param actions - Maps an intent action onto a closed `TaskOperation`.
881
+ * @param domains - Maps an intent domain onto a closed `TaskDomain`.
882
+ * @returns The derived `Task`, or `undefined` when either side is unmapped.
883
+ *
884
+ * @example
885
+ * ```ts
886
+ * import { deriveTask } from '@orkestrel/brief'
887
+ *
888
+ * const intent = { action: 'migrate', domain: 'code', confidence: 1 }
889
+ * deriveTask(intent, 'migrate the stores', { migrate: 'migrate' }, { code: 'code' })
890
+ * // { operation: 'migrate', domain: 'code', statement: 'Migrate the stores.' }
891
+ * deriveTask(intent, 'migrate the stores', {}, { code: 'code' }) // undefined
892
+ * ```
893
+ */
894
+ export declare function deriveTask(intent: Intent, text: string, actions: Readonly<Record<string, TaskOperation>>, domains: Readonly<Record<string, TaskDomain>>): Task | undefined;
895
+
896
+ /**
897
+ * The subagent projection of a brief.
898
+ *
899
+ * @remarks
900
+ * Two orthogonal axes, not five partitions. PERMISSION is `read`, `edit`, `locked`, and
901
+ * `forbidden`, with `edit` the owned set two concurrent dispatches must not intersect on and
902
+ * `locked` and `forbidden` do-not-touch. PRECEDENCE is `authority`, in ranked order, index 0
903
+ * winning every conflict.
904
+ *
905
+ * `authority` therefore OVERLAPS the permission arrays by design: a ranked path ALWAYS also
906
+ * sits in `read`, `edit`, or `locked`, because the executor has to open what it obeys, and
907
+ * the `granted` gate rule refuses a brief where it does not. Read the four to decide what may
908
+ * be touched and `authority` to decide what wins. Never union all five — that was already
909
+ * wrong before `authority` existed, because `forbidden` is an exclusion rather than a grant.
910
+ * `authority` is a path list rather than a rendered section because a machine consumer must
911
+ * reach mandatory authority without parsing `prompt`, which is written for a model.
912
+ *
913
+ * The four permission arrays are mutually disjoint in a GATED brief — `findManifestOverlaps`
914
+ * measures it and the `disjoint` rule refuses on it. `briefToDispatch` is a pure projection
915
+ * and runs no gate, so projecting an unvetted draft can produce arrays that intersect. Gate
916
+ * before you dispatch, or treat disjointness as unproven.
917
+ */
918
+ export declare interface Dispatch {
919
+ readonly prompt: string;
920
+ readonly authority: readonly string[];
921
+ readonly read: readonly string[];
922
+ readonly edit: readonly string[];
923
+ readonly locked: readonly string[];
924
+ readonly forbidden: readonly string[];
925
+ }
926
+
927
+ /** The `draft` phase snapshot — the caller's input in, an unpinned `Brief` out. */
928
+ export declare interface DraftStageRecord {
929
+ readonly stage: 'draft';
930
+ readonly input: BriefInput;
931
+ readonly output?: Brief;
932
+ readonly error?: string;
933
+ }
934
+
935
+ /**
936
+ * Render a value thrown by a stage into a message.
937
+ *
938
+ * @remarks
939
+ * TOTAL: it never throws, for any input. That is load-bearing rather than tidy, because this
940
+ * is the containment code itself — `compile` calls it inside the `catch` that turns a thrown
941
+ * stage into a recorded `BriefStageFailure`. A throw here escapes `compile` uncontained and
942
+ * falsifies the package's central promise that a failing stage yields an incomplete
943
+ * `Briefing` rather than an exception.
944
+ *
945
+ * Three real inputs used to throw: an `Error` subclass whose `message` getter throws, a value
946
+ * whose string conversion throws, and a null-prototype object, which has no inherited
947
+ * conversion for String() to reach. Each is wrapped, and an unreadable value degrades to its
948
+ * type rather than propagating.
949
+ *
950
+ * @param error - The caught value, of any shape.
951
+ * @returns The `Error` message when there is one, otherwise the value stringified; a fixed
952
+ * description when the value cannot be read at all.
953
+ *
954
+ * @example
955
+ * ```ts
956
+ * import { errorToMessage } from '@orkestrel/brief'
957
+ *
958
+ * errorToMessage(new Error('boom')) // 'boom'
959
+ * errorToMessage('boom') // 'boom'
960
+ * errorToMessage(Object.create(null)) // 'an unreadable object was thrown'
961
+ * ```
962
+ */
963
+ export declare function errorToMessage(error: unknown): string;
964
+
965
+ /** One input to output exemplar — the highest-leverage ambiguity remover. */
966
+ export declare interface Example {
967
+ readonly input: string;
968
+ readonly output: string;
969
+ readonly note?: string;
970
+ }
971
+
972
+ /**
973
+ * Build an `Example`.
974
+ *
975
+ * @param input - The exemplar input.
976
+ * @param result - The expected output for that input.
977
+ * @param note - Optional detail; the key is OMITTED when absent.
978
+ * @returns A fresh `Example`.
979
+ *
980
+ * @example
981
+ * ```ts
982
+ * import { example } from '@orkestrel/brief'
983
+ *
984
+ * example('<input required>', 'validity read from el.validity')
985
+ * ```
986
+ */
987
+ export declare function example(input: string, result: string, note?: string): Example;
988
+
989
+ /** The `Example` shape — one input to output exemplar. */
990
+ export declare const exampleShape: ObjectShape<{
991
+ input: StringShape;
992
+ output: StringShape;
993
+ note: OptionalShape<StringShape>;
994
+ }, false>;
995
+
996
+ /**
997
+ * Render one exemplar as markdown lines.
998
+ *
999
+ * @remarks
1000
+ * An `Example`'s two sides are the only brief members permitted to span lines, so a
1001
+ * single-line pair renders as one row and a multi-line pair renders as a fenced block.
1002
+ * Fencing is what stops the one permissive field from forging a heading.
1003
+ *
1004
+ * @param entry - The exemplar to render.
1005
+ * @returns The markdown lines, without a trailing blank.
1006
+ *
1007
+ * @example
1008
+ * ```ts
1009
+ * import { example, exampleToLines } from '@orkestrel/brief'
1010
+ *
1011
+ * exampleToLines(example('<input required>', 'el.validity')) // ['- ` <input required> ` → ` el.validity `']
1012
+ * ```
1013
+ */
1014
+ export declare function exampleToLines(entry: Example): readonly string[];
1015
+
1016
+ /**
1017
+ * The gaps that block emission.
1018
+ *
1019
+ * @param source - The brief to inspect.
1020
+ * @returns Every gap carrying `blocking: true`, in declaration order.
1021
+ *
1022
+ * @example
1023
+ * ```ts
1024
+ * import { brief, findBlockingGaps, gap, task } from '@orkestrel/brief'
1025
+ *
1026
+ * const draft = brief(task('plan', 'ops', 'Plan the release.'), {
1027
+ * gaps: [gap('output', 'Diff or files?', { blocking: true })],
1028
+ * })
1029
+ * findBlockingGaps(draft).length // 1
1030
+ * ```
1031
+ */
1032
+ export declare function findBlockingGaps(source: Brief): readonly Gap[];
1033
+
1034
+ /**
1035
+ * The paths appearing in more than one manifest partition.
1036
+ *
1037
+ * @remarks
1038
+ * Duplicates WITHIN one partition are not an overlap; the four partitions must be
1039
+ * mutually disjoint, which is what `validateBrief` errors on.
1040
+ *
1041
+ * Paths are compared as EXACT strings. A glob is never expanded, so `edit: 'app/file.ts'`
1042
+ * and `forbidden: 'app/**'` are not reported as an overlap even though a walker would place
1043
+ * one inside the other. Disjointness here is a property of the written paths.
1044
+ *
1045
+ * @param source - The brief to inspect.
1046
+ * @returns Each overlapping path once, in first-seen partition order.
1047
+ *
1048
+ * @example
1049
+ * ```ts
1050
+ * import { brief, findManifestOverlaps, manifest, reference, task } from '@orkestrel/brief'
1051
+ *
1052
+ * const draft = brief(task('debug', 'code', 'Fix the leak.'), {
1053
+ * manifest: manifest({
1054
+ * edit: [reference('src/core/BriefCompiler.ts', 'the leaking pipeline')],
1055
+ * locked: [reference('src/core/BriefCompiler.ts', 'the published contract')],
1056
+ * }),
1057
+ * })
1058
+ * findManifestOverlaps(draft) // ['src/core/BriefCompiler.ts']
1059
+ * ```
1060
+ */
1061
+ export declare function findManifestOverlaps(source: Brief): readonly string[];
1062
+
1063
+ /**
1064
+ * The authority paths the manifest never grants access to.
1065
+ *
1066
+ * @remarks
1067
+ * An authority the executor cannot open is an instruction it cannot follow, so every ranked
1068
+ * path must appear in `read`, `edit`, or `locked`. Those three are the grants: `locked` is a
1069
+ * grant, because read-only is exactly what obeying a file requires.
1070
+ *
1071
+ * This subsumes the narrower question of an authority sitting in `forbidden`. The four
1072
+ * partitions are disjoint — `findManifestOverlaps` and the `disjoint` rule enforce it — so a
1073
+ * forbidden path is in none of the three grants and is reported here. An authority named in
1074
+ * NO partition at all is reported for the same reason, and that is the case a forbidden-only
1075
+ * check misses entirely: the brief simply never says the executor may open what it must obey.
1076
+ *
1077
+ * Paths are compared as EXACT strings, matching `findManifestOverlaps`. A glob is never
1078
+ * expanded, so `read: 'guides/**'` does not grant `authority: 'guides/brief.md'`. State a
1079
+ * grant as the same literal path the authority carries.
1080
+ *
1081
+ * @param source - The brief to inspect.
1082
+ * @returns Each ungranted authority path once, in authority order; empty when all are granted.
1083
+ *
1084
+ * @example
1085
+ * ```ts
1086
+ * import { brief, findUngrantedAuthority, manifest, reference, task } from '@orkestrel/brief'
1087
+ *
1088
+ * const draft = brief(task('debug', 'code', 'Fix the leak.'), {
1089
+ * authority: [reference('AGENTS.md', 'project law')],
1090
+ * manifest: manifest(),
1091
+ * })
1092
+ * findUngrantedAuthority(draft) // ['AGENTS.md'] — ranked, but no partition opens it
1093
+ * ```
1094
+ */
1095
+ export declare function findUngrantedAuthority(source: Brief): readonly string[];
1096
+
1097
+ /**
1098
+ * The readiness rules a brief fails, computed directly from its own measures.
1099
+ *
1100
+ * @remarks
1101
+ * The gate's decision, in code. `gateDefinition()` states the same six rules as data for a
1102
+ * reasoner to narrate, and a narration is not a decision: `BriefCompilerOptions.reason` lets a
1103
+ * caller supply the engine, and an engine that answers "met" to everything would otherwise
1104
+ * emit a brief with no proofs. `compile` refuses on THIS and keeps the verdict for its
1105
+ * trace, so a supplied engine can add detail and never remove a refusal.
1106
+ *
1107
+ * The two must agree. `tests/src/core/helpers.test.ts` drives both over one value set, which
1108
+ * is what stops the data and the code from drifting apart.
1109
+ *
1110
+ * @param source - The brief to measure.
1111
+ * @returns The unmet rule ids, in gate order; empty when the brief is ready.
1112
+ *
1113
+ * @example
1114
+ * ```ts
1115
+ * import { brief, findUnmetRules, outcome, proof, task } from '@orkestrel/brief'
1116
+ *
1117
+ * findUnmetRules(brief(task('plan', 'ops', 'Plan the release.'))) // ['aimed', 'proven']
1118
+ * findUnmetRules(
1119
+ * brief(task('plan', 'ops', 'Plan the release.'), {
1120
+ * outcomes: [outcome(1, 'shipped')],
1121
+ * proofs: [proof('x', 'npm test')],
1122
+ * }),
1123
+ * ) // []
1124
+ * ```
1125
+ */
1126
+ export declare function findUnmetRules(source: Brief): readonly string[];
1127
+
1128
+ /**
1129
+ * The open gaps with no assumption to stand on.
1130
+ *
1131
+ * @remarks
1132
+ * The discipline is exactly one recorded assumption per open gap, so the open gaps past
1133
+ * the assumption count are the unpaired ones. A blocking gap is never unpaired — it is
1134
+ * a question, not something to assume around.
1135
+ *
1136
+ * @param source - The brief to inspect.
1137
+ * @returns The surplus open gaps, in declaration order.
1138
+ *
1139
+ * @example
1140
+ * ```ts
1141
+ * import { brief, findUnpairedGaps, gap, task } from '@orkestrel/brief'
1142
+ *
1143
+ * const draft = brief(task('plan', 'ops', 'Plan the release.'), {
1144
+ * gaps: [gap('rules', 'Keep the wording?'), gap('output', 'Diff or files?')],
1145
+ * assumptions: ['Wording is preserved.'],
1146
+ * })
1147
+ * findUnpairedGaps(draft).length // 1
1148
+ * ```
1149
+ */
1150
+ export declare function findUnpairedGaps(source: Brief): readonly Gap[];
1151
+
1152
+ /**
1153
+ * Freeze one branch of a value graph, skipping what the visited set already holds.
1154
+ *
1155
+ * @param value - The branch to freeze.
1156
+ * @param seen - The objects already frozen on this walk; what makes a cycle terminate.
1157
+ * @returns The same branch, now frozen.
1158
+ *
1159
+ * @example
1160
+ * ```ts
1161
+ * import { freezeBranch } from '@orkestrel/brief'
1162
+ *
1163
+ * freezeBranch({ a: [1] }, new WeakSet()) // frozen, one level of nesting included
1164
+ * ```
1165
+ */
1166
+ export declare function freezeBranch<T>(value: T, seen: WeakSet<object>): T;
1167
+
1168
+ /**
1169
+ * Freeze a value and everything reachable from it.
1170
+ *
1171
+ * @remarks
1172
+ * `Object.freeze` is SHALLOW, so freezing a record leaves every nested array and object
1173
+ * writable. A `Briefing` is documented as a replayable record, and a shallow freeze let a
1174
+ * consumer rewrite the recorded stage input after the digest describing it was already
1175
+ * sealed — the replay and its hash could disagree.
1176
+ *
1177
+ * Cycles terminate: `structuredClone` preserves them, so a naive walk would not return.
1178
+ * Delegates each branch to `freezeBranch` with the shared visited set.
1179
+ *
1180
+ * Reaches PLAIN objects and arrays, which is the whole of a `Brief` — it is JSON-serializable
1181
+ * by contract. A `Map`, `Set`, or typed array is frozen as an object and its CONTENTS are left
1182
+ * writable, and `Object.isFrozen` reports `true` for it either way. Nothing this package
1183
+ * produces contains one; a caller freezing their own value should know the limit.
1184
+ *
1185
+ * @param value - The value to freeze in place; returned for convenience.
1186
+ * @returns The same value, now deeply frozen.
1187
+ *
1188
+ * @example
1189
+ * ```ts
1190
+ * import { freezeDeep } from '@orkestrel/brief'
1191
+ *
1192
+ * const owned = freezeDeep({ outcomes: [{ rank: 1 }] })
1193
+ * Object.isFrozen(owned.outcomes) // true — the nested array too
1194
+ * ```
1195
+ */
1196
+ export declare function freezeDeep<T>(value: T): T;
1197
+
1198
+ /**
1199
+ * One unknown the brief has not resolved.
1200
+ *
1201
+ * @remarks
1202
+ * `blocking: true` means no safe default exists and the gate must fail closed. An
1203
+ * open gap proceeds on a narrow recorded assumption instead.
1204
+ */
1205
+ export declare interface Gap {
1206
+ readonly field: string;
1207
+ readonly question: string;
1208
+ readonly blocking: boolean;
1209
+ readonly candidates?: readonly string[];
1210
+ }
1211
+
1212
+ /**
1213
+ * Build a `Gap`.
1214
+ *
1215
+ * @param field - The brief section the unknown belongs to.
1216
+ * @param question - The question that would close it.
1217
+ * @param overrides - Optional `blocking` (defaults `false`) and `candidates`; an absent
1218
+ * `candidates` key is OMITTED entirely.
1219
+ * @returns A fresh `Gap`.
1220
+ *
1221
+ * @example
1222
+ * ```ts
1223
+ * import { gap } from '@orkestrel/brief'
1224
+ *
1225
+ * gap('rules', 'Should validation message wording change?') // blocking: false
1226
+ * gap('output', 'Diff or full files?', { blocking: true, candidates: ['diff', 'code'] })
1227
+ * ```
1228
+ */
1229
+ export declare function gap(field: string, question: string, overrides?: Partial<Omit<Gap, 'field' | 'question'>>): Gap;
1230
+
1231
+ /** The `Gap` shape — an unknown, whether it blocks, and the candidates that would close it. */
1232
+ export declare const gapShape: ObjectShape<{
1233
+ field: StringShape;
1234
+ question: StringShape;
1235
+ blocking: BooleanShape;
1236
+ candidates: OptionalShape<ArrayShape<StringShape>>;
1237
+ }, false>;
1238
+
1239
+ /** `'gate'` — the id of the `gateDefinition()` logical definition. */
1240
+ export declare const GATE_ID = "gate";
1241
+
1242
+ /**
1243
+ * Build the fail-closed readiness gate as a reasons `LogicalDefinition`.
1244
+ *
1245
+ * @remarks
1246
+ * Six readiness rules each derive one named fact from `briefToSubject`'s measures, and a
1247
+ * final `ready` rule conjoins all six. Forward chaining reports the LAST rule's
1248
+ * conclusion, so `LogicalResult.conclusion` is exactly `ready`.
1249
+ *
1250
+ * The gate takes NO parameters, and that is deliberate rather than unfinished. The
1251
+ * reasoner overlays every derived fact into one flat namespace, so a caller rule named
1252
+ * for a readiness fact overwrites it and `ready` then conjoins a fact no base rule
1253
+ * proved — a refusal silently becomes a pass. Readiness is this package's contract, not
1254
+ * a caller setting. A caller who needs different readiness composes their own
1255
+ * `LogicalDefinition` over `briefToSubject` and evaluates it on their own reasoner; both
1256
+ * are exported for exactly that, and neither can reach this definition.
1257
+ *
1258
+ * @returns A fresh `LogicalDefinition` with id `GATE_ID`.
1259
+ *
1260
+ * @example
1261
+ * ```ts
1262
+ * import { briefToSubject, gateDefinition } from '@orkestrel/brief'
1263
+ * import { createLogicalReasoner, createReason } from '@orkestrel/reason'
1264
+ *
1265
+ * const reason = createReason({ reasoners: [createLogicalReasoner()] })
1266
+ * const verdict = reason.reason(briefToSubject(pinned), gateDefinition())
1267
+ * reason.destroy()
1268
+ * ```
1269
+ */
1270
+ export declare function gateDefinition(): LogicalDefinition;
1271
+
1272
+ /** The `gate` phase snapshot — the readiness `Subject` in, the reasoner's verdict out. */
1273
+ export declare interface GateStageRecord {
1274
+ readonly stage: 'gate';
1275
+ readonly input: Subject;
1276
+ readonly output?: LogicalResult;
1277
+ readonly error?: string;
1278
+ }
1279
+
1280
+ /** One context fact handed to the executor — a convention, a version, a constraint value. */
1281
+ export declare interface Given {
1282
+ readonly category: string;
1283
+ readonly name: string;
1284
+ readonly value: string;
1285
+ }
1286
+
1287
+ /**
1288
+ * Build a `Given`.
1289
+ *
1290
+ * @param category - The kind of fact — a convention, a version, a constraint.
1291
+ * @param name - The fact's name.
1292
+ * @param value - The fact's value, already rendered as text.
1293
+ * @returns A fresh `Given`.
1294
+ *
1295
+ * @example
1296
+ * ```ts
1297
+ * import { given } from '@orkestrel/brief'
1298
+ *
1299
+ * given('convention', 'indentation', 'tabs')
1300
+ * ```
1301
+ */
1302
+ export declare function given(category: string, name: string, value: string): Given;
1303
+
1304
+ /** The `Given` shape — one categorized context fact. */
1305
+ export declare const givenShape: ObjectShape<{
1306
+ category: StringShape;
1307
+ name: StringShape;
1308
+ value: StringShape;
1309
+ }, false>;
1310
+
1311
+ /**
1312
+ * The `interpret` phase snapshot — raw text in, an `Interpretation` out.
1313
+ *
1314
+ * @remarks
1315
+ * `output` is absent exactly when `error` is present, which is what makes the phase
1316
+ * failure derivable rather than stored twice.
1317
+ */
1318
+ export declare interface InterpretStageRecord {
1319
+ readonly stage: 'interpret';
1320
+ readonly input: string;
1321
+ readonly output?: Interpretation;
1322
+ readonly error?: string;
1323
+ }
1324
+
1325
+ /**
1326
+ * `true` when the value satisfies the whole exact-record `Brief` contract.
1327
+ *
1328
+ * @remarks
1329
+ * Every section must be present; an extra key fails. `trace` and `hash` are the only
1330
+ * optional members, because `pinBrief` rather than the author fills them.
1331
+ */
1332
+ export declare const isBrief: Guard<Brief>;
1333
+
1334
+ /**
1335
+ * Narrow a caught value to a {@link BriefError}.
1336
+ *
1337
+ * @param value - The caught value to inspect.
1338
+ * @returns `true` when `value` is a `BriefError`.
1339
+ *
1340
+ * @example
1341
+ * ```ts
1342
+ * import { BriefError, isBriefError } from '@orkestrel/brief'
1343
+ *
1344
+ * try {
1345
+ * throw new BriefError('DESTROYED', 'BriefCompiler has been destroyed')
1346
+ * } catch (error) {
1347
+ * if (isBriefError(error)) error.code // 'DESTROYED'
1348
+ * }
1349
+ * ```
1350
+ */
1351
+ export declare function isBriefError(value: unknown): value is BriefError;
1352
+
1353
+ /** `true` when the value is a well-formed `Citation` — all three members single-line. */
1354
+ export declare const isCitation: Guard<Citation>;
1355
+
1356
+ /**
1357
+ * `true` when the value is a well-formed `Example`.
1358
+ *
1359
+ * @remarks
1360
+ * An exemplar's two sides are the ONLY members a brief lets span lines, because they
1361
+ * carry code. `briefToMarkdown` fences them rather than rendering them as a row.
1362
+ */
1363
+ export declare const isExample: Guard<Example>;
1364
+
1365
+ /** `true` when the value is a well-formed `Gap`. */
1366
+ export declare const isGap: Guard<Gap>;
1367
+
1368
+ /** `true` when the value is a well-formed `Given` — `value` may be empty but stays one line. */
1369
+ export declare const isGiven: Guard<Given>;
1370
+
1371
+ /** `true` when the value is a non-empty string holding no line terminator. */
1372
+ export declare const isLine: Guard<string>;
1373
+
1374
+ /**
1375
+ * `true` when the value is a well-formed reasons `LogicalResult`.
1376
+ *
1377
+ * @remarks
1378
+ * The gate's reasoner is supplied by the caller through `BriefCompilerOptions.reason`, so its
1379
+ * return value is FOREIGN data no matter how well-typed the interface is. `BriefCompiler`
1380
+ * dereferences `reasoning`, `conclusion`, and `rules`; checking one field left a malformed
1381
+ * result to throw a raw `TypeError` out of `compile`, from the very code that contains stage
1382
+ * failures. Total: returns `false` for `undefined`, `null`, and every off-shape value.
1383
+ *
1384
+ * Checks the WHOLE published shape rather than only the three members read today, because a
1385
+ * guard that narrows to `LogicalResult` while ignoring four of its members is unsound.
1386
+ *
1387
+ * OPEN on unknown keys, deliberately. The exact-record combinator this file uses elsewhere
1388
+ * refuses a value a FOREIGN interface permits: `LogicalResult` is a TypeScript interface,
1389
+ * so a conforming reasoner returning a richer result is still returning a `LogicalResult`. An
1390
+ * exact check refused it and failed the gate closed on a valid engine — trading a loud crash
1391
+ * for a wrong refusal, which is the worse of the two. Exactness belongs on records this
1392
+ * package OWNS, where an extra key means the caller misunderstood the contract.
1393
+ *
1394
+ * `count` is checked as a number rather than an integer for the same reason: the published
1395
+ * type says `number`, and narrowing past a foreign contract is the same mistake.
1396
+ */
1397
+ export declare const isLogicalVerdict: Guard<LogicalResult>;
1398
+
1399
+ /**
1400
+ * `true` when the value is a well-formed `Manifest`.
1401
+ *
1402
+ * @remarks
1403
+ * Partition presence only — disjointness is `validateBrief`'s semantic pass.
1404
+ */
1405
+ export declare const isManifest: Guard<Manifest>;
1406
+
1407
+ /**
1408
+ * `true` when the value is a non-null object whose named members can be read.
1409
+ *
1410
+ * @remarks
1411
+ * Wider than the contract package's plain-record guard, which refuses any object carrying its
1412
+ * own prototype — a class instance among them. The verdict guards below narrow FOREIGN
1413
+ * interfaces, and an
1414
+ * interface is satisfied by a class instance as readily as by a literal — refusing one is the
1415
+ * same narrowing-past-the-contract mistake that made an exact-record verdict guard fail the
1416
+ * gate closed on a valid engine.
1417
+ *
1418
+ * Arrays are excluded because no interface this narrows is an array, and admitting one would
1419
+ * let index access stand in for member access.
1420
+ */
1421
+ export declare const isObject: Guard<Record<string, unknown>>;
1422
+
1423
+ /** `true` when the value is a well-formed `Outcome` — `rank` a positive integer. */
1424
+ export declare const isOutcome: Guard<Outcome>;
1425
+
1426
+ /** `true` when the value is a well-formed `Output` — `format` on the closed vocabulary. */
1427
+ export declare const isOutput: Guard<Output>;
1428
+
1429
+ /** `true` when the value is one of the five `OutputFormat` literals. */
1430
+ export declare const isOutputFormat: Guard<OutputFormat>;
1431
+
1432
+ /** `true` when the value is a well-formed `Proof`. */
1433
+ export declare const isProof: Guard<Proof>;
1434
+
1435
+ /** `true` when the value is a well-formed `Reference` — both members required, both single-line. */
1436
+ export declare const isReference: Guard<Reference>;
1437
+
1438
+ /** `true` when the value is a well-formed `Risk` — `severity` on the closed vocabulary. */
1439
+ export declare const isRisk: Guard<Risk>;
1440
+
1441
+ /** `true` when the value is one of the three `RiskSeverity` literals. */
1442
+ export declare const isRiskSeverity: Guard<RiskSeverity>;
1443
+
1444
+ /**
1445
+ * `true` when the value is a well-formed reasons `RuleResult`.
1446
+ *
1447
+ * @remarks
1448
+ * `@orkestrel/reason` publishes the type but no guard for it, and `BriefCompiler` reads these
1449
+ * off a BORROWED engine's return value, so the shape has to be checked rather than trusted.
1450
+ */
1451
+ export declare const isRuleVerdict: Guard<RuleResult>;
1452
+
1453
+ /** `true` when the value is a well-formed `Task` — both vocabularies closed, statement one line. */
1454
+ export declare const isTask: Guard<Task>;
1455
+
1456
+ /** `true` when the value is one of the eight `TaskDomain` literals. */
1457
+ export declare const isTaskDomain: Guard<TaskDomain>;
1458
+
1459
+ /** `true` when the value is one of the twelve `TaskOperation` literals. */
1460
+ export declare const isTaskOperation: Guard<TaskOperation>;
1461
+
1462
+ /**
1463
+ * `true` when the value is a string holding no line terminator, empty included.
1464
+ *
1465
+ * @remarks
1466
+ * `briefToMarkdown` renders each brief field as ONE markdown row, so a field carrying a
1467
+ * line break would forge a heading or an extra manifest row — which is how a rendered
1468
+ * prompt and `briefToDispatch`'s path sets could disagree about the same brief.
1469
+ */
1470
+ export declare const isText: Guard<string>;
1471
+
1472
+ /**
1473
+ * Every line terminator a brief field refuses.
1474
+ *
1475
+ * @remarks
1476
+ * The four ECMAScript line terminators, not just `\n`: a renderer that splits on any of
1477
+ * them would let the other three forge a markdown row. CRLF leads the alternation so a
1478
+ * Windows exemplar splits as ONE break rather than two, which would insert a blank line the
1479
+ * caller never wrote. Kept unanchored and stateless — no `g` flag — so `test` never carries
1480
+ * `lastIndex` between calls.
1481
+ */
1482
+ export declare const LINE_BREAK_PATTERN: RegExp;
1483
+
1484
+ /** A non-empty single-line string — the shape mirror of `isLine`. */
1485
+ export declare const lineShape: StringShape;
1486
+
1487
+ /**
1488
+ * The four disjoint file partitions of a brief.
1489
+ *
1490
+ * @remarks
1491
+ * `read` order is the reading order. A path in more than one partition is a
1492
+ * `validateBrief` error, found by `findManifestOverlaps`.
1493
+ */
1494
+ export declare interface Manifest {
1495
+ readonly read: readonly Reference[];
1496
+ readonly edit: readonly Reference[];
1497
+ readonly locked: readonly Reference[];
1498
+ readonly forbidden: readonly Reference[];
1499
+ }
1500
+
1501
+ /**
1502
+ * Build a `Manifest`, defaulting every absent partition to an empty list.
1503
+ *
1504
+ * @param partitions - The partitions to fill; a partial literal is enough.
1505
+ * @returns A fresh `Manifest` with all four partitions present.
1506
+ *
1507
+ * @example
1508
+ * ```ts
1509
+ * import { manifest, reference } from '@orkestrel/brief'
1510
+ *
1511
+ * manifest({ edit: [reference('src/core/helpers.ts', 'implementation')] })
1512
+ * ```
1513
+ */
1514
+ export declare function manifest(partitions?: Partial<Manifest>): Manifest;
1515
+
1516
+ /** The `Manifest` shape — four disjoint reference partitions. */
1517
+ export declare const manifestShape: ObjectShape<{
1518
+ read: ArrayShape<ObjectShape<{
1519
+ path: StringShape;
1520
+ note: StringShape;
1521
+ }, false>>;
1522
+ edit: ArrayShape<ObjectShape<{
1523
+ path: StringShape;
1524
+ note: StringShape;
1525
+ }, false>>;
1526
+ locked: ArrayShape<ObjectShape<{
1527
+ path: StringShape;
1528
+ note: StringShape;
1529
+ }, false>>;
1530
+ forbidden: ArrayShape<ObjectShape<{
1531
+ path: StringShape;
1532
+ note: StringShape;
1533
+ }, false>>;
1534
+ }, false>;
1535
+
1536
+ /**
1537
+ * One ranked outcome — a result, never a step.
1538
+ *
1539
+ * @remarks
1540
+ * `required: true` gates "done"; a demoted outcome is desirable but not blocking.
1541
+ */
1542
+ export declare interface Outcome {
1543
+ readonly rank: number;
1544
+ readonly text: string;
1545
+ readonly required: boolean;
1546
+ }
1547
+
1548
+ /**
1549
+ * Build an `Outcome`.
1550
+ *
1551
+ * @param rank - The one-based rank; lower ranks matter more.
1552
+ * @param text - The result, never a step.
1553
+ * @param required - Whether the outcome gates "done"; defaults to `true`.
1554
+ * @returns A fresh `Outcome`.
1555
+ *
1556
+ * @example
1557
+ * ```ts
1558
+ * import { outcome } from '@orkestrel/brief'
1559
+ *
1560
+ * outcome(1, 'useForm uses native FormData with no behavior change') // required: true
1561
+ * outcome(2, 'the diff stays under 200 lines', false)
1562
+ * ```
1563
+ */
1564
+ export declare function outcome(rank: number, text: string, required?: boolean): Outcome;
1565
+
1566
+ /** The `Outcome` shape — a one-based rank, the result text, and whether it gates done. */
1567
+ export declare const outcomeShape: ObjectShape<{
1568
+ rank: NumberShape;
1569
+ text: StringShape;
1570
+ required: BooleanShape;
1571
+ }, false>;
1572
+
1573
+ /**
1574
+ * The closed shape of the deliverable.
1575
+ *
1576
+ * @remarks
1577
+ * `format` is required; `sections` / `include` / `exclude` refine it.
1578
+ */
1579
+ export declare interface Output {
1580
+ readonly format: OutputFormat;
1581
+ readonly sections?: readonly string[];
1582
+ readonly include?: readonly string[];
1583
+ readonly exclude?: readonly string[];
1584
+ }
1585
+
1586
+ /**
1587
+ * Build an `Output`.
1588
+ *
1589
+ * @param format - The closed deliverable format.
1590
+ * @param overrides - Optional `sections` / `include` / `exclude`; absent keys are OMITTED.
1591
+ * @returns A fresh `Output`.
1592
+ *
1593
+ * @example
1594
+ * ```ts
1595
+ * import { output } from '@orkestrel/brief'
1596
+ *
1597
+ * output('markdown') // { format: 'markdown' }
1598
+ * output('diff', { include: ['updated useForm.ts'] })
1599
+ * ```
1600
+ */
1601
+ export declare function output(format: OutputFormat, overrides?: Partial<Omit<Output, 'format'>>): Output;
1602
+
1603
+ /** The five `OutputFormat` values, frozen. */
1604
+ export declare const OUTPUT_FORMATS: readonly OutputFormat[];
1605
+
1606
+ /** The closed vocabulary of deliverable shapes. */
1607
+ export declare type OutputFormat = 'markdown' | 'json' | 'code' | 'diff' | 'prose';
1608
+
1609
+ /** The `Output` shape — a closed format plus its optional refinements. */
1610
+ export declare const outputShape: ObjectShape<{
1611
+ format: LiteralShape<readonly OutputFormat[]>;
1612
+ sections: OptionalShape<ArrayShape<StringShape>>;
1613
+ include: OptionalShape<ArrayShape<StringShape>>;
1614
+ exclude: OptionalShape<ArrayShape<StringShape>>;
1615
+ }, false>;
1616
+
1617
+ /**
1618
+ * Parse a JSON string into a `Brief`.
1619
+ *
1620
+ * @remarks
1621
+ * The parse-then-trust boundary for a stored brief, a tool argument, or an agent's
1622
+ * emission. Invalid JSON, an extra key, an off-vocabulary literal, and a missing section
1623
+ * all fail the same way — `undefined`, never a throw. Coerce a bare vocabulary value with
1624
+ * `parseEnum` from `@orkestrel/contract` against the exported tuple instead.
1625
+ *
1626
+ * @param value - The JSON text to parse.
1627
+ * @returns The `Brief` when the parsed value satisfies `isBrief`, otherwise `undefined`.
1628
+ *
1629
+ * @example
1630
+ * ```ts
1631
+ * import { parseBrief } from '@orkestrel/brief'
1632
+ *
1633
+ * parseBrief('not json') // undefined
1634
+ * parseBrief('{"task":{"operation":"plan","domain":"ops","statement":"x."}}') // undefined
1635
+ * ```
1636
+ */
1637
+ export declare function parseBrief(value: string): Brief | undefined;
1638
+
1639
+ /**
1640
+ * Return a fresh brief with `trace` and `hash` derived from its own content.
1641
+ *
1642
+ * @remarks
1643
+ * Deterministic: no clock, no randomness, no run-specific data. Any existing `trace` /
1644
+ * `hash` is stripped before the digest, so pinning is idempotent and a re-pin of unchanged
1645
+ * content produces the same hash.
1646
+ *
1647
+ * The snapshot is taken FIRST, before any member is read, so a hostile input whose getters
1648
+ * throw surfaces as this package's coded error rather than as whatever it threw.
1649
+ *
1650
+ * @param source - The brief to pin.
1651
+ * @returns A fresh, pinned, deeply frozen `Brief`.
1652
+ * @throws {@link BriefError} `INVALID` when the brief carries data JSON cannot express.
1653
+ *
1654
+ * @example
1655
+ * ```ts
1656
+ * import { brief, pinBrief, task } from '@orkestrel/brief'
1657
+ *
1658
+ * const pinned = pinBrief(brief(task('document', 'writing', 'Write the brief guide.')))
1659
+ * pinned.hash // an 8-hex-digit structural digest
1660
+ * pinned.trace // 'document/writing · outcomes:0 · gaps:0/0 · proofs:0'
1661
+ * ```
1662
+ */
1663
+ export declare function pinBrief(source: Brief): Brief;
1664
+
1665
+ /** The `pin` phase snapshot — the drafted `Brief` in, the pinned `Brief` out. */
1666
+ export declare interface PinStageRecord {
1667
+ readonly stage: 'pin';
1668
+ readonly input: Brief;
1669
+ readonly output?: Brief;
1670
+ readonly error?: string;
1671
+ }
1672
+
1673
+ /**
1674
+ * One mechanical, transcript-provable check.
1675
+ *
1676
+ * @remarks
1677
+ * `command` should carry a clear exit signal — it becomes the `/goal` condition verbatim.
1678
+ */
1679
+ export declare interface Proof {
1680
+ readonly text: string;
1681
+ readonly command: string;
1682
+ }
1683
+
1684
+ /**
1685
+ * Build a `Proof`.
1686
+ *
1687
+ * @param text - What the check settles.
1688
+ * @param command - The command whose exit signal settles it.
1689
+ * @returns A fresh `Proof`.
1690
+ *
1691
+ * @example
1692
+ * ```ts
1693
+ * import { proof } from '@orkestrel/brief'
1694
+ *
1695
+ * proof('type-check and lint pass', 'npm run check')
1696
+ * ```
1697
+ */
1698
+ export declare function proof(text: string, command: string): Proof;
1699
+
1700
+ /** The `Proof` shape — the claim and the command that settles it. */
1701
+ export declare const proofShape: ObjectShape<{
1702
+ text: StringShape;
1703
+ command: StringShape;
1704
+ }, false>;
1705
+
1706
+ /**
1707
+ * One referenced path and why it is listed.
1708
+ *
1709
+ * @remarks
1710
+ * The ONE path record. A reference means different things in different containers, and the
1711
+ * container is what says which: `Brief.authority` ranks its entries so index 0 wins every
1712
+ * conflict, and each `Manifest` partition states a permission. The record itself carries no
1713
+ * classifier, because a second label on the row would restate what the container already
1714
+ * fixed — and the label this package used to carry was one repository's document taxonomy
1715
+ * rather than a domain.
1716
+ *
1717
+ * `note` is required. A path with no rationale is interpretation left to do, which is the
1718
+ * one thing a brief exists to remove.
1719
+ */
1720
+ export declare interface Reference {
1721
+ readonly path: string;
1722
+ readonly note: string;
1723
+ }
1724
+
1725
+ /**
1726
+ * Build a `Reference`.
1727
+ *
1728
+ * @param path - The referenced path or glob.
1729
+ * @param note - Why the path is listed.
1730
+ * @returns A fresh `Reference`.
1731
+ *
1732
+ * @example
1733
+ * ```ts
1734
+ * import { reference } from '@orkestrel/brief'
1735
+ *
1736
+ * reference('AGENTS.md', 'project law') // { path: 'AGENTS.md', note: 'project law' }
1737
+ * ```
1738
+ */
1739
+ export declare function reference(path: string, note: string): Reference;
1740
+
1741
+ /** The `Reference` shape — a path and the note that justifies listing it. */
1742
+ export declare const referenceShape: ObjectShape<{
1743
+ path: StringShape;
1744
+ note: StringShape;
1745
+ }, false>;
1746
+
1747
+ /** One pre-empted risk and the mitigation that answers it. */
1748
+ export declare interface Risk {
1749
+ readonly severity: RiskSeverity;
1750
+ readonly text: string;
1751
+ readonly mitigation: string;
1752
+ }
1753
+
1754
+ /**
1755
+ * Build a `Risk`.
1756
+ *
1757
+ * @param severity - The closed severity.
1758
+ * @param text - What could go wrong.
1759
+ * @param mitigation - What answers it.
1760
+ * @returns A fresh `Risk`.
1761
+ *
1762
+ * @example
1763
+ * ```ts
1764
+ * import { risk } from '@orkestrel/brief'
1765
+ *
1766
+ * risk('medium', 'native validation differs subtly', 'assert message and state in tests')
1767
+ * ```
1768
+ */
1769
+ export declare function risk(severity: RiskSeverity, text: string, mitigation: string): Risk;
1770
+
1771
+ /** The three `RiskSeverity` values, frozen. */
1772
+ export declare const RISK_SEVERITIES: readonly RiskSeverity[];
1773
+
1774
+ /** The closed vocabulary of risk severities. */
1775
+ export declare type RiskSeverity = 'low' | 'medium' | 'high';
1776
+
1777
+ /** The `Risk` shape — a closed severity, the risk, and its mitigation. */
1778
+ export declare const riskShape: ObjectShape<{
1779
+ severity: LiteralShape<readonly RiskSeverity[]>;
1780
+ text: StringShape;
1781
+ mitigation: StringShape;
1782
+ }, false>;
1783
+
1784
+ /**
1785
+ * The positive form of {@link LINE_BREAK_PATTERN}, for the shape DSL.
1786
+ *
1787
+ * @remarks
1788
+ * `stringShape`'s `pattern` must MATCH an accepted value, so the guard's refusal regex
1789
+ * cannot be reused directly. Both are derived from one character class, which is what
1790
+ * keeps the hand-composed guards and the compiled shapes refusing the same strings.
1791
+ */
1792
+ export declare const SINGLE_LINE_PATTERN: RegExp;
1793
+
1794
+ /**
1795
+ * Return a deeply owned, deeply frozen copy of a brief, refusing anything off-contract.
1796
+ *
1797
+ * @remarks
1798
+ * The one reading boundary this package has, used by the pin, the registry, and every
1799
+ * projection. It matters twice over. A brief built from caller collections ADOPTS those
1800
+ * arrays, so a later `outcomes.push` would change content a hash already described. And a
1801
+ * caller's object may answer differently on each read, so validating one reading and
1802
+ * rendering from a second let a brief that passed the contract render a row it does not
1803
+ * contain — this takes ONE reading, validates that, and freezes it.
1804
+ *
1805
+ * `cloneJSONRecord` is `@orkestrel/contract`'s primitive rather than the ambient
1806
+ * `structuredClone`: it deep-freezes, it refuses a value JSON cannot express, and it is a
1807
+ * captured import rather than a mutable global. The result is a null-prototype record, so
1808
+ * compare it structurally rather than by prototype.
1809
+ *
1810
+ * This file imports no sibling helper, which is what lets `helpers.ts` consume it without a
1811
+ * module cycle.
1812
+ *
1813
+ * @param source - The brief to snapshot.
1814
+ * @returns A deeply frozen `Brief` sharing no reference with `source`.
1815
+ * @throws {@link BriefError} `INVALID` when the value is off-contract or JSON cannot express it.
1816
+ *
1817
+ * @example
1818
+ * ```ts
1819
+ * import { brief, outcome, snapshotBrief, task } from '@orkestrel/brief'
1820
+ *
1821
+ * const outcomes = [outcome(1, 'shipped')]
1822
+ * const owned = snapshotBrief(brief(task('plan', 'ops', 'Plan the release.'), { outcomes }))
1823
+ * owned.outcomes === outcomes // false — the alias is broken
1824
+ * Object.isFrozen(owned.outcomes) // true
1825
+ * ```
1826
+ */
1827
+ export declare function snapshotBrief(source: Brief): Brief;
1828
+
1829
+ /**
1830
+ * What the brief asks for, in one imperative sentence.
1831
+ *
1832
+ * @remarks
1833
+ * A compound `statement` is two briefs — `validateBrief` errors on more than one sentence.
1834
+ */
1835
+ export declare interface Task {
1836
+ readonly operation: TaskOperation;
1837
+ readonly domain: TaskDomain;
1838
+ readonly statement: string;
1839
+ }
1840
+
1841
+ /**
1842
+ * Build a `Task`.
1843
+ *
1844
+ * @param operation - What the brief asks for, from the closed operation vocabulary.
1845
+ * @param domain - The subject matter, from the closed domain vocabulary.
1846
+ * @param statement - One imperative sentence naming the object of the work.
1847
+ * @returns A fresh `Task`.
1848
+ *
1849
+ * @example
1850
+ * ```ts
1851
+ * import { task } from '@orkestrel/brief'
1852
+ *
1853
+ * task('refactor', 'code', 'Refactor useForm to native browser form APIs.')
1854
+ * ```
1855
+ */
1856
+ export declare function task(operation: TaskOperation, domain: TaskDomain, statement: string): Task;
1857
+
1858
+ /** The eight `TaskDomain` values, frozen. */
1859
+ export declare const TASK_DOMAINS: readonly TaskDomain[];
1860
+
1861
+ /** The twelve `TaskOperation` values, frozen. */
1862
+ export declare const TASK_OPERATIONS: readonly TaskOperation[];
1863
+
1864
+ /** The closed vocabulary of the subject matter a brief operates on. */
1865
+ export declare type TaskDomain = 'code' | 'writing' | 'research' | 'analysis' | 'design' | 'data' | 'ops' | 'other';
1866
+
1867
+ /**
1868
+ * The closed vocabulary of what a brief asks for.
1869
+ *
1870
+ * @remarks
1871
+ * A request that fits none of these twelve is mis-scoped rather than a missing
1872
+ * literal. Compose with `literalOf(TASK_OPERATIONS)` or `parseEnum(value, TASK_OPERATIONS)`.
1873
+ */
1874
+ export declare type TaskOperation = 'create' | 'refactor' | 'debug' | 'extract' | 'migrate' | 'explain' | 'review' | 'optimize' | 'audit' | 'test' | 'document' | 'plan';
1875
+
1876
+ /** The `Task` shape — closed operation and domain vocabularies plus a non-empty statement. */
1877
+ export declare const taskShape: ObjectShape<{
1878
+ operation: LiteralShape<readonly TaskOperation[]>;
1879
+ domain: LiteralShape<readonly TaskDomain[]>;
1880
+ statement: StringShape;
1881
+ }, false>;
1882
+
1883
+ /** A single-line string of any length, including empty. */
1884
+ export declare const textShape: StringShape;
1885
+
1886
+ /**
1887
+ * The semantic pass over an already-shape-valid brief.
1888
+ *
1889
+ * @remarks
1890
+ * ERRORS are the structural violations no assumption can paper over: a manifest
1891
+ * overlap, an authority no partition grants access to, an empty `proofs` list, and a
1892
+ * statement that is not exactly one sentence.
1893
+ * WARNINGS are runnable but suspicious: duplicate outcome ranks, an unpaired open gap,
1894
+ * and an optional outcome ranked above a required one. Never throws.
1895
+ *
1896
+ * @param source - The brief to inspect.
1897
+ * @returns A reasons `ReasonValidationResult`; `valid` exactly when `errors` is empty.
1898
+ *
1899
+ * @example
1900
+ * ```ts
1901
+ * import { brief, proof, task, validateBrief } from '@orkestrel/brief'
1902
+ *
1903
+ * validateBrief(brief(task('plan', 'ops', 'Plan the release.'))) // valid: false — no proofs
1904
+ * validateBrief(
1905
+ * brief(task('plan', 'ops', 'Plan the release.'), { proofs: [proof('ok', 'npm test')] }),
1906
+ * ) // valid: true
1907
+ * ```
1908
+ */
1909
+ export declare function validateBrief(source: Brief): ReasonValidationResult;
1910
+
1911
+ export { }