@smthrs/plan 1.0.0-rc.8 → 1.0.0-rc.9

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.
Files changed (59) hide show
  1. package/dist/cjs/Effects.d.ts +29 -11
  2. package/dist/cjs/Effects.d.ts.map +1 -1
  3. package/dist/cjs/Effects.js.map +2 -2
  4. package/dist/cjs/FileSet.d.ts +121 -51
  5. package/dist/cjs/FileSet.d.ts.map +1 -1
  6. package/dist/cjs/FileSet.js.map +2 -2
  7. package/dist/cjs/GraphBuildError.d.ts +2 -4
  8. package/dist/cjs/GraphBuildError.d.ts.map +1 -1
  9. package/dist/cjs/GraphBuildError.js.map +2 -2
  10. package/dist/cjs/KeyMaterial.d.ts +13 -8
  11. package/dist/cjs/KeyMaterial.d.ts.map +1 -1
  12. package/dist/cjs/KeyMaterial.js.map +2 -2
  13. package/dist/cjs/Plan.d.ts +72 -23
  14. package/dist/cjs/Plan.d.ts.map +1 -1
  15. package/dist/cjs/Plan.js +3 -0
  16. package/dist/cjs/Plan.js.map +2 -2
  17. package/dist/cjs/PlanDiff.d.ts +7 -3
  18. package/dist/cjs/PlanDiff.d.ts.map +1 -1
  19. package/dist/cjs/PlanDiff.js.map +2 -2
  20. package/dist/cjs/StepKey.d.ts +38 -17
  21. package/dist/cjs/StepKey.d.ts.map +1 -1
  22. package/dist/cjs/StepKey.js.map +2 -2
  23. package/dist/esm/Effects.d.ts +29 -11
  24. package/dist/esm/Effects.d.ts.map +1 -1
  25. package/dist/esm/Effects.js +18 -7
  26. package/dist/esm/Effects.js.map +1 -1
  27. package/dist/esm/FileSet.d.ts +121 -51
  28. package/dist/esm/FileSet.d.ts.map +1 -1
  29. package/dist/esm/FileSet.js +97 -35
  30. package/dist/esm/FileSet.js.map +1 -1
  31. package/dist/esm/GraphBuildError.d.ts +2 -4
  32. package/dist/esm/GraphBuildError.d.ts.map +1 -1
  33. package/dist/esm/GraphBuildError.js +2 -3
  34. package/dist/esm/GraphBuildError.js.map +1 -1
  35. package/dist/esm/KeyMaterial.d.ts +13 -8
  36. package/dist/esm/KeyMaterial.d.ts.map +1 -1
  37. package/dist/esm/KeyMaterial.js +11 -4
  38. package/dist/esm/KeyMaterial.js.map +1 -1
  39. package/dist/esm/Plan.d.ts +72 -23
  40. package/dist/esm/Plan.d.ts.map +1 -1
  41. package/dist/esm/Plan.js +61 -16
  42. package/dist/esm/Plan.js.map +1 -1
  43. package/dist/esm/PlanDiff.d.ts +7 -3
  44. package/dist/esm/PlanDiff.d.ts.map +1 -1
  45. package/dist/esm/PlanDiff.js +0 -1
  46. package/dist/esm/PlanDiff.js.map +1 -1
  47. package/dist/esm/StepKey.d.ts +38 -17
  48. package/dist/esm/StepKey.d.ts.map +1 -1
  49. package/dist/esm/StepKey.js +33 -11
  50. package/dist/esm/StepKey.js.map +1 -1
  51. package/docs/guides/diff-two-plans.md +78 -1
  52. package/package.json +4 -4
  53. package/src/Effects.ts +29 -11
  54. package/src/FileSet.ts +121 -51
  55. package/src/GraphBuildError.ts +2 -4
  56. package/src/KeyMaterial.ts +13 -8
  57. package/src/Plan.ts +66 -23
  58. package/src/PlanDiff.ts +7 -3
  59. package/src/StepKey.ts +38 -17
package/src/Plan.ts CHANGED
@@ -34,12 +34,17 @@ import * as StepKey from "./StepKey.ts"
34
34
  * What a node does to the world, declared. Paths only — measuring them is
35
35
  * run-time work, so a digest here would break the no-I/O law.
36
36
  *
37
+ * Decoding fails with a `SchemaError` when a path is not workspace-relative.
38
+ * {@link compile} also refuses a node whose `removes` overlaps its `writes`
39
+ * with an `invalid_effects` {@link PlanError}.
40
+ *
37
41
  * @since 0.1.0
38
42
  * @category schemas
39
- * @slop
40
43
  */
41
44
  export const NodeEffects = Schema.Struct({
45
+ /** Paths the node reads. A read orders the node after every writer of an overlapping path. */
42
46
  reads: Schema.Array(FileSet.ReadDeclaration),
47
+ /** Paths the node writes. Two writers of an overlapping path form a conflict. */
43
48
  writes: Schema.Array(FileSet.Declaration),
44
49
  /**
45
50
  * Paths the node declares it will DELETE. A removal mutates the world exactly
@@ -47,6 +52,7 @@ export const NodeEffects = Schema.Struct({
47
52
  * conflict pass and the reader-after-writer pass see them as one set.
48
53
  */
49
54
  removes: Schema.optional(Schema.Array(FileSet.Pattern)),
55
+ /** How strictly the sandbox enforces these declarations. */
50
56
  boundaryMode: FileSet.BoundaryMode
51
57
  })
52
58
 
@@ -55,16 +61,18 @@ export const NodeEffects = Schema.Struct({
55
61
  *
56
62
  * @since 0.1.0
57
63
  * @category models
58
- * @slop
59
64
  */
60
65
  export type NodeEffects = typeof NodeEffects.Type
61
66
 
62
67
  /**
63
- * The plan-time write-conflict strategies of the effect taxonomy.
68
+ * The plan-time verdict for two writers of an overlapping path:
69
+ * `serialize` gives the later writer an ordering edge, `lane` records a lane
70
+ * annotation on both writers and adds no edge, and `fail` refuses the plan
71
+ * with `overlap_forbidden`. For a pair, `fail` dominates `lane`, which
72
+ * dominates `serialize`. The same schema as `Effects.ConflictStrategy`.
64
73
  *
65
74
  * @since 0.1.0
66
75
  * @category schemas
67
- * @slop
68
76
  */
69
77
  export const PairStrategy = Effects.ConflictStrategy
70
78
 
@@ -73,18 +81,17 @@ export const PairStrategy = Effects.ConflictStrategy
73
81
  *
74
82
  * @since 0.1.0
75
83
  * @category models
76
- * @slop
77
84
  */
78
85
  export type PairStrategy = typeof PairStrategy.Type
79
86
 
80
87
  /**
81
88
  * The runtime half of a conflict annotation: what the scheduler
82
89
  * does when the overlap the plan predicted actually bites, or when a
83
- * scheduled node's inputs are invalidated under it.
90
+ * scheduled node's inputs are invalidated under it. For a pair,
91
+ * `stop-merge` dominates `delay-rebase`.
84
92
  *
85
93
  * @since 0.1.0
86
94
  * @category schemas
87
- * @slop
88
95
  */
89
96
  export const RuntimeStrategy = Schema.Literals(["delay-rebase", "stop-merge"])
90
97
 
@@ -93,7 +100,6 @@ export const RuntimeStrategy = Schema.Literals(["delay-rebase", "stop-merge"])
93
100
  *
94
101
  * @since 0.1.0
95
102
  * @category models
96
- * @slop
97
103
  */
98
104
  export type RuntimeStrategy = typeof RuntimeStrategy.Type
99
105
 
@@ -104,9 +110,11 @@ const NodeKind = Schema.Literals(["step", "agent", "merge"])
104
110
  * One resolved overlap between two writers that no dependency path already
105
111
  * orders. Conflict is a property of the PAIR, not of one declaration.
106
112
  *
113
+ * `with` names the other node, `paths` lists the overlapping declarations,
114
+ * and `strategy` and `runtime` are the verdicts resolved for the pair.
115
+ *
107
116
  * @since 0.1.0
108
117
  * @category schemas
109
- * @slop
110
118
  */
111
119
  export const ConflictAnnotation = Schema.Struct({
112
120
  with: Schema.NonEmptyString,
@@ -120,7 +128,6 @@ export const ConflictAnnotation = Schema.Struct({
120
128
  *
121
129
  * @since 0.1.0
122
130
  * @category models
123
- * @slop
124
131
  */
125
132
  export type ConflictAnnotation = typeof ConflictAnnotation.Type
126
133
 
@@ -138,9 +145,11 @@ export type ConflictAnnotation = typeof ConflictAnnotation.Type
138
145
  * a later elaboration can resolve a pair against them without re-reading the
139
146
  * flow source.
140
147
  *
148
+ * `generation` is 0 for nodes from {@link compile} and grows by one with each
149
+ * {@link append}.
150
+ *
141
151
  * @since 0.1.0
142
152
  * @category schemas
143
- * @slop
144
153
  */
145
154
  export const PlanNode = Schema.Struct({
146
155
  id: Schema.NonEmptyString,
@@ -161,7 +170,6 @@ export const PlanNode = Schema.Struct({
161
170
  *
162
171
  * @since 0.1.0
163
172
  * @category models
164
- * @slop
165
173
  */
166
174
  export type PlanNode = typeof PlanNode.Type
167
175
 
@@ -175,9 +183,11 @@ export type PlanNode = typeof PlanNode.Type
175
183
  * `baseDigest` is the digest at generation 0: what a human approved and what
176
184
  * a `RUNNING` run pins. `digest` advances with every appended elaboration.
177
185
  *
186
+ * Decoding checks only the shape. Use {@link verify} to check that keys,
187
+ * edges, and digests match the declarations.
188
+ *
178
189
  * @since 0.1.0
179
190
  * @category schemas
180
- * @slop
181
191
  */
182
192
  export const Plan = Schema.Struct({
183
193
  planId: Schema.NonEmptyString,
@@ -193,7 +203,6 @@ export const Plan = Schema.Struct({
193
203
  *
194
204
  * @since 0.1.0
195
205
  * @category models
196
- * @slop
197
206
  */
198
207
  export type Plan = typeof Plan.Type
199
208
 
@@ -202,13 +211,17 @@ export type Plan = typeof Plan.Type
202
211
  *
203
212
  * @since 0.1.0
204
213
  * @category models
205
- * @slop
206
214
  */
207
215
  export interface NodeDraft {
216
+ /** A non-empty id, unique within the plan. */
208
217
  readonly id: string
218
+ /** The key inputs. Its `Ref` inputs name the node's material dependencies. */
209
219
  readonly material: KeyMaterial.KeyMaterial
220
+ /** The declared reads, writes, and removals. They replace `material.effects`. */
210
221
  readonly effects: NodeEffects
222
+ /** `step`, `agent`, or `merge`. Defaults to `step`. */
211
223
  readonly kind?: PlanNode["kind"] | undefined
224
+ /** A safe integer scheduling priority. Defaults to 0. */
212
225
  readonly priority?: number | undefined
213
226
  /** This declaration's preferred plan-time verdict for a detected overlap. */
214
227
  readonly conflictStrategy?: PairStrategy | undefined
@@ -217,11 +230,22 @@ export interface NodeDraft {
217
230
  }
218
231
 
219
232
  /**
220
- * A graph the compiler refuses.
233
+ * A graph the compiler refuses. `code` names the rule:
234
+ *
235
+ * - `cycle`: material or inferred edges form a cycle.
236
+ * - `unknown_dependency`: a `Ref` names a node that is not in the plan.
237
+ * - `duplicate_node`: two nodes share an id.
238
+ * - `overlap_forbidden`: two unordered writers overlap and the pair's
239
+ * strategy is `fail`.
240
+ * - `invalid_effects`: a node both writes and removes one path.
241
+ * - `invalid_node`: a draft field, the plan id, or the flow name is malformed.
242
+ * - `invalid_plan`: {@link verify} found a plan that does not match its
243
+ * declarations.
244
+ * - `graph_too_large`: the plan exceeds {@link maximumPlanNodes} or the
245
+ * effect-analysis work budget.
221
246
  *
222
247
  * @since 0.1.0
223
248
  * @category errors
224
- * @slop
225
249
  */
226
250
  export class PlanError extends Schema.TaggedError<PlanError>()("@smthrs/plan/PlanError", {
227
251
  code: Schema.Literals([
@@ -245,7 +269,6 @@ export class PlanError extends Schema.TaggedError<PlanError>()("@smthrs/plan/Pla
245
269
  *
246
270
  * @since 1.0.0
247
271
  * @category limits
248
- * @slop
249
272
  */
250
273
  export const maximumPlanNodes = 10_000
251
274
 
@@ -671,9 +694,15 @@ const keyNodes = (
671
694
  * {@link maximumPlanNodes} and fail with `graph_too_large` above that limit
672
695
  * or the effect-analysis work budget. Analysis yields periodically.
673
696
  *
697
+ * The options and drafts are copied when `compile` is called, so later
698
+ * caller mutation does not reach the plan. The result is deeply frozen.
699
+ *
700
+ * @param options - `planId` and `flow` name the plan; `nodes` are the drafts.
701
+ * @returns The generation-0 plan, whose `baseDigest` equals its `digest`.
702
+ * Fails with {@link PlanError} for a refused graph, `KeyMaterialError` or
703
+ * `SchemaError` when a key cannot be derived.
674
704
  * @since 0.1.0
675
705
  * @category constructors
676
- * @slop
677
706
  */
678
707
  export const compile = (options: {
679
708
  readonly planId: string
@@ -710,9 +739,16 @@ export const compile = (options: {
710
739
  * Re-ordering after a reconciliation happens by re-keying *future* steps,
711
740
  * never by rewriting history.
712
741
  *
742
+ * A plan not produced by this module is checked with {@link verify} first.
743
+ *
744
+ * @param plan - The plan to grow.
745
+ * @param drafts - At least one new node. Ids must not repeat existing ones;
746
+ * `Ref` inputs may name existing nodes.
747
+ * @returns The plan with one more generation and a new `digest`. Fails with
748
+ * {@link PlanError} (`invalid_node` for an empty `drafts`, `invalid_plan` for a
749
+ * plan that fails verification), `KeyMaterialError`, or `SchemaError`.
713
750
  * @since 0.1.0
714
751
  * @category constructors
715
- * @slop
716
752
  */
717
753
  export const append = (
718
754
  plan: Plan,
@@ -832,9 +868,10 @@ export const verify = (
832
868
  * The nodes added by the newest generation: what {@link module:PlanStore}
833
869
  * appends and what the `subgraph-appended` journal record names.
834
870
  *
871
+ * @param plan - A plan.
872
+ * @returns The nodes whose generation equals `plan.generation`, in order.
835
873
  * @since 0.1.0
836
874
  * @category accessors
837
- * @slop
838
875
  */
839
876
  export const generationNodes = (plan: Plan): ReadonlyArray<PlanNode> =>
840
877
  plan.nodes.filter((node) => node.generation === plan.generation)
@@ -844,9 +881,11 @@ export const generationNodes = (plan: Plan): ReadonlyArray<PlanNode> =>
844
881
  * {@link verify}: such values take verify's trusted fast path and need no
845
882
  * second schema validation.
846
883
  *
884
+ * @param input - Any value.
885
+ * @returns `true` only for a plan returned by {@link compile},
886
+ * {@link append}, or {@link verify}; a structurally equal copy returns `false`.
847
887
  * @since 1.0.0
848
888
  * @category guards
849
- * @slop
850
889
  */
851
890
  export const isVerified = (input: unknown): input is Plan =>
852
891
  typeof input === "object" && input !== null && frozenPlans.has(input as Plan)
@@ -858,9 +897,13 @@ export const isVerified = (input: unknown): input is Plan =>
858
897
  * plan's integrity from the already-verified prefix instead of decoding and
859
898
  * re-verifying every stored row.
860
899
  *
900
+ * For a generation-0 plan no generation precedes the newest, so this is the
901
+ * digest of an empty node list.
902
+ *
903
+ * @param plan - A plan.
904
+ * @returns The approval digest of the nodes older than `plan.generation`.
861
905
  * @since 1.0.0
862
906
  * @category accessors
863
- * @slop
864
907
  */
865
908
  export const prefixDigest = (
866
909
  plan: Plan
package/src/PlanDiff.ts CHANGED
@@ -23,11 +23,13 @@ import type * as Planned from "./Planned.ts"
23
23
  *
24
24
  * @since 0.1.0
25
25
  * @category models
26
- * @slop
27
26
  */
28
27
  export interface Rekeyed {
28
+ /** The node's id in the newer plan. */
29
29
  readonly id: string
30
+ /** The step key the paired node carried in the older plan. */
30
31
  readonly from: string
32
+ /** The step key the node carries in the newer plan. */
31
33
  readonly to: string
32
34
  /**
33
35
  * Field labels such as `"kind"`, `"body"`, `"layers"`, `"capabilities"`,
@@ -44,12 +46,15 @@ export interface Rekeyed {
44
46
  *
45
47
  * @since 0.1.0
46
48
  * @category models
47
- * @slop
48
49
  */
49
50
  export interface PlanDiff {
51
+ /** Ids, in the newer plan, of nodes with no counterpart in the older plan. */
50
52
  readonly added: ReadonlyArray<string>
53
+ /** Ids, in the older plan, of nodes with no counterpart in the newer plan. */
51
54
  readonly removed: ReadonlyArray<string>
55
+ /** Nodes paired by id whose step key changed, with the fields that changed it. */
52
56
  readonly rekeyed: ReadonlyArray<Rekeyed>
57
+ /** Ids, in the newer plan, of nodes whose step key is unchanged, including nodes whose id moved. */
53
58
  readonly unchanged: ReadonlyArray<string>
54
59
  }
55
60
 
@@ -174,7 +179,6 @@ const changedFields = (
174
179
  * plan's node order.
175
180
  * @since 0.1.0
176
181
  * @category constructors
177
- * @slop
178
182
  */
179
183
  export const diff = (previous: Plan.Plan, next: Plan.Plan): PlanDiff => {
180
184
  // pairs maps a next node to the previous node it is the same step as.
package/src/StepKey.ts CHANGED
@@ -43,7 +43,6 @@ import type * as KeyMaterial from "./KeyMaterial.ts"
43
43
  *
44
44
  * @since 0.1.0
45
45
  * @category models
46
- * @slop
47
46
  */
48
47
  export type StepKey = StoredKey
49
48
 
@@ -72,7 +71,6 @@ type GraphReference = "ref" | "pending" | "ref-projected"
72
71
  *
73
72
  * @since 0.1.0
74
73
  * @category models
75
- * @slop
76
74
  */
77
75
  export interface DigestInput {
78
76
  readonly [DigestInputTypeId]: typeof DigestInputTypeId
@@ -94,9 +92,12 @@ export interface DigestInput {
94
92
  * Nominally tags a precomputed digest so it is hashed as a digest reference
95
93
  * rather than a literal value.
96
94
  *
95
+ * @param digest - The precomputed digest.
96
+ * @param reference - Which graph reference resolved to the digest, and the
97
+ * projection path for `ref-projected`. Omit for a plain digest.
98
+ * @returns The tagged digest input.
97
99
  * @since 0.1.0
98
100
  * @category constructors
99
- * @slop
100
101
  */
101
102
  export const digestInput = (
102
103
  digest: string,
@@ -112,11 +113,13 @@ export const digestInput = (
112
113
  })
113
114
 
114
115
  /**
115
- * Type guard for values produced by {@link digestInput}.
116
+ * Type guard for values produced by {@link digestInput}. A plain object
117
+ * shaped like `{ digest: "..." }` is not one.
116
118
  *
119
+ * @param value - Any value.
120
+ * @returns `true` only for a value carrying the private digest-input brand.
117
121
  * @since 0.1.0
118
122
  * @category guards
119
- * @slop
120
123
  */
121
124
  export const isDigestInput = (value: unknown): value is DigestInput =>
122
125
  typeof value === "object" && value !== null && DigestInputTypeId in value &&
@@ -146,7 +149,6 @@ export const isDigestInput = (value: unknown): value is DigestInput =>
146
149
  *
147
150
  * @since 0.1.0
148
151
  * @category models
149
- * @slop
150
152
  */
151
153
  export type EnvironmentIdentity = typeof EnvironmentIdentity.Type
152
154
 
@@ -180,9 +182,13 @@ export const EnvironmentIdentity = Schema.Union([
180
182
  * digest. Entries are sound only while each settled `from` value is immutable;
181
183
  * callers must create a fresh memo when those values can change.
182
184
  *
185
+ * `digest(from, path, compute)` runs `compute` once per address and shares its
186
+ * result with concurrent callers. A failed or interrupted computation is not
187
+ * kept: the next caller recomputes, and a waiter whose leader was interrupted
188
+ * becomes the new leader instead of inheriting the interrupt.
189
+ *
183
190
  * @since 0.1.0
184
191
  * @category models
185
- * @slop
186
192
  */
187
193
  export interface DigestMemo {
188
194
  readonly digest: (
@@ -196,9 +202,9 @@ export interface DigestMemo {
196
202
  * Creates an empty projected-value digest memo. Projection addresses are
197
203
  * JSON-encoded `[from, path]` tuples so segment boundaries are unambiguous.
198
204
  *
205
+ * @returns A memo with no entries.
199
206
  * @since 0.1.0
200
207
  * @category constructors
201
- * @slop
202
208
  */
203
209
  export const makeDigestMemo = (): DigestMemo => {
204
210
  const entries = new Map<string, Deferred.Deferred<StepKey, Schema.SchemaError>>()
@@ -240,7 +246,6 @@ export const makeDigestMemo = (): DigestMemo => {
240
246
  *
241
247
  * @since 0.1.0
242
248
  * @category models
243
- * @slop
244
249
  */
245
250
  export interface ContentIdentity {
246
251
  readonly body: unknown
@@ -261,7 +266,6 @@ export interface ContentIdentity {
261
266
  *
262
267
  * @since 0.1.0
263
268
  * @category models
264
- * @slop
265
269
  */
266
270
  export interface OrdinalIdentity {
267
271
  readonly runId: string
@@ -271,11 +275,18 @@ export interface OrdinalIdentity {
271
275
  }
272
276
 
273
277
  /**
274
- * Stable failures while resolving graph-local dependency references.
278
+ * Stable failures while deriving a key. `code` names the rule:
279
+ *
280
+ * - `invalid_environment`: an {@link EnvironmentIdentity} that is neither
281
+ * declared nor run-scoped.
282
+ * - `missing_dependency`: a `Ref` or `Pending` input names a node with no
283
+ * recorded digest, or, at dispatch, a `Ref` names one with no settled
284
+ * result.
285
+ * - `non_content_material`: a content-key constructor received material whose
286
+ * tier is not `sealed`.
275
287
  *
276
288
  * @since 0.1.0
277
289
  * @category errors
278
- * @slop
279
290
  */
280
291
  export class KeyMaterialError extends Schema.TaggedError<KeyMaterialError>()("@smthrs/plan/KeyMaterialError", {
281
292
  code: Schema.Literals(["invalid_environment", "missing_dependency", "non_content_material"]),
@@ -423,9 +434,11 @@ export const environmentIdentity = (
423
434
  * declarations are normalized before serialization; write declarations remain
424
435
  * part of the identity even when the step writes nothing.
425
436
  *
437
+ * @param identity - The step's content material.
438
+ * @returns The content key. Fails with `invalid_environment` for a malformed
439
+ * environment, or `SchemaError` when the material cannot be serialized.
426
440
  * @since 0.1.0
427
441
  * @category constructors
428
- * @slop
429
442
  */
430
443
  export const content = (
431
444
  identity: ContentIdentity
@@ -447,9 +460,10 @@ export const content = (
447
460
  * Produces a run-local ordinal key for compensable, irreversible, or unsealed
448
461
  * work. These keys intentionally cannot be reused across runs.
449
462
  *
463
+ * @param identity - The run id, optional parent scope, ordinal, and tier.
464
+ * @returns The ordinal key.
450
465
  * @since 0.1.0
451
466
  * @category constructors
452
- * @slop
453
467
  */
454
468
  export const ordinal = (
455
469
  identity: OrdinalIdentity
@@ -466,9 +480,13 @@ export const ordinal = (
466
480
  * and never enter the hashed value — rename a node and nothing re-keys; change
467
481
  * what a node consumes and everything downstream of it does.
468
482
  *
483
+ * @param material - The node's key material; its tier must be `sealed`.
484
+ * @param dependencyDigests - The key of every node the material references,
485
+ * by node id.
486
+ * @returns The content key. Fails with `non_content_material` for a
487
+ * non-sealed tier and `missing_dependency` for an unresolved reference.
469
488
  * @since 0.1.0
470
489
  * @category constructors
471
- * @slop
472
490
  */
473
491
  export const fromKeyMaterial = (
474
492
  material: KeyMaterial.KeyMaterial,
@@ -637,7 +655,6 @@ const orderingOnly = "ordering-only"
637
655
  *
638
656
  * @since 0.1.0
639
657
  * @category utilities
640
- * @slop
641
658
  */
642
659
  export const project = (value: unknown, path: ReadonlyArray<string>): unknown => {
643
660
  let current = value
@@ -672,9 +689,13 @@ export const project = (value: unknown, path: ReadonlyArray<string>): unknown =>
672
689
  * halt rule guarantees it: a dependent of failed or skipped work never
673
690
  * dispatches, so a `Ref` always resolves against a success.
674
691
  *
692
+ * @param options - The material, the dependencies' settled results, the
693
+ * measured hermetic boundary, and an optional environment and memo.
694
+ * @returns The dispatch key. Fails with `non_content_material` for a
695
+ * non-sealed tier, `missing_dependency` for a `Ref` absent from `results`,
696
+ * or `invalid_environment` for a malformed environment.
675
697
  * @since 0.1.0
676
698
  * @category constructors
677
- * @slop
678
699
  */
679
700
  export const dispatchIdentity = (options: {
680
701
  readonly material: KeyMaterial.KeyMaterial